@rynfar/meridian 1.45.0 → 1.45.1

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.
@@ -3818,6 +3818,33 @@ async function* guardUpstreamIdle(source, idleMs, onStall, clock = realClock) {
3818
3818
  }
3819
3819
  }
3820
3820
 
3821
+ // src/proxy/requestAbort.ts
3822
+ function linkRequestAbort(signal) {
3823
+ const controller = new AbortController;
3824
+ let attached = false;
3825
+ const abort = (reason) => {
3826
+ if (!controller.signal.aborted)
3827
+ controller.abort(reason);
3828
+ };
3829
+ const forwardAbort = () => abort(signal.reason);
3830
+ if (signal.aborted) {
3831
+ forwardAbort();
3832
+ } else {
3833
+ signal.addEventListener("abort", forwardAbort, { once: true });
3834
+ attached = true;
3835
+ }
3836
+ return {
3837
+ controller,
3838
+ abort,
3839
+ detach: () => {
3840
+ if (!attached)
3841
+ return;
3842
+ signal.removeEventListener("abort", forwardAbort);
3843
+ attached = false;
3844
+ }
3845
+ };
3846
+ }
3847
+
3821
3848
  // src/proxy/oauthUsage.ts
3822
3849
  var OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
3823
3850
  var OAUTH_BETA_HEADER = "oauth-2025-04-20";
@@ -8111,6 +8138,9 @@ function stableStringify(value) {
8111
8138
  const parts = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`);
8112
8139
  return `{${parts.join(",")}}`;
8113
8140
  }
8141
+ function toolUseSignature(name, input) {
8142
+ return `${name}::${stableStringify(input ?? null)}`;
8143
+ }
8114
8144
  function stripMcpPrefix(toolName) {
8115
8145
  if (toolName.startsWith(PASSTHROUGH_MCP_PREFIX)) {
8116
8146
  return toolName.slice(PASSTHROUGH_MCP_PREFIX.length);
@@ -9987,7 +10017,6 @@ var BLOCKED_BUILTIN_TOOLS = [
9987
10017
  "Read",
9988
10018
  "Write",
9989
10019
  "Edit",
9990
- "MultiEdit",
9991
10020
  "Bash",
9992
10021
  "Glob",
9993
10022
  "Grep",
@@ -16563,7 +16592,9 @@ function stripConfigDir(env2) {
16563
16592
  }
16564
16593
  function computePassthroughMaxTurns(resumeSessionId, hasDeferredTools, advisorModel) {
16565
16594
  const hasResume = !!resumeSessionId;
16566
- const base = hasResume && hasDeferredTools ? 4 : 3;
16595
+ const defaultBase = hasResume && hasDeferredTools ? 4 : 3;
16596
+ const configured = envInt("PASSTHROUGH_MAX_TURNS", defaultBase);
16597
+ const base = configured > 0 ? configured : defaultBase;
16567
16598
  const advisorBump = advisorModel ? 3 : 0;
16568
16599
  return base + advisorBump;
16569
16600
  }
@@ -16594,7 +16625,7 @@ function resolveSystemPrompt(systemContext, passthrough, settingSources, codeSys
16594
16625
  return { systemPrompt: "" };
16595
16626
  return {};
16596
16627
  }
16597
- function buildQueryOptions(ctx) {
16628
+ function buildQueryOptions(ctx, abortController) {
16598
16629
  const {
16599
16630
  prompt,
16600
16631
  model,
@@ -16620,6 +16651,7 @@ function buildQueryOptions(ctx) {
16620
16651
  effort,
16621
16652
  thinking,
16622
16653
  taskBudget,
16654
+ outputFormat,
16623
16655
  betas,
16624
16656
  settingSources,
16625
16657
  codeSystemPrompt,
@@ -16642,6 +16674,7 @@ function buildQueryOptions(ctx) {
16642
16674
  cwd: workingDirectory,
16643
16675
  model,
16644
16676
  pathToClaudeCodeExecutable: claudeExecutable,
16677
+ ...abortController ? { abortController } : {},
16645
16678
  ...stream2 ? { includePartialMessages: true } : {},
16646
16679
  permissionMode: "bypassPermissions",
16647
16680
  allowDangerouslySkipPermissions: true,
@@ -16682,6 +16715,7 @@ function buildQueryOptions(ctx) {
16682
16715
  ...effort ? { effort } : {},
16683
16716
  ...thinking ? { thinking } : {},
16684
16717
  ...taskBudget ? { taskBudget } : {},
16718
+ ...outputFormat ? { outputFormat } : {},
16685
16719
  ...betas && betas.length > 0 ? { betas } : {},
16686
16720
  ...maxBudgetUsd && maxBudgetUsd > 0 ? { maxBudgetUsd } : {},
16687
16721
  ...fallbackModel ? { fallbackModel } : {},
@@ -16698,6 +16732,40 @@ function normalizeEffort(value) {
16698
16732
  return typeof value === "string" && VALID_EFFORTS.includes(value) ? value : undefined;
16699
16733
  }
16700
16734
 
16735
+ // src/proxy/structuredOutput.ts
16736
+ function isRecord(value) {
16737
+ return value !== null && typeof value === "object" && !Array.isArray(value);
16738
+ }
16739
+ function parseOutputFormat(outputConfig, tools) {
16740
+ if (outputConfig === undefined)
16741
+ return { ok: true, value: undefined };
16742
+ if (!isRecord(outputConfig)) {
16743
+ return { ok: false, message: "output_config: Expected an object" };
16744
+ }
16745
+ const format = outputConfig.format;
16746
+ if (format === undefined)
16747
+ return { ok: true, value: undefined };
16748
+ if (!isRecord(format)) {
16749
+ return { ok: false, message: "output_config.format: Expected an object" };
16750
+ }
16751
+ if (format.type !== "json_schema") {
16752
+ return { ok: false, message: "output_config.format.type: Only 'json_schema' is supported" };
16753
+ }
16754
+ if (!isRecord(format.schema)) {
16755
+ return { ok: false, message: "output_config.format.schema: Expected a JSON Schema object" };
16756
+ }
16757
+ if (Array.isArray(tools) && tools.length > 0) {
16758
+ return { ok: false, message: "output_config.format: Cannot be combined with tools" };
16759
+ }
16760
+ return {
16761
+ ok: true,
16762
+ value: { type: "json_schema", schema: format.schema }
16763
+ };
16764
+ }
16765
+ function structuredOutputText(value) {
16766
+ return JSON.stringify(value);
16767
+ }
16768
+
16701
16769
  // src/proxy/transforms/registry.ts
16702
16770
  var ADAPTER_TRANSFORMS = {
16703
16771
  opencode: openCodeTransforms,
@@ -17829,6 +17897,8 @@ function createProxyServer(config = {}) {
17829
17897
  }
17830
17898
  const handleMessages = async (c, requestMeta) => {
17831
17899
  const requestStartAt = Date.now();
17900
+ const requestAbort = linkRequestAbort(c.req.raw.signal);
17901
+ let streamOwnsAbortLink = false;
17832
17902
  return withClaudeLogContext({ requestId: requestMeta.requestId, endpoint: requestMeta.endpoint }, async () => {
17833
17903
  const adapter = detectAdapter(c);
17834
17904
  try {
@@ -17849,13 +17919,18 @@ function createProxyServer(config = {}) {
17849
17919
  if (body.messages.length === 0) {
17850
17920
  return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Cannot be empty — at least one message is required" } }, 400);
17851
17921
  }
17922
+ const parsedOutputFormat = parseOutputFormat(body.output_config, body.tools);
17923
+ if (!parsedOutputFormat.ok) {
17924
+ return c.json({ type: "error", error: { type: "invalid_request_error", message: parsedOutputFormat.message } }, 400);
17925
+ }
17926
+ const outputFormat = parsedOutputFormat.value;
17852
17927
  const profile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile, c.req.header("x-meridian-profile") || undefined);
17853
17928
  const authStatus = await getClaudeAuthStatusAsync(profile.id !== "default" ? profile.id : undefined, Object.keys(profile.env).length > 0 ? profile.env : undefined);
17854
17929
  const agentMode = c.req.header("x-opencode-agent-mode") ?? null;
17855
17930
  const requestSource = c.req.header("x-meridian-source")?.slice(0, 64) || undefined;
17856
17931
  const requestedModel = typeof body.model === "string" ? body.model : "sonnet";
17857
17932
  let model = mapModelToClaudeModel(requestedModel, authStatus?.subscriptionType, agentMode);
17858
- const envOverrides = requestedModel.startsWith("claude-opus-") ? { ANTHROPIC_DEFAULT_OPUS_MODEL: requestedModel } : requestedModel.startsWith("claude-fable-") ? { ANTHROPIC_DEFAULT_FABLE_MODEL: requestedModel } : undefined;
17933
+ const envOverrides = requestedModel.startsWith("claude-opus-") ? { ANTHROPIC_DEFAULT_OPUS_MODEL: requestedModel } : requestedModel.startsWith("claude-fable-") ? { ANTHROPIC_DEFAULT_FABLE_MODEL: requestedModel } : requestedModel.startsWith("claude-sonnet-") ? { ANTHROPIC_DEFAULT_SONNET_MODEL: requestedModel } : requestedModel.startsWith("claude-haiku-") ? { ANTHROPIC_DEFAULT_HAIKU_MODEL: requestedModel } : undefined;
17859
17934
  const cwdResolution = resolveSdkWorkingDirectory({
17860
17935
  envOverride: process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR,
17861
17936
  adapterCwd: adapter.extractWorkingDirectory(body),
@@ -17946,7 +18021,10 @@ function createProxyServer(config = {}) {
17946
18021
  const agentSessionId = adapter.getSessionId(c);
17947
18022
  const profileSessionId = profile.id !== "default" && agentSessionId ? `${profile.id}:${agentSessionId}` : agentSessionId;
17948
18023
  const profileScopedCwd = profile.id !== "default" ? `${clientWorkingDirectory}::profile=${profile.id}` : clientWorkingDirectory;
17949
- const isIndependentSession = requestSource?.startsWith("fork-") || requestSource?.startsWith("subagent-") || false;
18024
+ const lastMessage = Array.isArray(body.messages) ? body.messages[body.messages.length - 1] : undefined;
18025
+ const lastIsToolResult = Array.isArray(lastMessage?.content) && lastMessage.content.some((b) => b?.type === "tool_result");
18026
+ const isClientDrivenLoop = !agentSessionId && lastIsToolResult;
18027
+ const isIndependentSession = requestSource?.startsWith("fork-") || requestSource?.startsWith("subagent-") || isClientDrivenLoop || false;
17950
18028
  let lineageResult = isIndependentSession ? { type: "diverged" } : lookupSession(profileSessionId, body.messages || [], profileScopedCwd);
17951
18029
  if (lineageResult.type === "undo" && adapter.name === "opencode" && !agentSessionId) {
17952
18030
  lineageResult = { type: "diverged" };
@@ -18056,6 +18134,11 @@ function createProxyServer(config = {}) {
18056
18134
  const passthrough = pipelineCtx.passthrough !== undefined ? pipelineCtx.passthrough : envBool("PASSTHROUGH");
18057
18135
  const settingSources = envBool("LOAD_CONTEXT") || sdkFeatures.claudeMd === "full" ? ["user", "project"] : sdkFeatures.claudeMd === "project" ? ["project"] : pipelineCtx.settingSources ?? [];
18058
18136
  const capturedToolUses = [];
18137
+ const capturedSignatures = new Set;
18138
+ const capturedToolNames = new Set;
18139
+ let sawDuplicateToolUse = false;
18140
+ const toolChoice = body.tool_choice;
18141
+ const forceSingleToolUse = !!toolChoice && (toolChoice.type === "tool" || toolChoice.disable_parallel_tool_use === true);
18059
18142
  const fileChanges = [];
18060
18143
  let passthroughMcp;
18061
18144
  let requestTools = Array.isArray(body.tools) ? body.tools : [];
@@ -18104,6 +18187,8 @@ function createProxyServer(config = {}) {
18104
18187
  hooks: [async (input) => {
18105
18188
  if (input.tool_name === "ToolSearch")
18106
18189
  return {};
18190
+ if (input.tool_name === "StructuredOutput")
18191
+ return {};
18107
18192
  const toolName = stripMcpPrefix(input.tool_name);
18108
18193
  if (hasDeferredTools && coreSet && !coreSet.has(toolName.toLowerCase())) {
18109
18194
  discoveredTools.add(toolName);
@@ -18113,11 +18198,28 @@ function createProxyServer(config = {}) {
18113
18198
  if (toolName.toLowerCase() === "task" && toolInput?.subagent_type && typeof toolInput.subagent_type === "string") {
18114
18199
  toolInput = { ...toolInput, subagent_type: resolveAgentAlias(toolInput.subagent_type) };
18115
18200
  }
18116
- capturedToolUses.push({
18117
- id: input.tool_use_id,
18118
- name: toolName,
18119
- input: toolInput
18120
- });
18201
+ const signature = toolUseSignature(toolName, toolInput);
18202
+ const isExactDuplicate = capturedSignatures.has(signature);
18203
+ const isSameToolRepeat = !isExactDuplicate && capturedToolNames.has(toolName);
18204
+ const exceedsForcedSingle = forceSingleToolUse && capturedToolUses.length >= 1;
18205
+ if (isExactDuplicate) {
18206
+ claudeLog("passthrough.duplicate_tool_use_dropped", { name: toolName });
18207
+ } else if (isSameToolRepeat || exceedsForcedSingle) {
18208
+ sawDuplicateToolUse = true;
18209
+ claudeLog("passthrough.extra_tool_use_dropped", {
18210
+ name: toolName,
18211
+ reason: exceedsForcedSingle ? "forced_single" : "same_tool_repeat"
18212
+ });
18213
+ requestAbort.abort("passthrough single-step complete");
18214
+ } else {
18215
+ capturedSignatures.add(signature);
18216
+ capturedToolNames.add(toolName);
18217
+ capturedToolUses.push({
18218
+ id: input.tool_use_id,
18219
+ name: toolName,
18220
+ input: toolInput
18221
+ });
18222
+ }
18121
18223
  return {
18122
18224
  decision: "block",
18123
18225
  reason: "This tool call has been forwarded to the client for execution. " + "The result will be delivered in a future turn. " + "Do not retry, do not call additional tools, and do not generate further text — end your turn now."
@@ -18136,6 +18238,8 @@ function createProxyServer(config = {}) {
18136
18238
  if (!stream2) {
18137
18239
  const contentBlocks = [];
18138
18240
  let assistantMessages = 0;
18241
+ let hasStructuredOutput = false;
18242
+ let structuredOutput;
18139
18243
  const upstreamStartAt = Date.now();
18140
18244
  let firstChunkAt;
18141
18245
  let currentSessionId;
@@ -18187,6 +18291,7 @@ function createProxyServer(config = {}) {
18187
18291
  effort,
18188
18292
  thinking,
18189
18293
  taskBudget,
18294
+ outputFormat,
18190
18295
  betas,
18191
18296
  settingSources,
18192
18297
  codeSystemPrompt: sdkFeatures.codeSystemPrompt,
@@ -18199,7 +18304,7 @@ function createProxyServer(config = {}) {
18199
18304
  sdkDebug: sdkFeatures.sdkDebug,
18200
18305
  additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined,
18201
18306
  advisorModel
18202
- }))) {
18307
+ }, requestAbort.controller))) {
18203
18308
  if (event.type === "rate_limit_event") {
18204
18309
  rateLimitStore.record(event.rate_limit_info);
18205
18310
  }
@@ -18250,6 +18355,7 @@ function createProxyServer(config = {}) {
18250
18355
  effort,
18251
18356
  thinking,
18252
18357
  taskBudget,
18358
+ outputFormat,
18253
18359
  betas,
18254
18360
  settingSources,
18255
18361
  codeSystemPrompt: sdkFeatures.codeSystemPrompt,
@@ -18262,7 +18368,7 @@ function createProxyServer(config = {}) {
18262
18368
  sdkDebug: sdkFeatures.sdkDebug,
18263
18369
  additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined,
18264
18370
  advisorModel
18265
- }));
18371
+ }, requestAbort.controller));
18266
18372
  return;
18267
18373
  }
18268
18374
  if (isExtraUsageRequiredError(errMsg) && hasExtendedContext(model)) {
@@ -18316,6 +18422,7 @@ function createProxyServer(config = {}) {
18316
18422
  effort,
18317
18423
  thinking,
18318
18424
  taskBudget,
18425
+ outputFormat,
18319
18426
  betas,
18320
18427
  settingSources,
18321
18428
  codeSystemPrompt: sdkFeatures.codeSystemPrompt,
@@ -18328,7 +18435,7 @@ function createProxyServer(config = {}) {
18328
18435
  sdkDebug: sdkFeatures.sdkDebug,
18329
18436
  additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined,
18330
18437
  advisorModel
18331
- }));
18438
+ }, requestAbort.controller));
18332
18439
  return;
18333
18440
  }
18334
18441
  if (isExpiredTokenError(errMsg) && !tokenRefreshed) {
@@ -18376,6 +18483,10 @@ function createProxyServer(config = {}) {
18376
18483
  if (message.session_id) {
18377
18484
  currentSessionId = message.session_id;
18378
18485
  }
18486
+ if (passthrough && sawDuplicateToolUse) {
18487
+ claudeLog("passthrough.loop_break", { mode: "non_stream", assistantMessages, captured: capturedToolUses.length });
18488
+ break;
18489
+ }
18379
18490
  if (message.type === "assistant") {
18380
18491
  assistantMessages += 1;
18381
18492
  if (message.uuid) {
@@ -18421,6 +18532,10 @@ function createProxyServer(config = {}) {
18421
18532
  if (resultUsage) {
18422
18533
  lastUsage = { ...lastUsage, ...resultUsage };
18423
18534
  }
18535
+ if (outputFormat && "structured_output" in message) {
18536
+ hasStructuredOutput = true;
18537
+ structuredOutput = message.structured_output;
18538
+ }
18424
18539
  }
18425
18540
  }
18426
18541
  claudeLog("upstream.completed", {
@@ -18448,14 +18563,40 @@ function createProxyServer(config = {}) {
18448
18563
  error.message = `${error.message}
18449
18564
  Subprocess stderr: ${stderrOutput}`;
18450
18565
  }
18451
- claudeLog("upstream.failed", {
18452
- mode: "non_stream",
18453
- model,
18454
- durationMs: Date.now() - upstreamStartAt,
18455
- error: error instanceof Error ? error.message : String(error),
18456
- ...stderrOutput ? { stderr: stderrOutput } : {}
18566
+ const sdkTerm = extractSdkTermination(error instanceof Error ? error.message : String(error));
18567
+ const canRecoverAsToolUse = passthrough && capturedToolUses.length > 0 && (sdkTerm.reason === "max_turns" || sdkTerm.reason === "aborted");
18568
+ if (canRecoverAsToolUse) {
18569
+ diagnosticLog2.session(`${requestMeta.requestId} sdk_termination_recovered ${formatSdkTermination(sdkTerm, {
18570
+ model,
18571
+ requestSource,
18572
+ isResume,
18573
+ hasDeferredTools,
18574
+ sdkSessionId: resumeSessionId
18575
+ })} captured=${capturedToolUses.length}`, requestMeta.requestId);
18576
+ claudeLog("passthrough.max_turns_recovered", {
18577
+ mode: "non_stream",
18578
+ reason: sdkTerm.reason,
18579
+ captured: capturedToolUses.length
18580
+ });
18581
+ } else {
18582
+ claudeLog("upstream.failed", {
18583
+ mode: "non_stream",
18584
+ model,
18585
+ durationMs: Date.now() - upstreamStartAt,
18586
+ error: error instanceof Error ? error.message : String(error),
18587
+ ...stderrOutput ? { stderr: stderrOutput } : {}
18588
+ });
18589
+ throw error;
18590
+ }
18591
+ }
18592
+ if (outputFormat) {
18593
+ if (!hasStructuredOutput) {
18594
+ throw new Error("Structured output was requested but the SDK returned no structured_output result");
18595
+ }
18596
+ contentBlocks.splice(0, contentBlocks.length, {
18597
+ type: "text",
18598
+ text: structuredOutputText(structuredOutput)
18457
18599
  });
18458
- throw error;
18459
18600
  }
18460
18601
  if (passthrough && capturedToolUses.length > 0) {
18461
18602
  const capturedById = new Map(capturedToolUses.map((tu) => [tu.id, tu]));
@@ -18606,6 +18747,8 @@ Subprocess stderr: ${stderrOutput}`;
18606
18747
  sdkUuidMap.push(null);
18607
18748
  let messageStartEmitted = false;
18608
18749
  let lastUsage;
18750
+ let hasStructuredOutput = false;
18751
+ let structuredOutput;
18609
18752
  const streamedToolUseIds = new Set;
18610
18753
  try {
18611
18754
  let currentSessionId;
@@ -18647,6 +18790,7 @@ Subprocess stderr: ${stderrOutput}`;
18647
18790
  effort,
18648
18791
  thinking,
18649
18792
  taskBudget,
18793
+ outputFormat,
18650
18794
  betas,
18651
18795
  settingSources,
18652
18796
  codeSystemPrompt: sdkFeatures.codeSystemPrompt,
@@ -18659,7 +18803,7 @@ Subprocess stderr: ${stderrOutput}`;
18659
18803
  sdkDebug: sdkFeatures.sdkDebug,
18660
18804
  additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined,
18661
18805
  advisorModel
18662
- }))) {
18806
+ }, requestAbort.controller))) {
18663
18807
  if (event.type === "rate_limit_event") {
18664
18808
  rateLimitStore.record(event.rate_limit_info);
18665
18809
  }
@@ -18710,6 +18854,7 @@ Subprocess stderr: ${stderrOutput}`;
18710
18854
  effort,
18711
18855
  thinking,
18712
18856
  taskBudget,
18857
+ outputFormat,
18713
18858
  betas,
18714
18859
  settingSources,
18715
18860
  codeSystemPrompt: sdkFeatures.codeSystemPrompt,
@@ -18722,7 +18867,7 @@ Subprocess stderr: ${stderrOutput}`;
18722
18867
  sdkDebug: sdkFeatures.sdkDebug,
18723
18868
  additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined,
18724
18869
  advisorModel
18725
- }));
18870
+ }, requestAbort.controller));
18726
18871
  return;
18727
18872
  }
18728
18873
  if (isExtraUsageRequiredError(errMsg) && hasExtendedContext(model)) {
@@ -18776,6 +18921,7 @@ Subprocess stderr: ${stderrOutput}`;
18776
18921
  effort,
18777
18922
  thinking,
18778
18923
  taskBudget,
18924
+ outputFormat,
18779
18925
  betas,
18780
18926
  settingSources,
18781
18927
  codeSystemPrompt: sdkFeatures.codeSystemPrompt,
@@ -18788,7 +18934,7 @@ Subprocess stderr: ${stderrOutput}`;
18788
18934
  sdkDebug: sdkFeatures.sdkDebug,
18789
18935
  additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined,
18790
18936
  advisorModel
18791
- }));
18937
+ }, requestAbort.controller));
18792
18938
  return;
18793
18939
  }
18794
18940
  if (isExpiredTokenError(errMsg) && !tokenRefreshed) {
@@ -18876,6 +19022,15 @@ Subprocess stderr: ${stderrOutput}`;
18876
19022
  if (message.type === "assistant" && message.uuid) {
18877
19023
  sdkUuidMap.push(message.uuid);
18878
19024
  }
19025
+ if (message.type === "result") {
19026
+ const resultUsage = message.usage;
19027
+ if (resultUsage)
19028
+ lastUsage = { ...lastUsage, ...resultUsage };
19029
+ if (outputFormat && "structured_output" in message) {
19030
+ hasStructuredOutput = true;
19031
+ structuredOutput = message.structured_output;
19032
+ }
19033
+ }
18879
19034
  if (message.type === "stream_event") {
18880
19035
  streamEventsSeen += 1;
18881
19036
  if (!firstChunkAt) {
@@ -18889,6 +19044,18 @@ Subprocess stderr: ${stderrOutput}`;
18889
19044
  const event = message.event;
18890
19045
  const eventType = event.type;
18891
19046
  const eventIndex = event.index;
19047
+ if (outputFormat) {
19048
+ if (eventType === "message_start") {
19049
+ const startUsage = event.message?.usage;
19050
+ if (startUsage)
19051
+ lastUsage = { ...lastUsage, ...startUsage };
19052
+ } else if (eventType === "message_delta") {
19053
+ const deltaUsage = event.usage;
19054
+ if (deltaUsage)
19055
+ lastUsage = { ...lastUsage, ...deltaUsage };
19056
+ }
19057
+ continue;
19058
+ }
18892
19059
  if (eventType === "message_start") {
18893
19060
  skipBlockIndices.clear();
18894
19061
  sdkToClientIndex.clear();
@@ -19026,6 +19193,60 @@ data: ${JSON.stringify({ type: "message_stop" })}
19026
19193
  } finally {
19027
19194
  clearInterval(heartbeat);
19028
19195
  }
19196
+ if (outputFormat) {
19197
+ if (!hasStructuredOutput) {
19198
+ throw new Error("Structured output was requested but the SDK returned no structured_output result");
19199
+ }
19200
+ const text = structuredOutputText(structuredOutput);
19201
+ const messageId = `msg_${Date.now()}`;
19202
+ safeEnqueue(encoder.encode(`event: message_start
19203
+ data: ${JSON.stringify({
19204
+ type: "message_start",
19205
+ message: {
19206
+ id: messageId,
19207
+ type: "message",
19208
+ role: "assistant",
19209
+ content: [],
19210
+ model: body.model,
19211
+ stop_reason: null,
19212
+ stop_sequence: null,
19213
+ usage: { input_tokens: lastUsage?.input_tokens ?? 0, output_tokens: 0 }
19214
+ }
19215
+ })}
19216
+
19217
+ `), "structured_message_start");
19218
+ safeEnqueue(encoder.encode(`event: content_block_start
19219
+ data: ${JSON.stringify({
19220
+ type: "content_block_start",
19221
+ index: 0,
19222
+ content_block: { type: "text", text: "" }
19223
+ })}
19224
+
19225
+ `), "structured_block_start");
19226
+ safeEnqueue(encoder.encode(`event: content_block_delta
19227
+ data: ${JSON.stringify({
19228
+ type: "content_block_delta",
19229
+ index: 0,
19230
+ delta: { type: "text_delta", text }
19231
+ })}
19232
+
19233
+ `), "structured_text_delta");
19234
+ safeEnqueue(encoder.encode(`event: content_block_stop
19235
+ data: ${JSON.stringify({ type: "content_block_stop", index: 0 })}
19236
+
19237
+ `), "structured_block_stop");
19238
+ safeEnqueue(encoder.encode(`event: message_delta
19239
+ data: ${JSON.stringify({
19240
+ type: "message_delta",
19241
+ delta: { stop_reason: "end_turn", stop_sequence: null },
19242
+ usage: { output_tokens: lastUsage?.output_tokens ?? 0 }
19243
+ })}
19244
+
19245
+ `), "structured_message_delta");
19246
+ messageStartEmitted = true;
19247
+ eventsForwarded += 5;
19248
+ textEventsForwarded += 1;
19249
+ }
19029
19250
  claudeLog("upstream.completed", {
19030
19251
  mode: "stream",
19031
19252
  model,
@@ -19230,7 +19451,7 @@ Subprocess stderr: ${stderrOutput}`;
19230
19451
  } : classifyError(errMsg);
19231
19452
  claudeLog("proxy.anthropic.error", { error: errMsg, classified: streamErr.type });
19232
19453
  const sdkTerm = extractSdkTermination(errMsg);
19233
- const canRecoverAsToolUse = sdkTerm.reason === "max_turns" && passthrough && capturedToolUses.length > 0 && messageStartEmitted;
19454
+ const canRecoverAsToolUse = (sdkTerm.reason === "max_turns" || sdkTerm.reason === "aborted" && sawDuplicateToolUse) && passthrough && capturedToolUses.length > 0 && messageStartEmitted;
19234
19455
  if (canRecoverAsToolUse) {
19235
19456
  diagnosticLog2.session(`${requestMeta.requestId} sdk_termination_recovered ${formatSdkTermination(sdkTerm, {
19236
19457
  model,
@@ -19377,10 +19598,17 @@ data: ${JSON.stringify({
19377
19598
  } catch {}
19378
19599
  streamClosed = true;
19379
19600
  }
19601
+ } finally {
19602
+ requestAbort.detach();
19380
19603
  }
19604
+ },
19605
+ cancel(reason) {
19606
+ requestAbort.abort(reason);
19607
+ requestAbort.detach();
19381
19608
  }
19382
19609
  });
19383
19610
  const streamSessionId = resumeSessionId || `session_${Date.now()}`;
19611
+ streamOwnsAbortLink = true;
19384
19612
  return new Response(readable, {
19385
19613
  headers: {
19386
19614
  "Content-Type": "text/event-stream",
@@ -19428,6 +19656,9 @@ data: ${JSON.stringify({
19428
19656
  error: classified.type
19429
19657
  });
19430
19658
  return new Response(JSON.stringify({ type: "error", error: { type: classified.type, message: classified.message } }), { status: classified.status, headers: { "Content-Type": "application/json" } });
19659
+ } finally {
19660
+ if (!streamOwnsAbortLink)
19661
+ requestAbort.detach();
19431
19662
  }
19432
19663
  });
19433
19664
  };
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startProxyServer
4
- } from "./cli-rakysex2.js";
4
+ } from "./cli-9pcv2qnp.js";
5
5
  import"./cli-cx463q74.js";
6
6
  import"./cli-sry5aqdj.js";
7
7
  import"./cli-4rqtm83g.js";
@@ -40,6 +40,18 @@ export declare function computeToolSetKey(tools: Array<{
40
40
  input_schema?: unknown;
41
41
  defer_loading?: boolean;
42
42
  }>): string;
43
+ /**
44
+ * Stable semantic signature for a forwarded tool_use: tool name + a
45
+ * key-order-independent serialization of its input. Two tool calls with the
46
+ * same name and the same arguments share a signature even if the SDK assigns
47
+ * them different tool_use ids.
48
+ *
49
+ * Used by the passthrough capture path to tell apart genuine parallel calls
50
+ * (distinct signatures — e.g. get_weather vs get_time) from an SDK internal
51
+ * continuation turn re-emitting a blocked call (identical signature, new id).
52
+ * The former are all forwarded; the latter is a duplicate and dropped.
53
+ */
54
+ export declare function toolUseSignature(name: string, input: unknown): string;
43
55
  /**
44
56
  * Strip the MCP prefix from a tool name to get the OpenCode tool name.
45
57
  * e.g., "mcp__oc__todowrite" → "todowrite"
@@ -1 +1 @@
1
- {"version":3,"file":"passthroughTools.d.ts","sourceRoot":"","sources":["../../src/proxy/passthroughTools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,eAAO,MAAM,oBAAoB,OAAO,CAAA;AACxC,eAAO,MAAM,sBAAsB,cAAmC,CAAA;AA0CtE,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,GAAG,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EACjG,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE;;;;EA4DlC;AAqBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,GAC9E,MAAM,CASR;AAUD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKvD;AAUD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC1C,YAAY,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,GACtF,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAgCrC"}
1
+ {"version":3,"file":"passthroughTools.d.ts","sourceRoot":"","sources":["../../src/proxy/passthroughTools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,eAAO,MAAM,oBAAoB,OAAO,CAAA;AACxC,eAAO,MAAM,sBAAsB,cAAmC,CAAA;AA0CtE,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,GAAG,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EACjG,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE;;;;EA4DlC;AAqBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,GAC9E,MAAM,CASR;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAErE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKvD;AAUD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC1C,YAAY,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,GACtF,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAgCrC"}
@@ -4,7 +4,7 @@
4
4
  * Centralizes the construction of query() options, eliminating duplication
5
5
  * between the streaming and non-streaming paths in server.ts.
6
6
  */
7
- import type { Options, SettingSource } from "@anthropic-ai/claude-agent-sdk";
7
+ import type { Options, OutputFormat, SettingSource } from "@anthropic-ai/claude-agent-sdk";
8
8
  import { createPassthroughMcpServer } from "./passthroughTools";
9
9
  import type { Effort } from "./effort";
10
10
  export interface QueryContext {
@@ -72,6 +72,8 @@ export interface QueryContext {
72
72
  taskBudget?: {
73
73
  total: number;
74
74
  };
75
+ /** Native JSON-schema output contract for the Claude Agent SDK */
76
+ outputFormat?: OutputFormat;
75
77
  /** Beta features to enable */
76
78
  betas?: string[];
77
79
  /** SDK setting sources — controls CLAUDE.md and user settings loading */
@@ -115,5 +117,5 @@ export interface BuildQueryResult {
115
117
  * reports that as its working directory.
116
118
  */
117
119
  export declare function buildCwdNote(sdkCwd: string, clientCwd?: string): string;
118
- export declare function buildQueryOptions(ctx: QueryContext): BuildQueryResult;
120
+ export declare function buildQueryOptions(ctx: QueryContext, abortController?: AbortController): BuildQueryResult;
119
121
  //# sourceMappingURL=query.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/proxy/query.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAW,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAErF,OAAO,EAAE,0BAA0B,EAAwB,MAAM,oBAAoB,CAAA;AACrF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAsBtC,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;IACnC,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,gBAAgB,EAAE,MAAM,CAAA;IACxB;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,yCAAyC;IACzC,aAAa,EAAE,MAAM,CAAA;IACrB,gCAAgC;IAChC,gBAAgB,EAAE,MAAM,CAAA;IACxB,0CAA0C;IAC1C,WAAW,EAAE,OAAO,CAAA;IACpB,0CAA0C;IAC1C,MAAM,EAAE,OAAO,CAAA;IACf,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC9B,mEAAmE;IACnE,cAAc,CAAC,EAAE,UAAU,CAAC,OAAO,0BAA0B,CAAC,CAAA;IAC9D,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC5C,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IACjD,yDAAyD;IACzD,gBAAgB,EAAE,OAAO,CAAA;IACzB,0DAA0D;IAC1D,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,wCAAwC;IACxC,MAAM,EAAE,OAAO,CAAA;IACf,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,GAAG,CAAA;IACd,iDAAiD;IACjD,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;IAC/B,+CAA+C;IAC/C,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAA;IACpC,uCAAuC;IACvC,aAAa,EAAE,MAAM,CAAA;IACrB,wCAAwC;IACxC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0EAA0E;IAC1E,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,UAAU,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,UAAU,CAAA;KAAE,CAAA;IACnG,8EAA8E;IAC9E,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9B,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,yEAAyE;IACzE,cAAc,CAAC,EAAE,aAAa,EAAE,CAAA;IAChC,+CAA+C;IAC/C,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,+CAA+C;IAC/C,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,wDAAwD;IACxD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,kCAAkC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,wCAAwC;IACxC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,+CAA+C;IAC/C,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAA;IAChC,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAC9B,OAAO,EAAE,OAAO,CAAA;CACjB;AA+BD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAkBvE;AAgCD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,gBAAgB,CA6GrE"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/proxy/query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface RequestAbortLink {
2
+ controller: AbortController;
3
+ abort: (reason?: unknown) => void;
4
+ detach: () => void;
5
+ }
6
+ /** Forward an HTTP request abort into the SDK query lifecycle. */
7
+ export declare function linkRequestAbort(signal: AbortSignal): RequestAbortLink;
8
+ //# sourceMappingURL=requestAbort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requestAbort.d.ts","sourceRoot":"","sources":["../../src/proxy/requestAbort.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,eAAe,CAAA;IAC3B,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACjC,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,gBAAgB,CAyBtE"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAcA,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;AAiCnG,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;AAiQ7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAgsFhF;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;AAkCnG,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;AAiQ7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAk8FhF;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,21 @@
1
+ import type { OutputFormat } from "@anthropic-ai/claude-agent-sdk";
2
+ export type OutputFormatParseResult = {
3
+ ok: true;
4
+ value: OutputFormat | undefined;
5
+ } | {
6
+ ok: false;
7
+ message: string;
8
+ };
9
+ /**
10
+ * Parse Anthropic's `output_config.format` into the Claude Agent SDK's native
11
+ * structured-output option. Absence is a no-op; malformed or unsupported
12
+ * values are rejected at the HTTP boundary instead of failing in the SDK.
13
+ *
14
+ * Combining `tools` with `output_config.format` is rejected: structured-output
15
+ * mode buffers the SDK's wire events and replaces the response content with the
16
+ * validated result, so a tool_use turn would be swallowed and the client-driven
17
+ * tool loop would never see it.
18
+ */
19
+ export declare function parseOutputFormat(outputConfig: unknown, tools?: unknown): OutputFormatParseResult;
20
+ export declare function structuredOutputText(value: unknown): string;
21
+ //# sourceMappingURL=structuredOutput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structuredOutput.d.ts","sourceRoot":"","sources":["../../src/proxy/structuredOutput.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAElE,MAAM,MAAM,uBAAuB,GAC/B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAA;CAAE,GAC7C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAMlC;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,uBAAuB,CAyBjG;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE3D"}
package/dist/server.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  runObserveHook,
12
12
  runTransformHook,
13
13
  startProxyServer
14
- } from "./cli-rakysex2.js";
14
+ } from "./cli-9pcv2qnp.js";
15
15
  import"./cli-cx463q74.js";
16
16
  import"./cli-sry5aqdj.js";
17
17
  import"./cli-4rqtm83g.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynfar/meridian",
3
- "version": "1.45.0",
3
+ "version": "1.45.1",
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",