Introduction¶
When developing in DSH (DeepSeek Harness), a common situation arises: both sides have command-line assistants. DSH runs DeepSeek, and Codex CLI is installed locally, but their sessions do not communicate—sending a task from here results in a response from there that disconnects immediately, and the next round requires pasting the context again. Model selection and workload intensity cannot be controlled either.
hato-24/dsh-codex-handoff (gpt_bridge) is a client plugin written to solve this problem: it connects the local Codex CLI’s multi-turn sessions into DSH. The history is checkable in the sidebar. After sessions are stored in the database, they can be resumed. Models and reasoning intensity can also be switched. Below is an introduction to its positioning, installation method, and actual usage.
What is this¶
One-sentence definition: Multi-turn dialogue bridge for Codex CLI. After installing the plugin, a GPT dialogue panel appears in DSH. At the same time, it registers a codex_gpt tool for the agent, allowing GPT to be called as a sub-agent. Sessions are persisted by default and can be resumed using codex exec resume to continue the same thread.
- Maintainer: HATO-24
- Category: Client (contains both Host side and Client side components)
- License: MIT
It solves three things: 1. Visibility of interactions; each Q&A round can be viewed by clicking in the DSH sidebar. 2. Resumability; sessions are written into the Codex session library, so chatting continues even if the command changes. 3. Configurable levels; model and reasoning intensity are switched via the interface rather than hardcoded in scripts.
Core Functions¶
The plugin consists of three parts:
| Component | Function |
|---|---|
Host side lib/index.js |
Calls codex exec, maintains .bridge storage, provides /api/codex-bridge/* routes, registers codex_gpt tool |
Client side lib/client.js |
Sidebar “GPT Chat” entry + Main panel (session list, per-round questions/replies) |
gpt_bridge.ps1 (Upgrade version) |
Command-line bridge, persists sessions by default, records thread_id, supports -Resume |
Data lands in <workspace>/.bridge/ in the workspace, sharing the same directory set as gpt_bridge.ps1, so old records before the upgrade are also visible.
Installation & Enablement¶
Prerequisites: DSH is installed locally, and Codex CLI is logged in and callable. The plugin will automatically look for the codex executable in %LOCALAPPDATA%\OpenAI\Codex\bin\*\codex.exe, CODEX_BIN, or PATH.
dsh-codex-handoff@0.1.1
Two installation methods, choose one:
- Interface operation: Plugins page → Add → Enter
dsh-codex-handoff - Tool installation:
plugin_manager→install_bundle, filltargetwithdsh-codex-handoff@0.1.1
Note: The version number here is filled according to the original README installation section; the version declared in the repository’s package.json is 0.1.3.
After installation, three entry points will appear on the interface:
- Bottom left (next to Settings, next to the update notification bot) adds a new GPT button, clicking it opens the dialogue panel in the DSH sidebar.
- GPT Chat tag appears at the top of the sidebar (alongside “Start”).
- The dialogue bubble button at the far right of the session title bar, serving as a shortcut.
Two points to note:
- After the Client plugin changes
inject, restart DSH for new configurations to be parsed. - After changing code locally, simply disable → enable the plugin in the plugin page to reload it; no need to restart DSH.
Also, the icon in the left sidebar cannot be used as an entry point: it switches the current conversation, causing sidebarRight.mounted to become empty and openResource to throw sidebarRight: no session surface is mounted, which is a mechanism limitation of DSH.
Viewing Records & Sending Messages in Sidebar¶
Click the GPT button in the bottom left (or the bubble button at the head of the session), and the sidebar expands the “GPT Chat” tab. The sidebar can be resized by dragging the divider bar; the main panel is unaffected, and the dialogue proceeds normally.
The bottom of the panel is the send area:
- Input box sends with
Enter, new line withShift+Enter; - Model dropdown comes from all 10 models listed by
codex debug models(GPT-6.1-Sol,GPT-6-Astra,GPT-6.1 Terra,GPT-6-Sol, etc.); - Reasoning dropdown follows the levels supported by the selected model (
low/medium/high/xhigh/max/ultra); - When a specific session segment is selected, the two dropdowns auto-fill with the model and reasoning intensity used for that session last time; sending directly continues using them; switching sessions resets them.
It records “what was actually used”: after each round ends, the plugin reads the real model and effort from turn_context in the rollout and writes them into actualModel/actualEffort in inbox/<id>.json. It does not display null values when the model is not passed. The actual comparison in the README is as follows (request parameters model=gpt-6.1-sol, reasoning=high):
| Source | model | effort |
|---|---|---|
| Request Params | gpt-6.1-sol | high |
Bridge Record actualModel |
gpt-6.1-sol | high |
Rollout turn_context |
gpt-6.1-sol | high |
Therefore, cards and session headers can distinguish gpt-6.1-sol from gpt-6-sol. These are different models; the former defaults reasoning to low, the latter to medium.
Session identification rules: Sending while a specific session segment is selected equals continuing that Codex session; sending without selection opens a new segment. After sending, it automatically selects the new session and refreshes the list.
Let GPT Act as a Sub-agent: codex_gpt & Multi-turn Continuation¶
The DSH agent sees the new tool codex_gpt, with parameters message (required), conversationId/threadId (one of the two), model, reasoning:
codex_gpt({ message: "Review milestones in docs/plan.md, find unexecutable parts", reasoning: "high" })
→ { conversationId: "c-...", threadId: "01a1...", reply: "..." }
For the second round, pass the same conversationId to continue the same Codex session without resending context:
codex_gpt({ conversationId: "c-...", message: "Expand on point 2, give specific changes" })
You can also directly resume any Codex thread:
codex_gpt({ threadId: "01a1ac8e-01a4-79a2-a70d-855254e70751", message: "Continue with the previous plan" })
Two pitfalls worth mentioning upfront:
- Reasoning levels are uniformly
low / medium / high / xhigh / max / ultra; there is nominimalnornone. Passingminimal, which old scripts are used to, will directly causeturn.failed. - If
model/reasoningare not passed, Codex defaults are used; if passed, they use-mand-c model_reasoning_effort=…. The README confirms they take effect.
HTTP Interface¶
Route prefix /api/codex-bridge, default port http://127.0.0.1:12530:
| Method | Path | Function |
|---|---|---|
| GET | /list |
Session list (including old records) + current default configuration |
| GET | /read?id=<id> |
Per-round questions and replies of a session + Codex side original text |
| GET | /status |
Workspace, codex path, isolation switch |
| GET | /models |
Available models directory (slug / display name / supported reasoning levels) |
| POST | /send |
Send a round of real dialogue, body: {message, conversationId?, threadId?, model?, reasoning?} |
| POST | /promote |
Mark a session as visible to Codex App, body: {threadId} or {conversationId} |
Send a round:
$body = @{ message = "Reply with only two words: Received"; reasoning = "low" } | ConvertTo-Json -Compress
Invoke-WebRequest -Uri "http://127.0.0.1:12530/api/codex-bridge/send" -Method POST `
-Body $body -ContentType "application/json" -UseBasicParsing -TimeoutSec 900
# → {"conversationId":"c-...","threadId":"01a1...","reply":"Received"}
Two points to note: POST /send will actually consume Codex quota, and it waits synchronously. The README tested it takes about 58 seconds for one low reasoning. Also, the route uses Node’s native handler instead of resource, because resource routes only allow GET/HEAD, and POST would be directly 405ed by the platform layer.
Context & Isolation: What GPT Can See¶
This is the initial question this plugin answers. The README tested using zero quota: codex debug prompt-input "test" prints exactly what the model receives as a JSON array. Looking at them one by one, what the model sees far exceeds the segment of prompt sent through the bridge.
| Injected Item | Test Evidence | Size/Content |
|---|---|---|
| Global Persona Instructions | developer message line 1 | prompts/gpt-with-taste/main.md pointed to by model_instructions_file in ~/.codex/config.toml, 24,461 characters |
| Skills List | developer message line 2 <skills_instructions> |
4,897 characters, listing skill roots like ~/.codex/skills/.system, plugins/cache/openai-bundled, etc. GPT can read SKILL.md itself once it sees the paths |
| MCP Tools | Tool table | mcp__cua_repl.js etc., from [mcp_servers.node_repl] in config.toml |
| Plugin Recommendation List | developer message <recommended_plugins> |
876 characters, 16 “available but not installed” plugins |
| Multi-agent System Prompts | developer message <multi_agent_role> / <multi_agent_mode> |
2,400 + 271 characters |
| Environment Context | user message <environment_context> |
cwd, shell, date, timezone, file system permission roots |
| Codex Memory | [features] memories = true / [memories] use_memories = true in config.toml |
Will read memories_1.sqlite / memories_v2_1.sqlite |
Conclusion: The bridge is only responsible for sending one segment of prompt; everything else is loaded by Codex itself. To get a cleaner context, you can turn on the plugin’s isolation switch DSH_CODEX_BRIDGE_ISOLATE=1 (or command line -Isolate), default is off. More thorough isolation methods are Codex CLI’s own parameters; the README provides three levels:
# 1) Complete isolation: do not load ~/.codex/config.toml (auth still uses CODEX_HOME)
codex exec --ignore-user-config -m gpt-6-sol ...
# 2) Only disable plugin recommendations
codex exec --disable plugins ...
# 3) Only disable MCP
codex exec -c 'mcp_servers={}' ...
Note that --ignore-user-config will also lose configuration like model, so -m must be explicitly passed when isolating.
Use Cases & Notes¶
Suitable for these types of usage: want GPT to participate in multi-turn collaboration in DSH as a sub-agent; want Codex sessions to continue in the Codex desktop app or CLI instead of dissipating after use; need to switch models and reasoning intensity based on tasks and archive them.
A few things need to be confirmed before getting started:
- The plugin runs with the permissions of the current DSH process and can read Codex CLI’s environment and configuration. Unverified source code is suggested to be reviewed personally before installation; the license is MIT.
- Sessions will consume Codex account quota. It is recommended to first use
codex debug prompt-inputand the isolation switch to confirm the scope of context, then decide whether to target specific questions. - Old records come from
outbox/*.prompt.md,inbox/<id>.json, andlogs/<id>.stdout.jsonl. Thread IDs can be retrieved from these two places. The README tested listing 3 old records before the upgrade, of which 2 successfully recovered thread IDs, and the other 1 is an empty record where the template didn’t write a reply.
Conclusion¶
This plugin turns the “use and discard” Codex CLI dialogue into a link in the DSH workflow that is checkable, resumable, and has switchable levels. The changes focus on the sidebar panel, the codex_gpt tool, and the HTTP routes. The scope of change is small but the anchor points are clear.
Community Plugin Directory Page: https://www.skillhub.cn/plugins/HATO-24/dsh-codex-handoff ; Source Code: https://github.com/HATO-24/dsh-codex-handoff . This directory is an independent site and has no official affiliation with DeepSeek / Huanfang.