Repository navigation
feat(claude): send eligible managed-key Messages natively (PF-08) #5816
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
lidge-jun
wants to merge
20
commits into
feat/pf07-native-chat-combos
from
feat/pf08-managed-messages-native
Closed
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
e7409f6
refactor(anthropic): share the Messages URL, version and key-auth hea…
lidge-jun c321dfe
feat(anthropic): build a managed native Messages request from the sou…
lidge-jun 88f21f3
feat(claude): name why a Messages route declines the managed native lane
lidge-jun 20cddfe
feat(claude): send eligible managed-key Messages natively
lidge-jun d042f7b
feat(claude): route eligible managed-key Messages to the native lane
lidge-jun 0dca674
feat(claude): count the native Messages body in count_tokens
lidge-jun beda74b
feat(protocols): preview managed native Messages candidates
lidge-jun 3cfded6
test(claude): pin the native Messages builder, decline rule and preview
lidge-jun 2580120
test(claude): exercise managed native Messages against a fake upstream
lidge-jun e84b404
docs(structure): describe the managed native Messages lane
lidge-jun e59e396
feat(protocols): name operator policy that only the bridge applies
lidge-jun 5f6d60d
feat(claude): tell whether translation would elide a blocked skill
lidge-jun 822a5a1
feat(claude): keep Messages on the bridge when bridge-only policy app…
lidge-jun 8bf884a
feat(claude): judge native Messages with the bridge's selector and Cl…
lidge-jun e863993
feat(claude): record why a Messages route declined the native lane
lidge-jun 9706f83
feat(protocols): preview the bridge-only decline for Messages candidates
lidge-jun 776e9a6
feat(claude): echo the requested model on native Messages responses
lidge-jun f4f943f
test(claude): expect the requested model on native Messages responses
lidge-jun b6fc330
test(claude): pin bridge-only-policy declines and the decline trace
lidge-jun 53e4783
docs(structure): record bridge-only declines, the decline trace and m…
lidge-jun File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| /** | ||
| * Managed native Messages request builder (PF-08). | ||
| * | ||
| * The request a proxy-managed Anthropic key sends when the client already spoke Messages: the | ||
| * caller's own body, cut to a fixed field allowlist, with the wire model and the provider's | ||
| * credential. URL, `anthropic-version`, client identity and credential placement come from the | ||
| * same helpers the Anthropic adapter uses, so the two lanes cannot drift apart. | ||
| * | ||
| * Authority: only the provider's configured key is ever placed on the request. No caller header | ||
| * is read here at all — the caller-forward passthrough in `src/server/claude-messages.ts` is the | ||
| * only place a caller's Anthropic credential may travel, and it does not come through here. | ||
| */ | ||
| import type { OcxConfig, OcxProviderConfig } from "../../types"; | ||
| import { anthropicBaseRequestHeaders, applyAnthropicKeyAuth, resolveAnthropicMessagesUrl } from "../anthropic"; | ||
|
|
||
| /** | ||
| * Top-level Messages fields the native lane forwards. Everything else is dropped: an unknown or | ||
| * beta-gated field would otherwise reach the provider unchecked. None of the dropped fields has | ||
| * a name in the protocol feature vocabulary, so no feature effect is recorded for them. | ||
| */ | ||
| export const ANTHROPIC_MESSAGES_PASSTHROUGH_FIELDS = [ | ||
| "model", | ||
| "messages", | ||
| "system", | ||
| "max_tokens", | ||
| "metadata", | ||
| "stop_sequences", | ||
| "stream", | ||
| "temperature", | ||
| "top_p", | ||
| "top_k", | ||
| "tools", | ||
| "tool_choice", | ||
| "thinking", | ||
| "output_config", | ||
| "service_tier", | ||
| ] as const; | ||
|
|
||
| const PASSTHROUGH_FIELD_SET: ReadonlySet<string> = new Set(ANTHROPIC_MESSAGES_PASSTHROUGH_FIELDS); | ||
|
|
||
| export interface AnthropicMessagesPassthroughRequest { | ||
| url: string; | ||
| headers: Record<string, string>; | ||
| /** The serialized wire body. */ | ||
| body: string; | ||
| /** The same body before serialization, for callers that count or inspect what is sent. */ | ||
| wireBody: Record<string, unknown>; | ||
| } | ||
|
|
||
| /** The allowlisted copy of `body` with `model` set to the wire model. Shallow: nothing is cloned. */ | ||
| export function anthropicMessagesPassthroughBody( | ||
| body: Readonly<Record<string, unknown>>, | ||
| modelId: string, | ||
| ): Record<string, unknown> { | ||
| const out: Record<string, unknown> = {}; | ||
| for (const [key, value] of Object.entries(body)) { | ||
| if (PASSTHROUGH_FIELD_SET.has(key) && value !== undefined) out[key] = value; | ||
| } | ||
| out.model = modelId; | ||
| return out; | ||
| } | ||
|
|
||
| /** | ||
| * Build the upstream request. Throws the adapter's own errors for a missing key or a malformed | ||
| * or unresolved base URL. `config` is accepted for parity with the other passthrough builders; | ||
| * no config key changes the wire today. | ||
| */ | ||
| export function buildAnthropicMessagesPassthroughRequest( | ||
| provider: OcxProviderConfig, | ||
| modelId: string, | ||
| body: Readonly<Record<string, unknown>>, | ||
| _config?: OcxConfig, | ||
| ): AnthropicMessagesPassthroughRequest { | ||
| if (provider.authMode !== undefined && provider.authMode !== "key") { | ||
| throw new Error("managed native Messages requires a key-auth anthropic provider"); | ||
| } | ||
| if (typeof provider.apiKey !== "string" || provider.apiKey.trim() === "") { | ||
| throw new Error("anthropic provider requires a non-empty apiKey (authMode: key)"); | ||
| } | ||
| const url = resolveAnthropicMessagesUrl(provider); | ||
| const wireBody = anthropicMessagesPassthroughBody(body, modelId); | ||
| const headers = anthropicBaseRequestHeaders(wireBody.stream === true); | ||
| applyAnthropicKeyAuth(headers, provider); | ||
| // Operator-configured provider headers apply exactly as the adapter applies them. | ||
| if (provider.headers) Object.assign(headers, provider.headers); | ||
| return { url, headers, body: JSON.stringify(wireBody), wireBody }; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a user message carries its content as a string rather than a block array, this condition skips it even though
userMessageToItemsappliesmaybeElideSkillTextto that valid string form. WithmanagedMessagesNativeenabled, a large blocked-skill payload beginning with the skill marker is therefore judged eligible and forwarded intact, bypassingclaudeCode.blockedSkills; check string content with the same elision predicate before continuing.Useful? React with 👍 / 👎.