Files
clawdbot/src/gateway/server-runtime-config.ts
Ryan Lisse f4b03599f0 feat(gateway): add OpenResponses /v1/responses endpoint
Add a new `/v1/responses` endpoint implementing the OpenResponses API
standard for agentic workflows. This provides:

- Item-based input (messages, function_call_output, reasoning)
- Semantic streaming events (response.created, response.output_text.delta,
  response.completed, etc.)
- Full SSE event support with both event: and data: lines
- Configuration via gateway.http.endpoints.responses.enabled

The endpoint is disabled by default and can be enabled independently
from the existing Chat Completions endpoint.

Phase 1 implementation supports:
- String or ItemParam[] input
- system/developer/user/assistant message roles
- function_call_output items
- instructions parameter
- Agent routing via headers or model parameter
- Session key management

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 07:37:01 +00:00

105 lines
3.5 KiB
TypeScript

import type {
GatewayAuthConfig,
GatewayBindMode,
GatewayTailscaleConfig,
loadConfig,
} from "../config/config.js";
import {
assertGatewayAuthConfigured,
type ResolvedGatewayAuth,
resolveGatewayAuth,
} from "./auth.js";
import { normalizeControlUiBasePath } from "./control-ui.js";
import { resolveHooksConfig } from "./hooks.js";
import { isLoopbackHost, resolveGatewayBindHost } from "./net.js";
export type GatewayRuntimeConfig = {
bindHost: string;
controlUiEnabled: boolean;
openAiChatCompletionsEnabled: boolean;
openResponsesEnabled: boolean;
controlUiBasePath: string;
resolvedAuth: ResolvedGatewayAuth;
authMode: ResolvedGatewayAuth["mode"];
tailscaleConfig: GatewayTailscaleConfig;
tailscaleMode: "off" | "serve" | "funnel";
hooksConfig: ReturnType<typeof resolveHooksConfig>;
canvasHostEnabled: boolean;
};
export async function resolveGatewayRuntimeConfig(params: {
cfg: ReturnType<typeof loadConfig>;
port: number;
bind?: GatewayBindMode;
host?: string;
controlUiEnabled?: boolean;
openAiChatCompletionsEnabled?: boolean;
openResponsesEnabled?: boolean;
auth?: GatewayAuthConfig;
tailscale?: GatewayTailscaleConfig;
}): Promise<GatewayRuntimeConfig> {
const bindMode = params.bind ?? params.cfg.gateway?.bind ?? "loopback";
const customBindHost = params.cfg.gateway?.customBindHost;
const bindHost = params.host ?? (await resolveGatewayBindHost(bindMode, customBindHost));
const controlUiEnabled =
params.controlUiEnabled ?? params.cfg.gateway?.controlUi?.enabled ?? true;
const openAiChatCompletionsEnabled =
params.openAiChatCompletionsEnabled ??
params.cfg.gateway?.http?.endpoints?.chatCompletions?.enabled ??
false;
const openResponsesEnabled =
params.openResponsesEnabled ?? params.cfg.gateway?.http?.endpoints?.responses?.enabled ?? false;
const controlUiBasePath = normalizeControlUiBasePath(params.cfg.gateway?.controlUi?.basePath);
const authBase = params.cfg.gateway?.auth ?? {};
const authOverrides = params.auth ?? {};
const authConfig = {
...authBase,
...authOverrides,
};
const tailscaleBase = params.cfg.gateway?.tailscale ?? {};
const tailscaleOverrides = params.tailscale ?? {};
const tailscaleConfig = {
...tailscaleBase,
...tailscaleOverrides,
};
const tailscaleMode = tailscaleConfig.mode ?? "off";
const resolvedAuth = resolveGatewayAuth({
authConfig,
env: process.env,
tailscaleMode,
});
const authMode: ResolvedGatewayAuth["mode"] = resolvedAuth.mode;
const hooksConfig = resolveHooksConfig(params.cfg);
const canvasHostEnabled =
process.env.CLAWDBOT_SKIP_CANVAS_HOST !== "1" && params.cfg.canvasHost?.enabled !== false;
assertGatewayAuthConfigured(resolvedAuth);
if (tailscaleMode === "funnel" && authMode !== "password") {
throw new Error(
"tailscale funnel requires gateway auth mode=password (set gateway.auth.password or CLAWDBOT_GATEWAY_PASSWORD)",
);
}
if (tailscaleMode !== "off" && !isLoopbackHost(bindHost)) {
throw new Error("tailscale serve/funnel requires gateway bind=loopback (127.0.0.1)");
}
if (!isLoopbackHost(bindHost) && authMode === "none") {
throw new Error(
`refusing to bind gateway to ${bindHost}:${params.port} without auth (set gateway.auth.token or CLAWDBOT_GATEWAY_TOKEN, or pass --token)`,
);
}
return {
bindHost,
controlUiEnabled,
openAiChatCompletionsEnabled,
openResponsesEnabled,
controlUiBasePath,
resolvedAuth,
authMode,
tailscaleConfig,
tailscaleMode,
hooksConfig,
canvasHostEnabled,
};
}