@rynfar/meridian 1.51.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.
@@ -22,9 +22,11 @@ import {
22
22
  themeCss
23
23
  } from "./cli-xmweegb1.js";
24
24
  import {
25
+ CANONICAL_SONNET_MODEL,
25
26
  env,
26
27
  envBool,
27
28
  envInt,
29
+ explicitModelPin,
28
30
  getAuthCacheInfo,
29
31
  getClaudeAuthStatusAsync,
30
32
  getResolvedClaudeExecutableInfo,
@@ -37,7 +39,7 @@ import {
37
39
  resolvePassthrough,
38
40
  resolveSdkModelDefaults,
39
41
  stripExtendedContext
40
- } from "./cli-aybvv9nb.js";
42
+ } from "./cli-vjeftz4z.js";
41
43
  import {
42
44
  getSetting
43
45
  } from "./cli-340h1chz.js";
@@ -1532,7 +1534,7 @@ function getExplicitThinking(adapterName) {
1532
1534
  return typeof explicit === "string" && VALID_THINKING_VALUES.has(explicit) ? explicit : undefined;
1533
1535
  }
1534
1536
  function getAllFeatureConfigs() {
1535
- const adapters = ["opencode", "crush", "forgecode", "pi", "droid", "passthrough", "openai"];
1537
+ const adapters = ["opencode", "crush", "forgecode", "pi", "droid", "passthrough", "openai", "codex"];
1536
1538
  const result = {};
1537
1539
  for (const name of adapters) {
1538
1540
  result[name] = getFeaturesForAdapter(name);
@@ -1610,6 +1612,9 @@ var init_sdkFeatures = __esm(() => {
1610
1612
  },
1611
1613
  cherry: {
1612
1614
  codeSystemPrompt: false
1615
+ },
1616
+ codex: {
1617
+ codeSystemPrompt: false
1613
1618
  }
1614
1619
  };
1615
1620
  VALID_CLAUDE_MD_VALUES = new Set(["off", "project", "full"]);
@@ -10179,6 +10184,11 @@ function isStaleSessionError(error) {
10179
10184
  const msg = error.message;
10180
10185
  return msg.includes("No message found with message.uuid") || msg.includes("No conversation found with session ID") || msg.includes("No conversation found to continue") || msg.includes("No conversations found to resume");
10181
10186
  }
10187
+ function isBusySessionError(error, stderr) {
10188
+ const needle = "is currently running as a background agent";
10189
+ const msg = error instanceof Error ? error.message : String(error);
10190
+ return msg.includes(needle) || (stderr?.includes(needle) ?? false);
10191
+ }
10182
10192
  function isRateLimitError(errMsg) {
10183
10193
  const lower = errMsg.toLowerCase();
10184
10194
  return lower.includes("429") || lower.includes("rate limit") || lower.includes("too many requests");
@@ -10459,7 +10469,7 @@ ${historyBlock}` : historyBlock;
10459
10469
  messagesToSend = turns.slice(-1);
10460
10470
  }
10461
10471
  const result = {
10462
- model: body.model ?? "claude-sonnet-4-6",
10472
+ model: body.model ?? CANONICAL_SONNET_MODEL,
10463
10473
  messages: messagesToSend,
10464
10474
  max_tokens: body.max_tokens ?? body.max_completion_tokens ?? 8192,
10465
10475
  tools,
@@ -10665,6 +10675,15 @@ var FULL_CAPABILITIES = Object.freeze({
10665
10675
  });
10666
10676
  function buildModelList(isMaxSubscription, now = Math.floor(Date.now() / 1000)) {
10667
10677
  return [
10678
+ {
10679
+ id: "claude-sonnet-5",
10680
+ object: "model",
10681
+ created: now,
10682
+ owned_by: "anthropic",
10683
+ display_name: "Claude Sonnet 5",
10684
+ context_window: 200000,
10685
+ capabilities: FULL_CAPABILITIES
10686
+ },
10668
10687
  {
10669
10688
  id: "claude-sonnet-4-6",
10670
10689
  object: "model",
@@ -10722,6 +10741,331 @@ function buildModelList(isMaxSubscription, now = Math.floor(Date.now() / 1000))
10722
10741
  ];
10723
10742
  }
10724
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
+
10725
11069
  // src/proxy/messages.ts
10726
11070
  function stripCacheControlForHashing(obj) {
10727
11071
  if (!obj || typeof obj !== "object")
@@ -10780,6 +11124,27 @@ function getLastUserMessage(messages) {
10780
11124
  }
10781
11125
  return messages.slice(-1);
10782
11126
  }
11127
+ function frameReplayTurns(turns) {
11128
+ const nonEmpty = turns.filter((t) => t.text);
11129
+ const joined = nonEmpty.map((t) => t.text).join(`
11130
+
11131
+ `);
11132
+ if (nonEmpty.length < 2)
11133
+ return joined;
11134
+ const last = nonEmpty[nonEmpty.length - 1];
11135
+ if (last.role !== "user")
11136
+ return joined;
11137
+ const history = nonEmpty.slice(0, -1).map((t) => t.text).join(`
11138
+
11139
+ `);
11140
+ return `<conversation_history>
11141
+ ${history}
11142
+ </conversation_history>
11143
+
11144
+ ` + `The above is a replay of your prior conversation with this user — the original session could not be resumed. ` + `It is context only: do not continue or imitate its transcript format, do not write "[Assistant: ...]" markers, ` + `and never invent tool output — use your actual tools when action is needed. ` + `Respond only as the assistant to the user's message below.
11145
+
11146
+ ` + last.text;
11147
+ }
10783
11148
  function stripNonStandardStreamFields(event) {
10784
11149
  if (event && typeof event === "object") {
10785
11150
  const e = event;
@@ -11099,7 +11464,7 @@ init_env();
11099
11464
  var openCodeTransforms = [
11100
11465
  {
11101
11466
  name: "opencode-core",
11102
- adapters: ["opencode", "openai"],
11467
+ adapters: ["opencode", "openai", "codex"],
11103
11468
  onRequest(ctx) {
11104
11469
  const body = ctx.body;
11105
11470
  const blockedTools = BLOCKED_BUILTIN_TOOLS;
@@ -11903,6 +12268,15 @@ var openAiAdapter = {
11903
12268
  name: "openai"
11904
12269
  };
11905
12270
 
12271
+ // src/proxy/adapters/codex.ts
12272
+ var codexAdapter = {
12273
+ ...openCodeAdapter,
12274
+ name: "codex",
12275
+ usesPassthrough() {
12276
+ return true;
12277
+ }
12278
+ };
12279
+
11906
12280
  // src/proxy/adapters/cherry.ts
11907
12281
  var CHERRY_WEB_TOOLS = ["WebSearch", "WebFetch"];
11908
12282
  var isWebTool = (t) => CHERRY_WEB_TOOLS.includes(t);
@@ -12024,7 +12398,8 @@ var ADAPTER_MAP = {
12024
12398
  claudecode: claudeCodeAdapter,
12025
12399
  cherry: cherryAdapter,
12026
12400
  cherrystudio: cherryAdapter,
12027
- openai: openAiAdapter
12401
+ openai: openAiAdapter,
12402
+ codex: codexAdapter
12028
12403
  };
12029
12404
  var envDefault = process.env.MERIDIAN_DEFAULT_AGENT || "";
12030
12405
  if (envDefault && !ADAPTER_MAP[envDefault]) {
@@ -17852,6 +18227,7 @@ function buildQueryOptions(ctx, abortController) {
17852
18227
  resumeSessionId,
17853
18228
  isUndo,
17854
18229
  undoRollbackUuid,
18230
+ forkSession,
17855
18231
  sdkHooks,
17856
18232
  blockedTools,
17857
18233
  incompatibleTools,
@@ -17891,7 +18267,6 @@ function buildQueryOptions(ctx, abortController) {
17891
18267
  ...resolveSystemPrompt(systemContext, passthrough, settingSources, codeSystemPrompt, clientSystemPrompt, cwdNote),
17892
18268
  ...passthrough ? {
17893
18269
  tools: [],
17894
- settingSources: [],
17895
18270
  disallowedTools: [...allBlockedTools],
17896
18271
  ...passthroughMcp ? {
17897
18272
  allowedTools: [...passthroughMcp.toolNames],
@@ -17903,13 +18278,11 @@ function buildQueryOptions(ctx, abortController) {
17903
18278
  mcpServers: { [mcpServerName]: createOpencodeMcpServer() }
17904
18279
  },
17905
18280
  plugins: [],
17906
- ...settingSources && settingSources.length > 0 ? {
17907
- settingSources,
17908
- settings: {
17909
- autoMemoryEnabled: ctx.memory ?? true,
17910
- autoDreamEnabled: ctx.dreaming ?? false
17911
- }
17912
- } : {},
18281
+ settings: {
18282
+ autoMemoryEnabled: ctx.memory ?? true,
18283
+ autoDreamEnabled: ctx.dreaming ?? false
18284
+ },
18285
+ settingSources: settingSources ?? [],
17913
18286
  ...onStderr ? { stderr: onStderr } : {},
17914
18287
  env: {
17915
18288
  ...sharedMemory ? stripConfigDir(cleanEnv) : cleanEnv,
@@ -17921,7 +18294,8 @@ function buildQueryOptions(ctx, abortController) {
17921
18294
  },
17922
18295
  ...Object.keys(sdkAgents).length > 0 ? { agents: sdkAgents } : {},
17923
18296
  ...resumeSessionId ? { resume: resumeSessionId } : {},
17924
- ...isUndo ? { forkSession: true, ...undoRollbackUuid ? { resumeSessionAt: undoRollbackUuid } : {} } : {},
18297
+ ...isUndo || forkSession ? { forkSession: true } : {},
18298
+ ...isUndo && undoRollbackUuid ? { resumeSessionAt: undoRollbackUuid } : {},
17925
18299
  ...sdkHooks ? { hooks: sdkHooks } : {},
17926
18300
  ...effort ? { effort } : {},
17927
18301
  ...thinking ? { thinking } : {},
@@ -17998,6 +18372,17 @@ var cherryTransforms = [
17998
18372
  }
17999
18373
  ];
18000
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
+
18001
18386
  // src/proxy/transforms/registry.ts
18002
18387
  var ADAPTER_TRANSFORMS = {
18003
18388
  opencode: openCodeTransforms,
@@ -18007,7 +18392,8 @@ var ADAPTER_TRANSFORMS = {
18007
18392
  forgecode: forgeCodeTransforms,
18008
18393
  passthrough: passthroughTransforms,
18009
18394
  cherry: cherryTransforms,
18010
- openai: openCodeTransforms
18395
+ openai: openCodeTransforms,
18396
+ codex: [...openCodeTransforms, ...codexTransforms]
18011
18397
  };
18012
18398
  function getAdapterTransforms(adapterName) {
18013
18399
  return ADAPTER_TRANSFORMS[adapterName] ?? [];
@@ -19019,15 +19405,13 @@ function buildFreshPrompt(messages, sanitizeOpts = {}) {
19019
19405
  yield msg;
19020
19406
  }();
19021
19407
  }
19022
- return messages.map((m) => {
19408
+ return frameReplayTurns(messages.map((m) => {
19023
19409
  if (m.role === "assistant") {
19024
19410
  const assistantText = flattenAssistantContent(m.content);
19025
- return assistantText ? `[Assistant: ${assistantText}]` : "";
19411
+ return { role: "assistant", text: assistantText ? `[Assistant: ${assistantText}]` : "" };
19026
19412
  }
19027
- return flattenUserContent(m.content, sanitizeOpts, toolIndex);
19028
- }).filter(Boolean).join(`
19029
-
19030
- `) || "";
19413
+ return { role: "user", text: flattenUserContent(m.content, sanitizeOpts, toolIndex) };
19414
+ }));
19031
19415
  }
19032
19416
  var proxyLogSilent = false;
19033
19417
  function plog(message) {
@@ -19090,6 +19474,8 @@ function createProxyServer(config = {}) {
19090
19474
  const sessionMcpCache = new LRUMap(getMaxSessionsLimit());
19091
19475
  const PENDING_STORE_WAIT_MS = 3000;
19092
19476
  const PENDING_STORE_AUTO_RESOLVE_MS = 1e4;
19477
+ const BUSY_SESSION_MAX_RETRIES = 3;
19478
+ const BUSY_SESSION_RETRY_DELAY_MS = parseInt(process.env.MERIDIAN_BUSY_RETRY_DELAY_MS ?? "500", 10);
19093
19479
  const pendingSessionStores = new Map;
19094
19480
  const registerPendingStore = (key) => {
19095
19481
  let resolveFn = () => {};
@@ -19133,7 +19519,7 @@ function createProxyServer(config = {}) {
19133
19519
  status: "ok",
19134
19520
  service: "meridian",
19135
19521
  format: "anthropic",
19136
- 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"]
19137
19523
  });
19138
19524
  }
19139
19525
  return c.html(landingHtml);
@@ -19198,7 +19584,7 @@ function createProxyServer(config = {}) {
19198
19584
  const requestSource = c.req.header("x-meridian-source")?.slice(0, 64) || undefined;
19199
19585
  const requestedModel = typeof body.model === "string" ? body.model : "sonnet";
19200
19586
  let model = mapModelToClaudeModel(requestedModel, authStatus?.subscriptionType, agentMode);
19201
- const envOverrides = requestedModel.startsWith("claude-opus-") ? { ANTHROPIC_DEFAULT_OPUS_MODEL: requestedModel } : requestedModel.startsWith("claude-fable-") ? { ANTHROPIC_DEFAULT_FABLE_MODEL: requestedModel } : requestedModel.startsWith("claude-mythos-") ? { ANTHROPIC_DEFAULT_FABLE_MODEL: requestedModel } : requestedModel.startsWith("claude-sonnet-") ? { ANTHROPIC_DEFAULT_SONNET_MODEL: requestedModel } : requestedModel.startsWith("claude-haiku-") ? { ANTHROPIC_DEFAULT_HAIKU_MODEL: requestedModel } : undefined;
19587
+ const envOverrides = explicitModelPin(requestedModel);
19202
19588
  const cwdResolution = resolveSdkWorkingDirectory({
19203
19589
  envOverride: process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR,
19204
19590
  adapterCwd: adapter.extractWorkingDirectory(body),
@@ -19407,17 +19793,18 @@ function createProxyServer(config = {}) {
19407
19793
  }
19408
19794
  } else {
19409
19795
  const toolIndex = buildToolUseIndex(allMessages ?? messagesToConvert ?? []);
19410
- textPrompt = messagesToConvert?.map((m) => {
19796
+ const promptTurns = (messagesToConvert ?? []).map((m) => {
19411
19797
  if (m.role === "assistant") {
19412
19798
  if (isResume)
19413
- return "";
19799
+ return { role: "assistant", text: "" };
19414
19800
  const assistantText = flattenAssistantContent(m.content);
19415
- return assistantText ? `[Assistant: ${assistantText}]` : "";
19801
+ return { role: "assistant", text: assistantText ? `[Assistant: ${assistantText}]` : "" };
19416
19802
  }
19417
- return flattenUserContent(m.content, sanitizeOpts, toolIndex);
19418
- }).filter(Boolean).join(`
19803
+ return { role: "user", text: flattenUserContent(m.content, sanitizeOpts, toolIndex) };
19804
+ });
19805
+ textPrompt = isResume ? promptTurns.map((t) => t.text).filter(Boolean).join(`
19419
19806
 
19420
- `) || "";
19807
+ `) || "" : frameReplayTurns(promptTurns);
19421
19808
  }
19422
19809
  const passthrough = adapter.instancePassthrough !== undefined ? adapter.instancePassthrough : pipelineCtx.passthrough !== undefined ? pipelineCtx.passthrough : envBool("PASSTHROUGH");
19423
19810
  const settingSources = envBool("LOAD_CONTEXT") || sdkFeatures.claudeMd === "full" ? ["user", "project"] : sdkFeatures.claudeMd === "project" ? ["project"] : pipelineCtx.settingSources ?? [];
@@ -19543,7 +19930,7 @@ function createProxyServer(config = {}) {
19543
19930
  input: toolInput
19544
19931
  });
19545
19932
  }
19546
- if (stream2 && earlyStopEnabled && turnGenerating && !requestAbort.controller.signal.aborted) {
19933
+ if (earlyStopEnabled && turnGenerating && !requestAbort.controller.signal.aborted) {
19547
19934
  await holdDenyUntilTurnEnd();
19548
19935
  }
19549
19936
  if (isExactDuplicate) {
@@ -19600,8 +19987,12 @@ function createProxyServer(config = {}) {
19600
19987
  }
19601
19988
  let tokenRefreshed = false;
19602
19989
  let didFreshBaseRetry = false;
19990
+ let busySessionRetries = 0;
19991
+ let busySessionFork = false;
19603
19992
  while (true) {
19604
19993
  let didYieldContent = false;
19994
+ const attemptStderrStart = stderrLines.length;
19995
+ turnGenerating = true;
19605
19996
  try {
19606
19997
  for await (const event of query(buildQueryOptions({
19607
19998
  prompt: makePrompt(),
@@ -19620,6 +20011,7 @@ function createProxyServer(config = {}) {
19620
20011
  resumeSessionId,
19621
20012
  isUndo,
19622
20013
  undoRollbackUuid,
20014
+ forkSession: busySessionFork || undefined,
19623
20015
  sdkHooks,
19624
20016
  blockedTools: pipelineCtx.blockedTools,
19625
20017
  incompatibleTools: pipelineCtx.incompatibleTools,
@@ -19654,8 +20046,25 @@ function createProxyServer(config = {}) {
19654
20046
  return;
19655
20047
  } catch (error) {
19656
20048
  const errMsg = error instanceof Error ? error.message : String(error);
20049
+ releaseHeldDenies("non_stream_attempt_error");
19657
20050
  if (didYieldContent)
19658
20051
  throw error;
20052
+ if (resumeSessionId && isBusySessionError(error, stderrLines.slice(attemptStderrStart).join(`
20053
+ `))) {
20054
+ if (busySessionRetries < BUSY_SESSION_MAX_RETRIES) {
20055
+ busySessionRetries++;
20056
+ claudeLog("session.busy_retry", { mode: "non_stream", attempt: busySessionRetries, resumeSessionId });
20057
+ plog(`[PROXY] ${requestMeta.requestId} session busy (bg agent), retrying resume ${busySessionRetries}/${BUSY_SESSION_MAX_RETRIES}`);
20058
+ await new Promise((resolve3) => setTimeout(resolve3, BUSY_SESSION_RETRY_DELAY_MS * busySessionRetries));
20059
+ continue;
20060
+ }
20061
+ if (!busySessionFork) {
20062
+ busySessionFork = true;
20063
+ claudeLog("session.busy_fork", { mode: "non_stream", resumeSessionId });
20064
+ plog(`[PROXY] ${requestMeta.requestId} session still busy after ${BUSY_SESSION_MAX_RETRIES} retries — forking session`);
20065
+ continue;
20066
+ }
20067
+ }
19659
20068
  if (isStaleSessionError(error)) {
19660
20069
  claudeLog("session.stale_uuid_retry", {
19661
20070
  mode: "non_stream",
@@ -19839,6 +20248,7 @@ function createProxyServer(config = {}) {
19839
20248
  }
19840
20249
  }
19841
20250
  if (message.type === "assistant") {
20251
+ releaseHeldDenies("assistant_message");
19842
20252
  assistantMessages += 1;
19843
20253
  if (message.uuid) {
19844
20254
  sdkUuidMap.push(message.uuid);
@@ -19899,6 +20309,7 @@ function createProxyServer(config = {}) {
19899
20309
  }
19900
20310
  }
19901
20311
  }
20312
+ releaseHeldDenies("non_stream_loop_exit");
19902
20313
  claudeLog("upstream.completed", {
19903
20314
  mode: "non_stream",
19904
20315
  model,
@@ -19918,6 +20329,7 @@ function createProxyServer(config = {}) {
19918
20329
  plog(`[PROXY] ${requestMeta.requestId} discovered=${discoveredTools.size} (${newNames}) session_total=${allNames.length}`);
19919
20330
  }
19920
20331
  } catch (error) {
20332
+ releaseHeldDenies("non_stream_error");
19921
20333
  const stderrOutput = stderrLines.join(`
19922
20334
  `).trim();
19923
20335
  if (stderrOutput && error instanceof Error && !error.message.includes(stderrOutput)) {
@@ -20158,8 +20570,11 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
20158
20570
  }
20159
20571
  let tokenRefreshed = false;
20160
20572
  let didFreshBaseRetry = false;
20573
+ let busySessionRetries = 0;
20574
+ let busySessionFork = false;
20161
20575
  while (true) {
20162
20576
  let didYieldClientEvent = false;
20577
+ const attemptStderrStart = stderrLines.length;
20163
20578
  try {
20164
20579
  for await (const event of query(buildQueryOptions({
20165
20580
  prompt: makePrompt(),
@@ -20178,6 +20593,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
20178
20593
  resumeSessionId,
20179
20594
  isUndo,
20180
20595
  undoRollbackUuid,
20596
+ forkSession: busySessionFork || undefined,
20181
20597
  sdkHooks,
20182
20598
  blockedTools: pipelineCtx.blockedTools,
20183
20599
  incompatibleTools: pipelineCtx.incompatibleTools,
@@ -20214,6 +20630,22 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
20214
20630
  const errMsg = error instanceof Error ? error.message : String(error);
20215
20631
  if (didYieldClientEvent)
20216
20632
  throw error;
20633
+ if (resumeSessionId && isBusySessionError(error, stderrLines.slice(attemptStderrStart).join(`
20634
+ `))) {
20635
+ if (busySessionRetries < BUSY_SESSION_MAX_RETRIES) {
20636
+ busySessionRetries++;
20637
+ claudeLog("session.busy_retry", { mode: "stream", attempt: busySessionRetries, resumeSessionId });
20638
+ plog(`[PROXY] ${requestMeta.requestId} session busy (bg agent), retrying resume ${busySessionRetries}/${BUSY_SESSION_MAX_RETRIES}`);
20639
+ await new Promise((resolve3) => setTimeout(resolve3, BUSY_SESSION_RETRY_DELAY_MS * busySessionRetries));
20640
+ continue;
20641
+ }
20642
+ if (!busySessionFork) {
20643
+ busySessionFork = true;
20644
+ claudeLog("session.busy_fork", { mode: "stream", resumeSessionId });
20645
+ plog(`[PROXY] ${requestMeta.requestId} session still busy after ${BUSY_SESSION_MAX_RETRIES} retries — forking session`);
20646
+ continue;
20647
+ }
20648
+ }
20217
20649
  if (isStaleSessionError(error)) {
20218
20650
  claudeLog("session.stale_uuid_retry", {
20219
20651
  mode: "stream",
@@ -21373,7 +21805,7 @@ data: ${JSON.stringify({
21373
21805
  }
21374
21806
  const completionId = `chatcmpl-${randomUUID()}`;
21375
21807
  const created = Math.floor(Date.now() / 1000);
21376
- const model = typeof rawBody.model === "string" && rawBody.model ? rawBody.model : "claude-sonnet-4-6";
21808
+ const model = typeof rawBody.model === "string" && rawBody.model ? rawBody.model : CANONICAL_SONNET_MODEL;
21377
21809
  const { getFeaturesForAdapter: getFeaturesForAdapter2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
21378
21810
  const sdkFeatures = getFeaturesForAdapter2("openai");
21379
21811
  if (!anthropicBody.stream) {
@@ -21452,6 +21884,107 @@ data: ${JSON.stringify({
21452
21884
  }
21453
21885
  });
21454
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
+ });
21455
21988
  app.get("/v1/models", async (c) => {
21456
21989
  const authStatus = await getClaudeAuthStatusAsync();
21457
21990
  const isMax = authStatus?.subscriptionType === "max";