@rynfar/meridian 1.52.0 → 1.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -99,7 +99,7 @@ Then in `~/.config/opencode/opencode.json`:
99
99
 
100
100
  > **Important:** Do not use `meridian setup` on NixOS. It writes an absolute Nix store path (e.g. `/nix/store/...-meridian-1.x.x/lib/...`) into your OpenCode config, which will break on the next `nixos-rebuild switch` or `home-manager switch` when the store path changes. Use one of the approaches above instead.
101
101
 
102
- > **Note:** The bundled Claude Code binary (`claude.exe`) is patched with `autoPatchelfHook` at build time, so it runs on NixOS out of the box. If you previously enabled `programs.nix-ld.enable = true` as a workaround for `Could not start dynamically linked executable` (#501), that is no longer required for Meridian.
102
+ > **Note:** Meridian's package depends on the unfree `claude-code` from nixpkgs instead of bundling its own binary. The flake accepts the unfree license when it builds the package and exports the finished derivation, so consuming it through the overlay or `packages.<system>.meridian` does not re-run nixpkgs' unfree check and needs no `allowUnfree` setting.
103
103
 
104
104
  **Home Manager service** -- run Meridian as a user systemd service:
105
105
 
@@ -122,8 +122,8 @@ Then in `~/.config/opencode/opencode.json`:
122
122
  # defaultAgent = "opencode";
123
123
  # sonnetModel = "sonnet";
124
124
  # Load plugins from the Nix store (rendered to a plugins.json manifest).
125
- # Point at an entry inside a packaged derivation so its deps come along.
126
- # pluginConfig = [ { path = "${pkgs.my-meridian-plugin}/lib/index.js"; } ];
125
+ # The official scrub plugins ship prebuilt via the meridian overlay:
126
+ # pluginConfig = [ { path = pkgs.meridianPlugins.opencode-scrub.path; } ];
127
127
  # pluginDir = "/path/to/extra/plugins";
128
128
  };
129
129
  # Extra env vars not covered by settings
@@ -497,6 +497,30 @@ ANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://127.0.0.1:3456 \
497
497
 
498
498
  > **Note:** `--no-stream` is incompatible due to a litellm parsing issue — use the default streaming mode.
499
499
 
500
+ ### Codex CLI
501
+
502
+ Codex CLI ≥ 0.96 dropped `wire_api = "chat"` and speaks only the OpenAI **Responses API** (`/v1/responses`), which Meridian serves. Add a provider to `~/.codex/config.toml`:
503
+
504
+ ```toml
505
+ model = "claude-sonnet-5"
506
+ model_provider = "meridian"
507
+
508
+ [model_providers.meridian]
509
+ name = "Meridian"
510
+ base_url = "http://127.0.0.1:3456/v1"
511
+ wire_api = "responses"
512
+ env_key = "MERIDIAN_KEY" # any value unless MERIDIAN_API_KEY is set
513
+ ```
514
+
515
+ ```bash
516
+ MERIDIAN_KEY=x codex "refactor this function"
517
+ MERIDIAN_KEY=x codex exec "run the tests and summarize failures" # non-interactive
518
+ ```
519
+
520
+ Codex is a tool-driving agent — Meridian runs the `/v1/responses` endpoint in **passthrough** mode automatically (Codex executes its own shell/apply-patch tools), so no `MERIDIAN_PASSTHROUGH` change is needed. A harmless `Model metadata for 'claude-sonnet-5' not found` warning from Codex is expected — it doesn't recognize non-OpenAI model ids but works regardless.
521
+
522
+ `model_reasoning_effort` is supported and won't stall the CLI, but Claude's private thinking isn't yet carried **across** turns — the Responses API's encrypted-reasoning envelope is OpenAI-specific and incompatible with Claude's signed thinking blocks, so cross-turn reasoning continuity is deferred (each turn still reasons with full context including tool results). Verified on Codex 0.144 with plain, tool-driving, and reasoning-enabled turns.
523
+
500
524
  ### OpenAI-compatible tools (Open WebUI, Continue, etc.)
501
525
 
502
526
  Meridian speaks the OpenAI protocol natively — no LiteLLM or translation proxy needed.
@@ -664,6 +688,7 @@ export ANTHROPIC_BASE_URL=http://127.0.0.1:3456
664
688
  | [Pi](https://github.com/mariozechner/pi-coding-agent) | ✅ Verified | models.json config (see above) — full tool support via passthrough; detected via `x-meridian-agent: pi` header |
665
689
  | [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | ✅ Verified | `ANTHROPIC_BASE_URL` — remote clients share a Max subscription over the network; client CWD preserved in system prompt |
666
690
  | [Cherry Studio](https://github.com/CherryHQ/cherry-studio) | ✅ Verified | `cherry` adapter (see above) — chat client with Claude's built-in web search via internal mode |
691
+ | [Codex CLI](https://github.com/openai/codex) | ✅ Verified | `/v1/responses` (see above) — Responses-API provider, passthrough tool execution; verified on 0.144 (plain + tool-driving turns) |
667
692
  | [Continue](https://github.com/continuedev/continue) | 🔲 Untested | OpenAI-compatible endpoints should work — set `apiBase` to `http://127.0.0.1:3456` |
668
693
 
669
694
  Tested an agent or built a plugin? [Open an issue](https://github.com/rynfar/meridian/issues) and we'll add it.
@@ -684,12 +709,14 @@ src/proxy/
684
709
  │ ├── cherry.ts ← Cherry Studio adapter (internal mode + web search)
685
710
  │ ├── claudecode.ts ← Claude Code adapter (remote clients sharing a Max host)
686
711
  │ ├── openai.ts ← OpenAI-endpoint adapter (/v1/chat/completions)
712
+ │ ├── codex.ts ← Codex CLI adapter (/v1/responses, forced passthrough)
687
713
  │ └── passthrough.ts ← LiteLLM passthrough adapter
688
714
  ├── query.ts ← SDK query options builder
689
715
  ├── errors.ts ← Error classification
690
716
  ├── models.ts ← Model mapping (sonnet/opus/haiku, agentMode)
691
717
  ├── tokenRefresh.ts ← Cross-platform OAuth token refresh
692
718
  ├── openai.ts ← OpenAI ↔ Anthropic format translation (pure)
719
+ ├── openaiResponses.ts ← OpenAI Responses API ↔ Anthropic translation (pure)
693
720
  ├── setup.ts ← OpenCode plugin configuration
694
721
  ├── session/
695
722
  │ ├── lineage.ts ← Per-message hashing, mutation classification (pure)
@@ -805,6 +832,7 @@ ANTHROPIC_API_KEY=your-secret-key ANTHROPIC_BASE_URL=http://meridian-host:3456 o
805
832
  | `POST /v1/messages` | Anthropic Messages API |
806
833
  | `POST /messages` | Alias for `/v1/messages` |
807
834
  | `POST /v1/chat/completions` | OpenAI-compatible chat completions |
835
+ | `POST /v1/responses` | OpenAI Responses API (Codex CLI ≥ 0.96) |
808
836
  | `GET /v1/models` | OpenAI-compatible model list |
809
837
  | `GET /health` | Auth status, mode, plugin status |
810
838
  | `POST /auth/refresh` | Manually refresh the OAuth token |
@@ -868,7 +896,9 @@ opt-in plugins instead:
868
896
  | [`@rynfar/meridian-plugin-pi-scrub`](https://github.com/rynfar/meridian-plugin-pi-scrub) | Strips Pi's coding-agent-harness prompt line that Anthropic meters as Extra Usage. |
869
897
  | [`@rynfar/meridian-plugin-opencode-scrub`](https://github.com/rynfar/meridian-plugin-opencode-scrub) | Strips OpenCode harness boilerplate from the system prompt before it reaches Claude. |
870
898
 
871
- Install into Meridian's config dir and register the built file in
899
+ **Nix users:** the flake packages all three prebuilt — `pkgs.meridianPlugins.<name>` via the `meridian` overlay (or `meridian.legacyPackages.${system}.meridianPlugins`), each exposing `.path` for a `plugins.json` entry or the home-manager `pluginConfig` option. Pins are refreshed by a scheduled workflow that rebuilds every plugin before bumping.
900
+
901
+ Everyone else: install into Meridian's config dir and register the built file in
872
902
  `~/.config/meridian/plugins.json`:
873
903
 
874
904
  ```bash
@@ -933,6 +963,8 @@ docker run -v ~/.claude:/home/claude/.claude -p 3456:3456 meridian
933
963
 
934
964
  Meridian refreshes OAuth tokens automatically — once the credentials are mounted, no further browser access is needed.
935
965
 
966
+ > **macOS hosts:** mounting `~/.claude` does **not** carry credentials into the container — on macOS the CLI stores OAuth tokens in the Keychain, not in files, so the container sees an empty credential store and requests fail with an authentication error. Use an [OAuth-token profile](#oauth-token-profiles-in-docker-no-volume-mount) instead (recommended), or run `claude login` once inside the container (`docker exec -it <name> claude login`).
967
+
936
968
  ### Multiple profiles in Docker
937
969
 
938
970
  Authenticate each profile locally, then pass them to Docker via the `MERIDIAN_PROFILES` environment variable:
@@ -1000,20 +1032,13 @@ meridian refresh-token
1000
1032
  curl -X POST http://127.0.0.1:3456/auth/refresh
1001
1033
  ```
1002
1034
 
1003
- **I'm getting `400 You're out of extra usage` only when tools are present. What do I do?**
1004
- First confirm the failure pattern: a tiny no-tool request succeeds, but the same client fails once it sends tool definitions. If that is the case, beta-header stripping and model fallback usually will not help because the request body still contains agentic tool context.
1005
-
1006
- For the affected adapter, try disabling the connecting client's system prompt while keeping the Claude Code prompt enabled:
1007
-
1008
- ```bash
1009
- curl -X PATCH http://127.0.0.1:3456/settings/api/features/pi \
1010
- -H 'Content-Type: application/json' \
1011
- -d '{"clientSystemPrompt":false,"codeSystemPrompt":true}'
1012
- ```
1035
+ **I'm getting `400 You're out of extra usage` on tool-bearing requests. What do I do?**
1036
+ This error class ([#516](https://github.com/rynfar/meridian/issues/516), historical) came from Anthropic's server-side classifier gating certain requests behind Extra Usage. It had two distinct triggers, both now addressed:
1013
1037
 
1014
- Replace `pi` with the adapter you use (`opencode`, `crush`, `forgecode`, `droid`, `passthrough`, or `openai`). You can make the same change in the `/settings` UI under **SDK Feature Toggles**. (The `openai` adapter used by the `/v1/chat/completions` endpoint — already defaults `codeSystemPrompt` to off, so on that one you typically only need `clientSystemPrompt:false`.)
1038
+ - **Harness fingerprints** identity lines in a client's system prompt (e.g. pi's "coding agent harness" line) were metered as Extra Usage. The [official scrub plugins](#official-plugins) strip these and remain recommended for the affected harnesses.
1039
+ - **Tool-definition presence** — reported in mid-2026 as triggering independently of prompt content; as of July 2026 this no longer reproduces on Max accounts (verified with Extra Usage disabled, tools present, and an unscrubbed fingerprint prompt). It appears to have been resolved upstream in Anthropic's billing policy.
1015
1040
 
1016
- This keeps the SDK's preset prompt and tool bridge, but removes the external client's large agent prompt from the request. That may help when the error is triggered by the combination of tool definitions plus client prompt context. The tradeoff is that the connected agent may behave more like vanilla Claude Code because its own persona and workflow instructions are no longer included. If it still fails, the remaining options are to use fewer/no tools for that client, enable Extra Usage/API billing, or switch to a local/provider-backed model for that workflow. See [#516](https://github.com/rynfar/meridian/issues/516) for the current debugging thread.
1041
+ If you still hit the error on a current release, first check `GET /v1/usage/quota` to rule out genuinely exhausted quota, then try disabling the connecting client's system prompt for the affected adapter while keeping the Claude Code prompt enabled (in the `/settings` UI under **SDK Feature Toggles**, or `PATCH /settings/api/features/<adapter>` with `{"clientSystemPrompt":false,"codeSystemPrompt":true}`) and please report it on [#516](https://github.com/rynfar/meridian/issues/516) with your plan type, since remaining occurrences are likely account-cohort specific (Team plans are treated differently by the API).
1017
1042
 
1018
1043
  **I'm hitting rate limits on 1M context. What do I do?**
1019
1044
  Meridian defaults Sonnet to 200k context because Sonnet 1M is always billed as Extra Usage on Max plans — even when regular usage isn't exhausted. This is [Anthropic's intended billing model](https://code.claude.com/docs/en/model-config#extended-context), not a bug. Set `MERIDIAN_SONNET_MODEL=sonnet[1m]` to opt in if you have Extra Usage enabled and understand the billing implications. Opus defaults to 1M context, which is included with Max/Team/Enterprise subscriptions at no extra cost. Note: there is a [known upstream bug](https://github.com/anthropics/claude-code/issues/39841) where Claude Code incorrectly gates Opus 1M behind Extra Usage on Max — this is Anthropic's to fix.
@@ -1534,7 +1534,7 @@ function getExplicitThinking(adapterName) {
1534
1534
  return typeof explicit === "string" && VALID_THINKING_VALUES.has(explicit) ? explicit : undefined;
1535
1535
  }
1536
1536
  function getAllFeatureConfigs() {
1537
- const adapters = ["opencode", "crush", "forgecode", "pi", "droid", "passthrough", "openai"];
1537
+ const adapters = ["opencode", "crush", "forgecode", "pi", "droid", "passthrough", "openai", "codex"];
1538
1538
  const result = {};
1539
1539
  for (const name of adapters) {
1540
1540
  result[name] = getFeaturesForAdapter(name);
@@ -1612,6 +1612,9 @@ var init_sdkFeatures = __esm(() => {
1612
1612
  },
1613
1613
  cherry: {
1614
1614
  codeSystemPrompt: false
1615
+ },
1616
+ codex: {
1617
+ codeSystemPrompt: false
1615
1618
  }
1616
1619
  };
1617
1620
  VALID_CLAUDE_MD_VALUES = new Set(["off", "project", "full"]);
@@ -10738,6 +10741,331 @@ function buildModelList(isMaxSubscription, now = Math.floor(Date.now() / 1000))
10738
10741
  ];
10739
10742
  }
10740
10743
 
10744
+ // src/proxy/openaiResponses.ts
10745
+ function partsToText(content) {
10746
+ if (typeof content === "string")
10747
+ return content;
10748
+ return content.filter((p) => typeof p.text === "string").map((p) => p.text).join("");
10749
+ }
10750
+ function mapToolChoice(tc) {
10751
+ if (tc === "auto")
10752
+ return { type: "auto" };
10753
+ if (tc === "required")
10754
+ return { type: "any" };
10755
+ if (tc && typeof tc === "object") {
10756
+ const o = tc;
10757
+ if (o.type === "function" && o.name)
10758
+ return { type: "tool", name: o.name };
10759
+ if (o.type === "auto")
10760
+ return { type: "auto" };
10761
+ if (o.type === "any" || o.type === "required")
10762
+ return { type: "any" };
10763
+ }
10764
+ return;
10765
+ }
10766
+ function translateResponsesToAnthropic(body) {
10767
+ if (body.input === undefined || body.input === null)
10768
+ return null;
10769
+ const items = typeof body.input === "string" ? [{ type: "message", role: "user", content: [{ type: "input_text", text: body.input }] }] : body.input;
10770
+ const systemParts = [];
10771
+ if (body.instructions)
10772
+ systemParts.push(body.instructions);
10773
+ const messages = [];
10774
+ const pushBlock = (role, block) => {
10775
+ const last = messages[messages.length - 1];
10776
+ if (last && last.role === role && Array.isArray(last.content)) {
10777
+ last.content.push(block);
10778
+ } else {
10779
+ messages.push({ role, content: [block] });
10780
+ }
10781
+ };
10782
+ for (const item of items) {
10783
+ switch (item.type) {
10784
+ case "message": {
10785
+ const msg = item;
10786
+ if (msg.role === "developer" || msg.role === "system") {
10787
+ const t = partsToText(msg.content);
10788
+ if (t)
10789
+ systemParts.push(t);
10790
+ break;
10791
+ }
10792
+ const text = partsToText(msg.content);
10793
+ if (text)
10794
+ pushBlock(msg.role === "assistant" ? "assistant" : "user", { type: "text", text });
10795
+ break;
10796
+ }
10797
+ case "function_call": {
10798
+ const fc = item;
10799
+ let input = {};
10800
+ try {
10801
+ input = fc.arguments ? JSON.parse(fc.arguments) : {};
10802
+ } catch {
10803
+ input = {};
10804
+ }
10805
+ pushBlock("assistant", { type: "tool_use", id: fc.call_id, name: fc.name, input });
10806
+ break;
10807
+ }
10808
+ case "function_call_output": {
10809
+ const fo = item;
10810
+ pushBlock("user", { type: "tool_result", tool_use_id: fo.call_id, content: fo.output });
10811
+ break;
10812
+ }
10813
+ case "reasoning":
10814
+ break;
10815
+ default:
10816
+ break;
10817
+ }
10818
+ }
10819
+ const result = {
10820
+ model: typeof body.model === "string" ? body.model : "",
10821
+ messages,
10822
+ max_tokens: body.max_output_tokens ?? 8192,
10823
+ stream: body.stream ?? false
10824
+ };
10825
+ if (systemParts.length > 0)
10826
+ result.system = systemParts.join(`
10827
+
10828
+ `);
10829
+ if (Array.isArray(body.tools) && body.tools.length > 0) {
10830
+ result.tools = body.tools.filter((t) => t.type === "function").map((t) => ({
10831
+ name: t.name,
10832
+ description: t.description ?? "",
10833
+ input_schema: t.parameters ?? { type: "object", properties: {} }
10834
+ }));
10835
+ }
10836
+ const tc = mapToolChoice(body.tool_choice);
10837
+ if (tc)
10838
+ result.tool_choice = tc;
10839
+ if (body.temperature !== undefined)
10840
+ result.temperature = body.temperature;
10841
+ if (body.top_p !== undefined)
10842
+ result.top_p = body.top_p;
10843
+ if (body.reasoning?.effort !== undefined)
10844
+ result.reasoning_effort = body.reasoning.effort;
10845
+ return result;
10846
+ }
10847
+ function reasoningRequested(body) {
10848
+ if (body.reasoning && typeof body.reasoning === "object")
10849
+ return true;
10850
+ const include = body.include;
10851
+ return Array.isArray(include) && include.some((v) => typeof v === "string" && v.startsWith("reasoning"));
10852
+ }
10853
+ function mapUsage(usage) {
10854
+ const input = usage?.input_tokens ?? 0;
10855
+ const output = usage?.output_tokens ?? 0;
10856
+ return { input_tokens: input, output_tokens: output, total_tokens: input + output };
10857
+ }
10858
+ function translateAnthropicToResponses(res, ctx) {
10859
+ const output = [];
10860
+ const textParts = [];
10861
+ for (const block of res.content ?? []) {
10862
+ if (block.type === "text" && typeof block.text === "string") {
10863
+ textParts.push({ type: "output_text", text: block.text, annotations: [] });
10864
+ } else if (block.type === "tool_use") {
10865
+ output.push({
10866
+ type: "function_call",
10867
+ id: `fc_${block.id}`,
10868
+ call_id: block.id,
10869
+ name: block.name,
10870
+ arguments: JSON.stringify(block.input ?? {}),
10871
+ status: "completed"
10872
+ });
10873
+ }
10874
+ }
10875
+ if (textParts.length > 0) {
10876
+ output.unshift({
10877
+ type: "message",
10878
+ id: `msg_${ctx.responseId}`,
10879
+ status: "completed",
10880
+ role: "assistant",
10881
+ content: textParts
10882
+ });
10883
+ }
10884
+ if (ctx.reasoningRequested) {
10885
+ output.unshift({
10886
+ type: "reasoning",
10887
+ id: `rs_${ctx.responseId}`,
10888
+ summary: [],
10889
+ encrypted_content: null,
10890
+ status: "completed"
10891
+ });
10892
+ }
10893
+ return {
10894
+ id: ctx.responseId,
10895
+ object: "response",
10896
+ created_at: ctx.created,
10897
+ model: ctx.model,
10898
+ status: "completed",
10899
+ output,
10900
+ usage: mapUsage(res.usage),
10901
+ parallel_tool_calls: true,
10902
+ tool_choice: "auto",
10903
+ tools: []
10904
+ };
10905
+ }
10906
+ function createResponsesSseTranslator(ctx) {
10907
+ let seq = 0;
10908
+ let outputIndex = 0;
10909
+ let inputTokens = 0;
10910
+ let outputTokens = 0;
10911
+ let createdEmitted = false;
10912
+ const blocks = new Map;
10913
+ const finalOutput = [];
10914
+ const emit = (event, data) => ({
10915
+ event,
10916
+ data: { type: event, ...data, sequence_number: seq++ }
10917
+ });
10918
+ const responseEnvelope = (status, extra = {}) => ({
10919
+ id: ctx.responseId,
10920
+ object: "response",
10921
+ created_at: ctx.created,
10922
+ model: ctx.model,
10923
+ status,
10924
+ ...extra
10925
+ });
10926
+ return (event) => {
10927
+ const out = [];
10928
+ switch (event.type) {
10929
+ case "message_start": {
10930
+ inputTokens = event.message?.usage?.input_tokens ?? 0;
10931
+ if (!createdEmitted) {
10932
+ createdEmitted = true;
10933
+ out.push(emit("response.created", { response: responseEnvelope("in_progress", { output: [] }) }));
10934
+ out.push(emit("response.in_progress", { response: responseEnvelope("in_progress", { output: [] }) }));
10935
+ if (ctx.reasoningRequested) {
10936
+ const oi = outputIndex++;
10937
+ const itemId = `rs_${ctx.responseId}`;
10938
+ const item = { type: "reasoning", id: itemId, summary: [], encrypted_content: null, status: "completed" };
10939
+ out.push(emit("response.output_item.added", { output_index: oi, item: { ...item, status: "in_progress" } }));
10940
+ finalOutput.push(item);
10941
+ out.push(emit("response.output_item.done", { output_index: oi, item }));
10942
+ }
10943
+ }
10944
+ break;
10945
+ }
10946
+ case "content_block_start": {
10947
+ const idx = event.index ?? 0;
10948
+ const cb = event.content_block ?? {};
10949
+ if (cb.type === "text") {
10950
+ const oi = outputIndex++;
10951
+ const itemId = `msg_${ctx.responseId}_${oi}`;
10952
+ blocks.set(idx, { kind: "text", outputIndex: oi, itemId, text: "", args: "" });
10953
+ out.push(emit("response.output_item.added", {
10954
+ output_index: oi,
10955
+ item: { type: "message", id: itemId, status: "in_progress", role: "assistant", content: [] }
10956
+ }));
10957
+ out.push(emit("response.content_part.added", {
10958
+ item_id: itemId,
10959
+ output_index: oi,
10960
+ content_index: 0,
10961
+ part: { type: "output_text", text: "", annotations: [] }
10962
+ }));
10963
+ } else if (cb.type === "tool_use") {
10964
+ const oi = outputIndex++;
10965
+ const itemId = `fc_${cb.id}`;
10966
+ blocks.set(idx, { kind: "tool", outputIndex: oi, itemId, text: "", args: "", callId: cb.id, name: cb.name });
10967
+ out.push(emit("response.output_item.added", {
10968
+ output_index: oi,
10969
+ item: { type: "function_call", id: itemId, call_id: cb.id, name: cb.name, arguments: "", status: "in_progress" }
10970
+ }));
10971
+ } else {
10972
+ blocks.set(idx, { kind: "skip", outputIndex: -1, itemId: "", text: "", args: "" });
10973
+ }
10974
+ break;
10975
+ }
10976
+ case "content_block_delta": {
10977
+ const idx = event.index ?? 0;
10978
+ const st = blocks.get(idx);
10979
+ if (!st || st.kind === "skip")
10980
+ break;
10981
+ if (st.kind === "text" && event.delta?.type === "text_delta" && typeof event.delta.text === "string") {
10982
+ st.text += event.delta.text;
10983
+ out.push(emit("response.output_text.delta", {
10984
+ item_id: st.itemId,
10985
+ output_index: st.outputIndex,
10986
+ content_index: 0,
10987
+ delta: event.delta.text
10988
+ }));
10989
+ } else if (st.kind === "tool" && event.delta?.type === "input_json_delta" && typeof event.delta.partial_json === "string") {
10990
+ st.args += event.delta.partial_json;
10991
+ out.push(emit("response.function_call_arguments.delta", {
10992
+ item_id: st.itemId,
10993
+ output_index: st.outputIndex,
10994
+ delta: event.delta.partial_json
10995
+ }));
10996
+ }
10997
+ break;
10998
+ }
10999
+ case "content_block_stop": {
11000
+ const idx = event.index ?? 0;
11001
+ const st = blocks.get(idx);
11002
+ if (!st || st.kind === "skip")
11003
+ break;
11004
+ if (st.kind === "text") {
11005
+ out.push(emit("response.output_text.done", {
11006
+ item_id: st.itemId,
11007
+ output_index: st.outputIndex,
11008
+ content_index: 0,
11009
+ text: st.text
11010
+ }));
11011
+ out.push(emit("response.content_part.done", {
11012
+ item_id: st.itemId,
11013
+ output_index: st.outputIndex,
11014
+ content_index: 0,
11015
+ part: { type: "output_text", text: st.text, annotations: [] }
11016
+ }));
11017
+ const item = {
11018
+ type: "message",
11019
+ id: st.itemId,
11020
+ status: "completed",
11021
+ role: "assistant",
11022
+ content: [{ type: "output_text", text: st.text, annotations: [] }]
11023
+ };
11024
+ finalOutput.push(item);
11025
+ out.push(emit("response.output_item.done", { output_index: st.outputIndex, item }));
11026
+ } else if (st.kind === "tool") {
11027
+ out.push(emit("response.function_call_arguments.done", {
11028
+ item_id: st.itemId,
11029
+ output_index: st.outputIndex,
11030
+ arguments: st.args
11031
+ }));
11032
+ const item = {
11033
+ type: "function_call",
11034
+ id: st.itemId,
11035
+ call_id: st.callId,
11036
+ name: st.name,
11037
+ arguments: st.args,
11038
+ status: "completed"
11039
+ };
11040
+ finalOutput.push(item);
11041
+ out.push(emit("response.output_item.done", { output_index: st.outputIndex, item }));
11042
+ }
11043
+ break;
11044
+ }
11045
+ case "message_delta": {
11046
+ if (typeof event.usage?.output_tokens === "number")
11047
+ outputTokens = event.usage.output_tokens;
11048
+ break;
11049
+ }
11050
+ case "message_stop": {
11051
+ out.push(emit("response.completed", {
11052
+ response: responseEnvelope("completed", {
11053
+ output: finalOutput,
11054
+ usage: { input_tokens: inputTokens, output_tokens: outputTokens, total_tokens: inputTokens + outputTokens },
11055
+ parallel_tool_calls: true,
11056
+ tool_choice: "auto",
11057
+ tools: []
11058
+ })
11059
+ }));
11060
+ break;
11061
+ }
11062
+ default:
11063
+ break;
11064
+ }
11065
+ return out;
11066
+ };
11067
+ }
11068
+
10741
11069
  // src/proxy/messages.ts
10742
11070
  function stripCacheControlForHashing(obj) {
10743
11071
  if (!obj || typeof obj !== "object")
@@ -11136,7 +11464,7 @@ init_env();
11136
11464
  var openCodeTransforms = [
11137
11465
  {
11138
11466
  name: "opencode-core",
11139
- adapters: ["opencode", "openai"],
11467
+ adapters: ["opencode", "openai", "codex"],
11140
11468
  onRequest(ctx) {
11141
11469
  const body = ctx.body;
11142
11470
  const blockedTools = BLOCKED_BUILTIN_TOOLS;
@@ -11940,6 +12268,15 @@ var openAiAdapter = {
11940
12268
  name: "openai"
11941
12269
  };
11942
12270
 
12271
+ // src/proxy/adapters/codex.ts
12272
+ var codexAdapter = {
12273
+ ...openCodeAdapter,
12274
+ name: "codex",
12275
+ usesPassthrough() {
12276
+ return true;
12277
+ }
12278
+ };
12279
+
11943
12280
  // src/proxy/adapters/cherry.ts
11944
12281
  var CHERRY_WEB_TOOLS = ["WebSearch", "WebFetch"];
11945
12282
  var isWebTool = (t) => CHERRY_WEB_TOOLS.includes(t);
@@ -12061,7 +12398,8 @@ var ADAPTER_MAP = {
12061
12398
  claudecode: claudeCodeAdapter,
12062
12399
  cherry: cherryAdapter,
12063
12400
  cherrystudio: cherryAdapter,
12064
- openai: openAiAdapter
12401
+ openai: openAiAdapter,
12402
+ codex: codexAdapter
12065
12403
  };
12066
12404
  var envDefault = process.env.MERIDIAN_DEFAULT_AGENT || "";
12067
12405
  if (envDefault && !ADAPTER_MAP[envDefault]) {
@@ -18034,6 +18372,17 @@ var cherryTransforms = [
18034
18372
  }
18035
18373
  ];
18036
18374
 
18375
+ // src/proxy/transforms/codex.ts
18376
+ var codexTransforms = [
18377
+ {
18378
+ name: "codex-force-passthrough",
18379
+ adapters: ["codex"],
18380
+ onRequest(ctx) {
18381
+ return { ...ctx, passthrough: true };
18382
+ }
18383
+ }
18384
+ ];
18385
+
18037
18386
  // src/proxy/transforms/registry.ts
18038
18387
  var ADAPTER_TRANSFORMS = {
18039
18388
  opencode: openCodeTransforms,
@@ -18043,7 +18392,8 @@ var ADAPTER_TRANSFORMS = {
18043
18392
  forgecode: forgeCodeTransforms,
18044
18393
  passthrough: passthroughTransforms,
18045
18394
  cherry: cherryTransforms,
18046
- openai: openCodeTransforms
18395
+ openai: openCodeTransforms,
18396
+ codex: [...openCodeTransforms, ...codexTransforms]
18047
18397
  };
18048
18398
  function getAdapterTransforms(adapterName) {
18049
18399
  return ADAPTER_TRANSFORMS[adapterName] ?? [];
@@ -19169,7 +19519,7 @@ function createProxyServer(config = {}) {
19169
19519
  status: "ok",
19170
19520
  service: "meridian",
19171
19521
  format: "anthropic",
19172
- endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/models", "/telemetry", "/metrics", "/health"]
19522
+ endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/responses", "/v1/models", "/telemetry", "/metrics", "/health"]
19173
19523
  });
19174
19524
  }
19175
19525
  return c.html(landingHtml);
@@ -21534,6 +21884,107 @@ data: ${JSON.stringify({
21534
21884
  }
21535
21885
  });
21536
21886
  });
21887
+ app.post("/v1/responses", async (c) => {
21888
+ const rawBody = await c.req.json();
21889
+ const anthropicBody = translateResponsesToAnthropic(rawBody);
21890
+ if (!anthropicBody) {
21891
+ return c.json({ error: { type: "invalid_request_error", message: "input: Field required", code: null } }, 400);
21892
+ }
21893
+ if (!anthropicBody.model) {
21894
+ return c.json({ error: { type: "invalid_request_error", message: "model: Field required", code: null } }, 400);
21895
+ }
21896
+ const internalHeaders = {
21897
+ "Content-Type": "application/json",
21898
+ "x-meridian-agent": "codex"
21899
+ };
21900
+ const xApiKey = c.req.header("x-api-key");
21901
+ if (xApiKey)
21902
+ internalHeaders["x-api-key"] = xApiKey;
21903
+ const authz = c.req.header("authorization");
21904
+ if (authz)
21905
+ internalHeaders["authorization"] = authz;
21906
+ const xProfile = c.req.header("x-meridian-profile");
21907
+ if (xProfile)
21908
+ internalHeaders["x-meridian-profile"] = xProfile;
21909
+ const internalReq = new Request("http://internal/v1/messages", {
21910
+ method: "POST",
21911
+ headers: internalHeaders,
21912
+ body: JSON.stringify(anthropicBody)
21913
+ });
21914
+ const internalRes = await app.fetch(internalReq);
21915
+ if (!internalRes.ok) {
21916
+ const errBody = await internalRes.text();
21917
+ return c.json({ error: { type: "upstream_error", message: errBody, code: null } }, internalRes.status);
21918
+ }
21919
+ const responseId = `resp_${randomUUID().replace(/-/g, "")}`;
21920
+ const created = Math.floor(Date.now() / 1000);
21921
+ const model = typeof rawBody.model === "string" && rawBody.model ? rawBody.model : CANONICAL_SONNET_MODEL;
21922
+ const ctx = { responseId, model, created, reasoningRequested: reasoningRequested(rawBody) };
21923
+ if (!anthropicBody.stream) {
21924
+ const anthropicRes = await internalRes.json();
21925
+ return c.json(translateAnthropicToResponses(anthropicRes, ctx));
21926
+ }
21927
+ const encoder = new TextEncoder;
21928
+ const readable = new ReadableStream({
21929
+ async start(controller) {
21930
+ const reader = internalRes.body?.getReader();
21931
+ if (!reader) {
21932
+ controller.close();
21933
+ return;
21934
+ }
21935
+ const decoder = new TextDecoder;
21936
+ let buffer = "";
21937
+ const translate = createResponsesSseTranslator(ctx);
21938
+ try {
21939
+ while (true) {
21940
+ const { done, value } = await reader.read();
21941
+ if (done)
21942
+ break;
21943
+ buffer += decoder.decode(value, { stream: true });
21944
+ const lines = buffer.split(`
21945
+ `);
21946
+ buffer = lines.pop() ?? "";
21947
+ for (const line of lines) {
21948
+ if (!line.startsWith("data: "))
21949
+ continue;
21950
+ const dataStr = line.slice(6).trim();
21951
+ if (!dataStr)
21952
+ continue;
21953
+ let event;
21954
+ try {
21955
+ event = JSON.parse(dataStr);
21956
+ } catch {
21957
+ continue;
21958
+ }
21959
+ if (typeof event.type !== "string")
21960
+ continue;
21961
+ for (const emission of translate(event)) {
21962
+ controller.enqueue(encoder.encode(`event: ${emission.event}
21963
+ data: ${JSON.stringify(emission.data)}
21964
+
21965
+ `));
21966
+ }
21967
+ }
21968
+ }
21969
+ } catch (err) {
21970
+ const message = err instanceof Error ? err.message : String(err);
21971
+ controller.enqueue(encoder.encode(`event: response.failed
21972
+ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: { message } } })}
21973
+
21974
+ `));
21975
+ } finally {
21976
+ controller.close();
21977
+ }
21978
+ }
21979
+ });
21980
+ return new Response(readable, {
21981
+ headers: {
21982
+ "Content-Type": "text/event-stream",
21983
+ "Cache-Control": "no-cache",
21984
+ Connection: "keep-alive"
21985
+ }
21986
+ });
21987
+ });
21537
21988
  app.get("/v1/models", async (c) => {
21538
21989
  const authStatus = await getClaudeAuthStatusAsync();
21539
21990
  const isMax = authStatus?.subscriptionType === "max";
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startProxyServer
4
- } from "./cli-tq3vtp0q.js";
4
+ } from "./cli-027as0fx.js";
5
5
  import"./cli-f0yqy2d2.js";
6
6
  import"./cli-sry5aqdj.js";
7
7
  import"./cli-xmweegb1.js";
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Codex CLI endpoint adapter (#475).
3
+ *
4
+ * The `/v1/responses` route serves the Codex CLI (≥ 0.96), which is a
5
+ * tool-driving agentic client: it sends `tools`, expects tool_use back,
6
+ * executes them locally, and returns `function_call_output`. So this endpoint
7
+ * MUST run in passthrough mode — tool_use flows back to the client — unlike
8
+ * the generic `openai` adapter which follows the global passthrough setting.
9
+ *
10
+ * The handler tags the internal /v1/messages hop with `x-meridian-agent:
11
+ * codex` so this adapter is selected deterministically. Behaviour otherwise
12
+ * mirrors `opencode` (tools, MCP server, transforms); the differences are:
13
+ * - `usesPassthrough() => true` — always forward tool calls (Codex needs them)
14
+ * - `codeSystemPrompt` OFF (sdkFeatures.ADAPTER_DEFAULTS) — Codex ships its
15
+ * own ~21KB instructions; the Claude Code preset must not override them
16
+ *
17
+ * NOTE: agent-specific (Codex). Keep this a thin re-identification of the
18
+ * OpenCode adapter; do not fork behaviour here.
19
+ */
20
+ import type { AgentAdapter } from "../adapter";
21
+ export declare const codexAdapter: AgentAdapter;
22
+ //# sourceMappingURL=codex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/codex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAG9C,eAAO,MAAM,YAAY,EAAE,YAM1B,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/detect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAsF9C,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,YAAY,CAsEtD"}
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/detect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AA0F9C,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,YAAY,CAsEtD"}
@@ -102,6 +102,11 @@ export interface AnthropicRequestBody {
102
102
  temperature?: number;
103
103
  top_p?: number;
104
104
  tools?: AnthropicTool[];
105
+ /** Tool selection constraint (carried from Responses `tool_choice`, #475). */
106
+ tool_choice?: {
107
+ type: "auto" | "any" | "tool";
108
+ name?: string;
109
+ };
105
110
  /** Reasoning effort carried from the OpenAI request so the internal
106
111
  * /v1/messages hop forwards it to the SDK (value gated by normalizeEffort). */
107
112
  reasoning_effort?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/proxy/openai.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/proxy/openai.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * OpenAI Responses API ⇄ Anthropic translation (#475).
3
+ *
4
+ * Pure functions — no HTTP/Hono — mirroring `openai.ts` (the chat-completions
5
+ * adapter). Serves the Codex CLI ≥ 0.96, which dropped `wire_api="chat"` and
6
+ * now speaks only `POST /v1/responses`. The `/v1/responses` route in
7
+ * server.ts forwards in-process to `/v1/messages` and pipes the response
8
+ * through these translators.
9
+ *
10
+ * Scope is Phase 1 of the approved design spec
11
+ * (docs/superpowers/specs/2026-07-08-codex-responses-api-design.md): text +
12
+ * function tool-calling, streaming and non-streaming, forced passthrough.
13
+ * Reasoning items are omitted (verified working against real Codex 0.143).
14
+ *
15
+ * NOTE: agent-specific (Codex). Kept isolated per ARCHITECTURE.md — this
16
+ * module owns the Responses wire format; no Responses logic leaks elsewhere.
17
+ */
18
+ import type { AnthropicRequestBody } from "./openai";
19
+ interface ResponsesContentPart {
20
+ type: "input_text" | "output_text" | string;
21
+ text?: string;
22
+ }
23
+ interface ResponsesMessageItem {
24
+ type: "message";
25
+ role: "user" | "assistant" | "developer" | "system";
26
+ content: ResponsesContentPart[] | string;
27
+ }
28
+ interface ResponsesFunctionCallItem {
29
+ type: "function_call";
30
+ name: string;
31
+ arguments: string;
32
+ call_id: string;
33
+ }
34
+ interface ResponsesFunctionCallOutputItem {
35
+ type: "function_call_output";
36
+ call_id: string;
37
+ output: string;
38
+ }
39
+ interface ResponsesReasoningItem {
40
+ type: "reasoning";
41
+ [k: string]: unknown;
42
+ }
43
+ type ResponsesInputItem = ResponsesMessageItem | ResponsesFunctionCallItem | ResponsesFunctionCallOutputItem | ResponsesReasoningItem;
44
+ interface ResponsesTool {
45
+ type: "function" | string;
46
+ name: string;
47
+ description?: string;
48
+ strict?: boolean;
49
+ parameters?: unknown;
50
+ }
51
+ export interface ResponsesRequest {
52
+ model?: string;
53
+ instructions?: string;
54
+ input?: string | ResponsesInputItem[];
55
+ tools?: ResponsesTool[];
56
+ tool_choice?: unknown;
57
+ parallel_tool_calls?: boolean;
58
+ max_output_tokens?: number;
59
+ temperature?: number;
60
+ top_p?: number;
61
+ reasoning?: {
62
+ effort?: string;
63
+ };
64
+ stream?: boolean;
65
+ [k: string]: unknown;
66
+ }
67
+ /**
68
+ * Translate a Responses request to an Anthropic /v1/messages body.
69
+ * Returns null when `input` is missing (invalid request).
70
+ */
71
+ export declare function translateResponsesToAnthropic(body: ResponsesRequest): AnthropicRequestBody | null;
72
+ export interface ResponsesCtx {
73
+ responseId: string;
74
+ model: string;
75
+ created: number;
76
+ /**
77
+ * The client asked for reasoning (Codex sends `reasoning.effort` +
78
+ * `include: ["reasoning.encrypted_content"]`). Phase 1 doesn't forward
79
+ * Claude's signed thinking, but Codex 0.144 HANGS waiting for a reasoning
80
+ * item it requested — so we emit one empty placeholder reasoning item to
81
+ * satisfy its state machine. Set from `reasoningRequested(body)`.
82
+ */
83
+ reasoningRequested?: boolean;
84
+ }
85
+ /** Did the Responses request ask for reasoning output? */
86
+ export declare function reasoningRequested(body: ResponsesRequest): boolean;
87
+ interface AnthropicResponseLike {
88
+ content?: Array<Record<string, unknown>>;
89
+ stop_reason?: string;
90
+ usage?: {
91
+ input_tokens?: number;
92
+ output_tokens?: number;
93
+ };
94
+ }
95
+ /**
96
+ * Assemble a complete Responses `response` object from an Anthropic response.
97
+ * Text blocks become a single `message` item with `output_text` parts;
98
+ * tool_use blocks become `function_call` items. Thinking blocks are dropped.
99
+ */
100
+ export declare function translateAnthropicToResponses(res: AnthropicResponseLike, ctx: ResponsesCtx): Record<string, unknown>;
101
+ export interface AnthropicSseEvent {
102
+ type: string;
103
+ index?: number;
104
+ message?: {
105
+ id?: string;
106
+ usage?: {
107
+ input_tokens?: number;
108
+ };
109
+ };
110
+ content_block?: {
111
+ type?: string;
112
+ id?: string;
113
+ name?: string;
114
+ input?: unknown;
115
+ };
116
+ delta?: {
117
+ type?: string;
118
+ text?: string;
119
+ partial_json?: string;
120
+ thinking?: string;
121
+ stop_reason?: string;
122
+ };
123
+ usage?: {
124
+ output_tokens?: number;
125
+ };
126
+ }
127
+ export interface ResponsesSseEmission {
128
+ event: string;
129
+ data: Record<string, unknown>;
130
+ }
131
+ /**
132
+ * Stateful Anthropic-SSE → Responses-SSE translator. One instance per stream;
133
+ * call with each Anthropic event, emit the returned Responses events in order.
134
+ */
135
+ export declare function createResponsesSseTranslator(ctx: ResponsesCtx): (event: AnthropicSseEvent) => ResponsesSseEmission[];
136
+ export {};
137
+ //# sourceMappingURL=openaiResponses.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openaiResponses.d.ts","sourceRoot":"","sources":["../../src/proxy/openaiResponses.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"sdkFeatures.d.ts","sourceRoot":"","sources":["../../src/proxy/sdkFeatures.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,gBAAgB,EAAE,OAAO,CAAA;IACzB,kFAAkF;IAClF,kBAAkB,EAAE,OAAO,CAAA;IAC3B,4DAA4D;IAC5D,QAAQ,EAAE,KAAK,GAAG,SAAS,GAAG,MAAM,CAAA;IACpC,wDAAwD;IACxD,MAAM,EAAE,OAAO,CAAA;IACf,6CAA6C;IAC7C,QAAQ,EAAE,OAAO,CAAA;IACjB,0DAA0D;IAC1D,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,CAAA;IAC7C,4CAA4C;IAC5C,mBAAmB,EAAE,OAAO,CAAA;IAC5B,iFAAiF;IACjF,YAAY,EAAE,OAAO,CAAA;IACrB,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAA;IACpB,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAA;IACrB,+CAA+C;IAC/C,QAAQ,EAAE,OAAO,CAAA;IACjB,uEAAuE;IACvE,qBAAqB,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,CAAA;AA4FpE;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CAU1E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,GAAG,SAAS,CAKhG;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAOtE;AAKD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAgC5E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI,CAInG;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAI9D"}
1
+ {"version":3,"file":"sdkFeatures.d.ts","sourceRoot":"","sources":["../../src/proxy/sdkFeatures.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,gBAAgB,EAAE,OAAO,CAAA;IACzB,kFAAkF;IAClF,kBAAkB,EAAE,OAAO,CAAA;IAC3B,4DAA4D;IAC5D,QAAQ,EAAE,KAAK,GAAG,SAAS,GAAG,MAAM,CAAA;IACpC,wDAAwD;IACxD,MAAM,EAAE,OAAO,CAAA;IACf,6CAA6C;IAC7C,QAAQ,EAAE,OAAO,CAAA;IACjB,0DAA0D;IAC1D,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,CAAA;IAC7C,4CAA4C;IAC5C,mBAAmB,EAAE,OAAO,CAAA;IAC5B,iFAAiF;IACjF,YAAY,EAAE,OAAO,CAAA;IACrB,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAA;IACpB,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAA;IACrB,+CAA+C;IAC/C,QAAQ,EAAE,OAAO,CAAA;IACjB,uEAAuE;IACvE,qBAAqB,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,CAAA;AAkGpE;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CAU1E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,GAAG,SAAS,CAKhG;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAOtE;AAKD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAgC5E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI,CAInG;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAI9D"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAGvD,YAAY,EACV,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAA;AAKpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAuCnG,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EAEpB,KAAK,aAAa,EAGnB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAGzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AAgR7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAi/GhF;AAWD,wBAAgB,gCAAgC,IAAI,IAAI,CAavD;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmGhG"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAGvD,YAAY,EACV,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAA;AAKpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAwCnG,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EAEpB,KAAK,aAAa,EAGnB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAGzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AAgR7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAkmHhF;AAWD,wBAAgB,gCAAgC,IAAI,IAAI,CAavD;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmGhG"}
@@ -0,0 +1,12 @@
1
+ import type { Transform } from "../transform";
2
+ /**
3
+ * Codex CLI transform (#475). Runs after the shared OpenCode transform (which
4
+ * codex reuses for tool config); its only job is to FORCE passthrough on.
5
+ *
6
+ * Codex is a tool-driving agentic client — it executes its own tools and needs
7
+ * tool_use blocks returned to it — so it must run passthrough regardless of the
8
+ * global MERIDIAN_PASSTHROUGH setting. Internal mode (SDK executes tools) would
9
+ * leave Codex waiting for tool calls that never come back.
10
+ */
11
+ export declare const codexTransforms: Transform[];
12
+ //# sourceMappingURL=codex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/codex.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,cAAc,CAAA;AAE7D;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,EAQtC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/opencode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,cAAc,CAAA;AAO7D,eAAO,MAAM,kBAAkB,EAAE,SAAS,EA6FzC,CAAA"}
1
+ {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/opencode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,cAAc,CAAA;AAO7D,eAAO,MAAM,kBAAkB,EAAE,SAAS,EA+FzC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAuB7C,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE,CAE9E"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AA2B7C,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE,CAE9E"}
package/dist/server.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  runObserveHook,
12
12
  runTransformHook,
13
13
  startProxyServer
14
- } from "./cli-tq3vtp0q.js";
14
+ } from "./cli-027as0fx.js";
15
15
  import"./cli-f0yqy2d2.js";
16
16
  import"./cli-sry5aqdj.js";
17
17
  import"./cli-xmweegb1.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynfar/meridian",
3
- "version": "1.52.0",
3
+ "version": "1.53.0",
4
4
  "description": "Local Anthropic API powered by your Claude Max subscription. One subscription, every agent.",
5
5
  "type": "module",
6
6
  "main": "./dist/server.js",
@@ -14,13 +14,17 @@
14
14
  * { "plugin": ["/absolute/path/to/plugin/meridian.ts"] }
15
15
  */
16
16
 
17
+ type AgentInput = string | { name?: string; mode?: string }
18
+
17
19
  type Plugin = (input: any) => Promise<{
20
+ config?: (cfg: { agent?: Record<string, { mode?: string } | undefined> }) => Promise<void> | void
18
21
  "chat.headers"?: (
19
22
  input: {
20
23
  sessionID: string
21
- // Typed as string in the SDK types but is actually the full agent
22
- // object at runtime: { name: string; mode: "primary" | "subagent" | "all" }
23
- agent: string | { name?: string; mode?: string }
24
+ // Older OpenCode versions pass the full agent object
25
+ // ({ name, mode: "primary" | "subagent" | "all" }); OpenCode >= 1.17
26
+ // passes just the agent NAME as a string. Handle both.
27
+ agent: AgentInput
24
28
  model: { providerID: string }
25
29
  message: { id: string }
26
30
  },
@@ -28,8 +32,52 @@ type Plugin = (input: any) => Promise<{
28
32
  ) => Promise<void>
29
33
  }>
30
34
 
35
+ /**
36
+ * Modes of OpenCode's built-in agents (they are not listed in the merged
37
+ * config unless the user overrides them, so the `config` hook alone can't
38
+ * see them). User-defined agents and built-in overrides are layered on top
39
+ * from the config hook.
40
+ */
41
+ const BUILTIN_AGENT_MODES: Record<string, string> = {
42
+ build: "primary",
43
+ plan: "primary",
44
+ general: "subagent",
45
+ explore: "subagent",
46
+ // Hidden internal agents. These usually route to small_model (a
47
+ // non-Anthropic provider) but are mapped defensively.
48
+ title: "subagent",
49
+ summary: "subagent",
50
+ compaction: "subagent",
51
+ }
52
+
31
53
  const MeridianPlugin: Plugin = async () => {
54
+ // Modes from the merged config, per plugin instance. Replaced wholesale on
55
+ // every config-hook fire so a reload can't leave stale entries behind.
56
+ let configModes: Record<string, string> = {}
57
+
58
+ const resolve = (agent: AgentInput): { name: string; mode: string } => {
59
+ if (typeof agent === "object" && agent !== null) {
60
+ // Legacy runtime shape: full agent object with an explicit mode.
61
+ return { name: agent.name ?? "unknown", mode: agent.mode ?? "primary" }
62
+ }
63
+ // OpenCode >= 1.17: agent is the name string. Resolve the mode from the
64
+ // merged config (captured in the config hook) + built-in defaults.
65
+ const name = String(agent)
66
+ return { name, mode: configModes[name] ?? BUILTIN_AGENT_MODES[name] ?? "primary" }
67
+ }
68
+
32
69
  return {
70
+ // Runs with the merged OpenCode config (on init, and again on config
71
+ // reload). Captures the mode of user-defined agents and built-in
72
+ // overrides so chat.headers can classify string agent names.
73
+ config: (cfg) => {
74
+ const next: Record<string, string> = {}
75
+ for (const [name, def] of Object.entries(cfg?.agent ?? {})) {
76
+ if (typeof def?.mode === "string") next[name] = def.mode
77
+ }
78
+ configModes = next
79
+ },
80
+
33
81
  "chat.headers": async (incoming, output) => {
34
82
  // Only inject headers for Anthropic provider requests
35
83
  if (incoming.model.providerID !== "anthropic") return
@@ -38,18 +86,15 @@ const MeridianPlugin: Plugin = async () => {
38
86
  output.headers["x-opencode-session"] = incoming.sessionID
39
87
  output.headers["x-opencode-request"] = incoming.message.id
40
88
 
41
- // Agent mode runtime value is the full agent object even though
42
- // the TypeScript type says string. Read .mode directly.
43
- const agent = incoming.agent as { name?: string; mode?: string } | string
44
- output.headers["x-opencode-agent-mode"] = typeof agent === "object"
45
- ? (agent.mode ?? "primary")
46
- : "primary"
47
- const rawName = typeof agent === "object"
48
- ? (agent.name ?? "unknown")
49
- : String(agent)
89
+ const { name, mode } = resolve(incoming.agent)
90
+
91
+ // The proxy expects primary|subagent. "all" agents can act as either;
92
+ // without per-request context, treat them as primary (full tier) to
93
+ // preserve capability.
94
+ output.headers["x-opencode-agent-mode"] = mode === "subagent" ? "subagent" : "primary"
50
95
  // Strip non-ASCII characters (e.g. zero-width spaces) that cause
51
96
  // "Header has invalid value" errors in Node.js / undici.
52
- output.headers["x-opencode-agent-name"] = rawName.replace(/[^\x20-\x7E]/g, "").trim() || "unknown"
97
+ output.headers["x-opencode-agent-name"] = name.replace(/[^\x20-\x7E]/g, "").trim() || "unknown"
53
98
  },
54
99
  }
55
100
  }