Add projects

This commit is contained in:
Alois 2026-08-24 00:10:41 +02:00
commit 8b607dd700
Signed by: alois
SSH key fingerprint: SHA256:GBzT2DXvAuGV9XIV5W3WrzVpjU54FThmxHXdbz95J24
1802 changed files with 503346 additions and 2 deletions

View file

@ -0,0 +1,606 @@
// Package session derives stable conversation identities from protocol request roots.
package session
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"regexp"
"strings"
"unicode"
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
sdktranslator "github.com/router-for-me/CLIProxyAPI/v7/sdk/translator"
"github.com/tidwall/gjson"
)
const (
identityVersion = "cpa-session-root-v1"
identityPrefix = "ctx:v1:"
instructionRuneLimit = 50
)
var legacyClaudeSessionPattern = regexp.MustCompile(`_session_([a-f0-9-]+)$`)
type canonicalRoot struct {
Version string `json:"version"`
Format string `json:"format"`
CallerScope string `json:"caller_scope"`
Instructions []string `json:"instructions,omitempty"`
User []canonicalPart `json:"user,omitempty"`
Resource string `json:"resource,omitempty"`
}
type canonicalPart struct {
Kind string `json:"kind"`
MIME string `json:"mime,omitempty"`
Value string `json:"value"`
}
// NormalizeExplicitID validates an explicit client-provided session identifier.
// It preserves opaque printable values while rejecting oversized or control-bearing IDs.
func NormalizeExplicitID(raw string) string {
for _, r := range raw {
if unicode.IsControl(r) {
return ""
}
}
raw = strings.TrimSpace(raw)
if raw == "" || len(raw) > 256 {
return ""
}
return raw
}
// ClaudeMetadataSessionID extracts the explicit Claude Code session from
// current JSON metadata or the legacy user_id suffix before bounding the
// surrounding metadata container.
func ClaudeMetadataSessionID(payload []byte) string {
if len(payload) == 0 {
return ""
}
userID := strings.TrimSpace(gjson.GetBytes(payload, "metadata.user_id").String())
if userID == "" {
return ""
}
if strings.HasPrefix(userID, "{") {
return NormalizeExplicitID(gjson.Get(userID, "session_id").String())
}
if matches := legacyClaudeSessionPattern.FindStringSubmatch(userID); len(matches) >= 2 {
return NormalizeExplicitID(matches[1])
}
return ""
}
// CallerScope returns an irreversible namespace for a downstream caller credential.
func CallerScope(value string) string {
value = strings.TrimSpace(value)
if value == "" {
return ""
}
sum := sha256.Sum256([]byte("cli-proxy-api:caller-scope:v1\x00" + value))
return hex.EncodeToString(sum[:])
}
// DerivedID returns a derived session identity stored in execution metadata.
func DerivedID(metadata map[string]any) string {
if metadata == nil {
return ""
}
value, _ := metadata[cliproxyexecutor.DerivedSessionIDMetadataKey].(string)
return strings.TrimSpace(value)
}
// Enrich derives a session identity once and places it in both request and option metadata.
func Enrich(req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (cliproxyexecutor.Request, cliproxyexecutor.Options) {
payload := opts.OriginalRequest
if len(payload) == 0 && len(req.Payload) > 0 {
opts.OriginalRequest = bytes.Clone(req.Payload)
payload = opts.OriginalRequest
}
if executionID := firstNormalizedMetadataID(cliproxyexecutor.ExecutionSessionMetadataKey, opts.Metadata, req.Metadata); executionID != "" {
req.Metadata = metadataWithValue(metadataWithoutKey(req.Metadata, cliproxyexecutor.DerivedSessionIDMetadataKey), cliproxyexecutor.ExecutionSessionMetadataKey, executionID)
opts.Metadata = metadataWithValue(metadataWithoutKey(opts.Metadata, cliproxyexecutor.DerivedSessionIDMetadataKey), cliproxyexecutor.ExecutionSessionMetadataKey, executionID)
return req, opts
}
req.Metadata = metadataWithoutKey(req.Metadata, cliproxyexecutor.ExecutionSessionMetadataKey)
opts.Metadata = metadataWithoutKey(opts.Metadata, cliproxyexecutor.ExecutionSessionMetadataKey)
if hasExplicitSession(opts.Headers, payload) {
req.Metadata = metadataWithoutKey(req.Metadata, cliproxyexecutor.DerivedSessionIDMetadataKey)
opts.Metadata = metadataWithoutKey(opts.Metadata, cliproxyexecutor.DerivedSessionIDMetadataKey)
return req, opts
}
derivedID := firstNormalizedMetadataID(cliproxyexecutor.DerivedSessionIDMetadataKey, opts.Metadata, req.Metadata)
req.Metadata = metadataWithoutKey(req.Metadata, cliproxyexecutor.DerivedSessionIDMetadataKey)
opts.Metadata = metadataWithoutKey(opts.Metadata, cliproxyexecutor.DerivedSessionIDMetadataKey)
if derivedID == "" {
callerScope := metadataString(opts.Metadata, cliproxyexecutor.CallerScopeMetadataKey)
if callerScope == "" {
callerScope = metadataString(req.Metadata, cliproxyexecutor.CallerScopeMetadataKey)
}
derivedID = DeriveID(opts.SourceFormat, payload, callerScope)
}
if derivedID == "" {
return req, opts
}
req.Metadata = metadataWithValue(req.Metadata, cliproxyexecutor.DerivedSessionIDMetadataKey, derivedID)
opts.Metadata = metadataWithValue(opts.Metadata, cliproxyexecutor.DerivedSessionIDMetadataKey, derivedID)
return req, opts
}
func hasExplicitSession(headers map[string][]string, payload []byte) bool {
for _, header := range []string{"X-Claude-Code-Session-Id", "X-Session-ID", "Session-Id", "Session_id", "X-Session-Affinity", "X-Client-Request-Id"} {
if NormalizeExplicitID(headerValue(headers, header)) != "" {
return true
}
}
if len(payload) == 0 {
return false
}
// Parsing without copying matters here: this runs on every request and the
// payload can be multiple megabytes.
root := util.ParseGJSONBytesNoCopy(payload)
for _, path := range []string{"session_id", "sessionId", "conversation_id", "prompt_cache_key"} {
if NormalizeExplicitID(root.Get(path).String()) != "" {
return true
}
}
if ClaudeMetadataSessionID(payload) != "" {
return true
}
userID := strings.TrimSpace(root.Get("metadata.user_id").String())
if NormalizeExplicitID(userID) != "" {
return true
}
conversation := root.Get("conversation")
if NormalizeExplicitID(conversation.Get("id").String()) != "" {
return true
}
return conversation.Type == gjson.String && NormalizeExplicitID(conversation.String()) != ""
}
func headerValue(headers map[string][]string, name string) string {
for key, values := range headers {
if !strings.EqualFold(key, name) {
continue
}
for _, value := range values {
if normalized := NormalizeExplicitID(value); normalized != "" {
return normalized
}
}
}
return ""
}
// DeriveID builds a stable identity from leading instructions and the first complete user input.
func DeriveID(format sdktranslator.Format, payload []byte, callerScope string) string {
if len(payload) == 0 {
return ""
}
var body map[string]any
if errUnmarshal := json.Unmarshal(payload, &body); errUnmarshal != nil {
return ""
}
root := canonicalRoot{
Version: identityVersion,
Format: format.String(),
CallerScope: strings.TrimSpace(callerScope),
}
if sourceFormatEqual(format, sdktranslator.FormatGemini) {
root.Resource = stringField(body, "cachedContent", "cached_content")
}
switch {
case sourceFormatEqual(format, sdktranslator.FormatGemini):
root.Instructions, root.User = geminiRoot(body)
case sourceFormatEqual(format, sdktranslator.FormatInteractions):
root.Instructions, root.User = interactionsRoot(body)
case sourceFormatEqual(format, sdktranslator.FormatOpenAIResponse), sourceFormatEqual(format, sdktranslator.FormatCodex):
root.Instructions, root.User = responsesRoot(body)
case sourceFormatEqual(format, sdktranslator.FormatClaude):
root.Instructions, root.User = messagesRoot(body, true)
default:
root.Instructions, root.User = messagesRoot(body, false)
}
if len(root.User) == 0 {
return ""
}
return hashRoot(root)
}
func messagesRoot(body map[string]any, includeTopLevelSystem bool) ([]string, []canonicalPart) {
instructions := make([]string, 0)
if includeTopLevelSystem {
if system, ok := body["system"]; ok {
instructions = appendInstruction(instructions, system)
}
}
messages, _ := body["messages"].([]any)
for _, rawMessage := range messages {
message, ok := rawMessage.(map[string]any)
if !ok {
continue
}
role := normalizedString(message["role"])
switch role {
case "system", "developer":
instructions = appendInstruction(instructions, message["content"])
case "user":
return instructions, canonicalParts(message["content"])
}
}
return instructions, nil
}
func responsesRoot(body map[string]any) ([]string, []canonicalPart) {
instructions := make([]string, 0)
if value, ok := body["instructions"]; ok {
instructions = appendInstruction(instructions, value)
}
input, ok := body["input"]
if !ok {
return instructions, nil
}
if inputString, okString := input.(string); okString {
return instructions, canonicalParts(inputString)
}
items, _ := input.([]any)
for _, rawItem := range items {
item, okItem := rawItem.(map[string]any)
if !okItem {
continue
}
role := normalizedString(item["role"])
switch role {
case "system", "developer":
instructions = appendInstruction(instructions, item["content"])
case "user":
return instructions, canonicalParts(item["content"])
}
}
return instructions, nil
}
func geminiRoot(body map[string]any) ([]string, []canonicalPart) {
instructions := make([]string, 0)
if value, ok := firstField(body, "systemInstruction", "system_instruction"); ok {
instructions = appendInstruction(instructions, contentValue(value))
}
contents, _ := body["contents"].([]any)
for _, rawContent := range contents {
content, okContent := rawContent.(map[string]any)
if !okContent || normalizedString(content["role"]) != "user" {
continue
}
return instructions, canonicalParts(contentValue(content))
}
return instructions, nil
}
func interactionsRoot(body map[string]any) ([]string, []canonicalPart) {
instructions := make([]string, 0)
if value, ok := firstField(body, "system_instruction", "systemInstruction"); ok {
instructions = appendInstruction(instructions, contentValue(value))
}
input, ok := body["input"]
if !ok {
return instructions, nil
}
if inputString, okString := input.(string); okString {
return instructions, canonicalParts(inputString)
}
for _, entry := range flattenInteractionEntries(input) {
if text, okString := entry.(string); okString {
return instructions, canonicalParts(text)
}
step, okStep := entry.(map[string]any)
if !okStep {
continue
}
role := normalizedString(step["role"])
stepType := normalizedString(step["type"])
if role == "system" || role == "developer" || stepType == "system_instruction" || stepType == "developer_instruction" {
instructions = appendInstruction(instructions, contentValue(step))
continue
}
if role == "user" || stepType == "user_input" || ((stepType == "message" || stepType == "") && role == "") {
return instructions, canonicalParts(contentValue(step))
}
}
return instructions, nil
}
func flattenInteractionEntries(value any) []any {
entries := make([]any, 0)
var appendValue func(any, string)
appendValue = func(current any, inheritedRole string) {
switch typed := current.(type) {
case []any:
for _, child := range typed {
appendValue(child, inheritedRole)
}
case map[string]any:
role := normalizedString(typed["role"])
if role == "" {
role = inheritedRole
}
if steps, ok := typed["steps"].([]any); ok {
for _, child := range steps {
appendValue(child, role)
}
return
}
if role != "" && normalizedString(typed["role"]) == "" {
cloned := make(map[string]any, len(typed)+1)
for key, child := range typed {
cloned[key] = child
}
cloned["role"] = role
typed = cloned
}
entries = append(entries, typed)
default:
entries = append(entries, typed)
}
}
appendValue(value, "")
return entries
}
func appendInstruction(instructions []string, value any) []string {
parts := canonicalParts(value)
var builder strings.Builder
for _, part := range parts {
if part.Kind != "text" || part.Value == "" {
continue
}
if builder.Len() > 0 {
builder.WriteByte('\n')
}
builder.WriteString(part.Value)
}
if builder.Len() == 0 {
return instructions
}
return append(instructions, truncateRunes(builder.String(), instructionRuneLimit))
}
func canonicalParts(value any) []canonicalPart {
parts := make([]canonicalPart, 0)
appendCanonicalParts(&parts, value)
return parts
}
func appendCanonicalParts(parts *[]canonicalPart, value any) {
switch typed := value.(type) {
case nil:
return
case string:
if typed != "" {
*parts = append(*parts, canonicalPart{Kind: "text", Value: typed})
}
case []any:
for _, child := range typed {
appendCanonicalParts(parts, child)
}
case map[string]any:
if text, ok := typed["text"].(string); ok {
appendCanonicalParts(parts, text)
return
}
if nested, ok := typed["content"]; ok {
appendCanonicalParts(parts, nested)
return
}
if nested, ok := typed["parts"]; ok {
appendCanonicalParts(parts, nested)
return
}
if imageURL, ok := typed["image_url"]; ok {
appendMediaPart(parts, "image", imageURL, "")
return
}
if inlineData, ok := firstField(typed, "inlineData", "inline_data"); ok {
appendMediaPart(parts, "inline_data", inlineData, "")
return
}
if fileData, ok := firstField(typed, "fileData", "file_data"); ok {
appendMediaPart(parts, "file", fileData, "")
return
}
if source, ok := typed["source"]; ok {
appendMediaPart(parts, normalizedString(typed["type"]), source, normalizedString(typed["media_type"]))
return
}
normalized := normalizeJSONValue(typed)
encoded, errMarshal := json.Marshal(normalized)
if errMarshal == nil && len(encoded) > 0 {
*parts = append(*parts, canonicalPart{Kind: "json", Value: string(encoded)})
}
default:
encoded, errMarshal := json.Marshal(typed)
if errMarshal == nil && len(encoded) > 0 {
*parts = append(*parts, canonicalPart{Kind: "json", Value: string(encoded)})
}
}
}
func appendMediaPart(parts *[]canonicalPart, kind string, value any, fallbackMIME string) {
kind = strings.TrimSpace(kind)
if kind == "" {
kind = "media"
}
switch typed := value.(type) {
case string:
if typed != "" {
*parts = append(*parts, canonicalPart{Kind: kind, MIME: fallbackMIME, Value: typed})
}
case map[string]any:
mime := stringField(typed, "mimeType", "mime_type", "media_type")
if mime == "" {
mime = fallbackMIME
}
mediaValue := stringField(typed, "url", "uri", "fileUri", "file_uri", "data")
if mediaValue != "" {
*parts = append(*parts, canonicalPart{Kind: kind, MIME: mime, Value: mediaValue})
}
default:
appendCanonicalParts(parts, typed)
}
}
func contentValue(value any) any {
object, ok := value.(map[string]any)
if !ok {
return value
}
if content, exists := object["content"]; exists {
return content
}
if parts, exists := object["parts"]; exists {
return parts
}
if text, exists := object["text"]; exists {
return text
}
return object
}
func normalizeJSONValue(value any) any {
switch typed := value.(type) {
case map[string]any:
normalized := make(map[string]any, len(typed))
for key, child := range typed {
if strings.EqualFold(strings.TrimSpace(key), "cache_control") {
continue
}
normalized[key] = normalizeJSONValue(child)
}
return normalized
case []any:
normalized := make([]any, len(typed))
for index, child := range typed {
normalized[index] = normalizeJSONValue(child)
}
return normalized
default:
return value
}
}
func hashRoot(root canonicalRoot) string {
encoded, errMarshal := json.Marshal(root)
if errMarshal != nil {
return ""
}
sum := sha256.Sum256(encoded)
return identityPrefix + hex.EncodeToString(sum[:])
}
func metadataWithValue(metadata map[string]any, key string, value any) map[string]any {
cloned := make(map[string]any, len(metadata)+1)
for existingKey, existingValue := range metadata {
cloned[existingKey] = existingValue
}
cloned[key] = value
return cloned
}
func metadataWithoutKey(metadata map[string]any, key string) map[string]any {
if metadata == nil {
return nil
}
if _, exists := metadata[key]; !exists {
return metadata
}
cloned := make(map[string]any, len(metadata)-1)
for existingKey, existingValue := range metadata {
if existingKey != key {
cloned[existingKey] = existingValue
}
}
return cloned
}
func firstNormalizedMetadataID(key string, metadataSets ...map[string]any) string {
for _, metadata := range metadataSets {
if metadata == nil {
continue
}
raw, ok := metadata[key].(string)
if !ok {
continue
}
if normalized := NormalizeExplicitID(raw); normalized != "" {
return normalized
}
}
return ""
}
func firstMetadataString(key string, metadataSets ...map[string]any) string {
for _, metadata := range metadataSets {
if value := metadataString(metadata, key); value != "" {
return value
}
}
return ""
}
func metadataString(metadata map[string]any, key string) string {
if metadata == nil {
return ""
}
value, ok := metadata[key]
if !ok || value == nil {
return ""
}
if text, okText := value.(string); okText {
return strings.TrimSpace(text)
}
return strings.TrimSpace(fmt.Sprint(value))
}
func firstField(object map[string]any, keys ...string) (any, bool) {
for _, key := range keys {
if value, ok := object[key]; ok {
return value, true
}
}
return nil, false
}
func stringField(object map[string]any, keys ...string) string {
value, ok := firstField(object, keys...)
if !ok {
return ""
}
text, _ := value.(string)
return strings.TrimSpace(text)
}
func normalizedString(value any) string {
text, _ := value.(string)
return strings.ToLower(strings.TrimSpace(text))
}
func truncateRunes(value string, limit int) string {
if limit <= 0 {
return ""
}
runes := []rune(value)
if len(runes) <= limit {
return value
}
return string(runes[:limit])
}
func sourceFormatEqual(left, right sdktranslator.Format) bool {
return strings.EqualFold(strings.TrimSpace(left.String()), strings.TrimSpace(right.String()))
}

View file

@ -0,0 +1,348 @@
package session
import (
"bytes"
"net/http"
"strings"
"testing"
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
sdktranslator "github.com/router-for-me/CLIProxyAPI/v7/sdk/translator"
)
func TestDeriveIDStableAcrossConversationGrowth(t *testing.T) {
t.Parallel()
tests := []struct {
name string
format sdktranslator.Format
first string
later string
}{
{
name: "openai chat",
format: sdktranslator.FormatOpenAI,
first: `{"messages":[{"role":"system","content":"system prompt"},{"role":"developer","content":"developer prompt"},{"role":"user","content":"complete first user prompt"}]}`,
later: `{"messages":[{"role":"system","content":"system prompt"},{"role":"developer","content":"developer prompt"},{"role":"user","content":"complete first user prompt"},{"role":"assistant","content":"answer"},{"role":"developer","content":"later instruction"},{"role":"user","content":"next"}]}`,
},
{
name: "claude messages",
format: sdktranslator.FormatClaude,
first: `{"system":[{"type":"text","text":"system prompt"}],"messages":[{"role":"user","content":[{"type":"text","text":"complete first user prompt"}]}]}`,
later: `{"system":[{"type":"text","text":"system prompt"}],"messages":[{"role":"user","content":[{"type":"text","text":"complete first user prompt"}]},{"role":"assistant","content":"answer"},{"role":"user","content":"next"}]}`,
},
{
name: "openai responses",
format: sdktranslator.FormatOpenAIResponse,
first: `{"instructions":"system prompt","input":[{"type":"message","role":"developer","content":[{"type":"input_text","text":"developer prompt"}]},{"type":"message","role":"user","content":[{"type":"input_text","text":"complete first user prompt"}]}]}`,
later: `{"instructions":"system prompt","input":[{"type":"message","role":"developer","content":[{"type":"input_text","text":"developer prompt"}]},{"type":"message","role":"user","content":[{"type":"input_text","text":"complete first user prompt"}]},{"type":"message","role":"assistant","content":[{"type":"output_text","text":"answer"}]},{"type":"message","role":"user","content":[{"type":"input_text","text":"next"}]}]}`,
},
{
name: "gemini",
format: sdktranslator.FormatGemini,
first: `{"systemInstruction":{"parts":[{"text":"system prompt"}]},"contents":[{"role":"user","parts":[{"text":"complete first user prompt"}]}]}`,
later: `{"systemInstruction":{"parts":[{"text":"system prompt"}]},"contents":[{"role":"user","parts":[{"text":"complete first user prompt"}]},{"role":"model","parts":[{"text":"answer"}]},{"role":"user","parts":[{"text":"next"}]}]}`,
},
{
name: "interactions",
format: sdktranslator.FormatInteractions,
first: `{"system_instruction":"system prompt","input":[{"type":"developer_instruction","text":"developer prompt"},{"type":"user_input","content":[{"type":"text","text":"complete first user prompt"}]}]}`,
later: `{"system_instruction":"system prompt","input":[{"type":"developer_instruction","text":"developer prompt"},{"type":"user_input","content":[{"type":"text","text":"complete first user prompt"}]},{"type":"model_output","content":[{"type":"text","text":"answer"}]},{"type":"user_input","content":[{"type":"text","text":"next"}]}]}`,
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
firstID := DeriveID(test.format, []byte(test.first), "caller-a")
laterID := DeriveID(test.format, []byte(test.later), "caller-a")
if firstID == "" {
t.Fatal("DeriveID() returned empty")
}
if firstID != laterID {
t.Fatalf("conversation growth changed identity: first=%q later=%q", firstID, laterID)
}
})
}
}
func TestDeriveIDInstructionPrefixAndFullUser(t *testing.T) {
t.Parallel()
prefix := strings.Repeat("界", 50)
first := []byte(`{"messages":[{"role":"system","content":"` + prefix + `timestamp-a"},{"role":"user","content":"` + strings.Repeat("u", 120) + `a"}]}`)
sameRoot := []byte(`{"messages":[{"role":"system","content":"` + prefix + `timestamp-b"},{"role":"user","content":"` + strings.Repeat("u", 120) + `a"}]}`)
differentUser := []byte(`{"messages":[{"role":"system","content":"` + prefix + `timestamp-b"},{"role":"user","content":"` + strings.Repeat("u", 120) + `b"}]}`)
firstID := DeriveID(sdktranslator.FormatOpenAI, first, "caller-a")
if firstID == "" {
t.Fatal("DeriveID() returned empty")
}
if got := DeriveID(sdktranslator.FormatOpenAI, sameRoot, "caller-a"); got != firstID {
t.Fatalf("content after 50 Unicode characters changed identity: got=%q want=%q", got, firstID)
}
if got := DeriveID(sdktranslator.FormatOpenAI, differentUser, "caller-a"); got == firstID {
t.Fatal("different full first user prompt produced the same identity")
}
}
func TestDeriveIDCallerIsolationAndGeminiCachedContent(t *testing.T) {
t.Parallel()
payload := []byte(`{"messages":[{"role":"user","content":"same prompt"}]}`)
callerA := DeriveID(sdktranslator.FormatOpenAI, payload, CallerScope("api-key-a"))
callerB := DeriveID(sdktranslator.FormatOpenAI, payload, CallerScope("api-key-b"))
if callerA == "" || callerB == "" || callerA == callerB {
t.Fatalf("caller isolation failed: callerA=%q callerB=%q", callerA, callerB)
}
firstCached := []byte(`{"cachedContent":"cachedContents/abc","contents":[{"role":"user","parts":[{"text":"first"}]}]}`)
grownCached := []byte(`{"cachedContent":"cachedContents/abc","contents":[{"role":"user","parts":[{"text":"first"}]},{"role":"model","parts":[{"text":"answer"}]},{"role":"user","parts":[{"text":"next"}]}]}`)
differentCached := []byte(`{"cachedContent":"cachedContents/abc","contents":[{"role":"user","parts":[{"text":"different"}]}]}`)
firstID := DeriveID(sdktranslator.FormatGemini, firstCached, "caller-a")
grownID := DeriveID(sdktranslator.FormatGemini, grownCached, "caller-a")
differentID := DeriveID(sdktranslator.FormatGemini, differentCached, "caller-a")
if firstID == "" || firstID != grownID {
t.Fatalf("cachedContent conversation growth changed identity: first=%q grown=%q", firstID, grownID)
}
if differentID == firstID {
t.Fatalf("different first user prompts sharing cachedContent produced the same identity: %q", firstID)
}
}
func TestDeriveIDRequiresFirstUser(t *testing.T) {
t.Parallel()
payload := []byte(`{"messages":[{"role":"system","content":"shared system"}]}`)
if got := DeriveID(sdktranslator.FormatOpenAI, payload, "caller-a"); got != "" {
t.Fatalf("DeriveID() = %q, want empty without first user", got)
}
}
func TestEnrichSkipsDerivationForExplicitSessions(t *testing.T) {
t.Parallel()
tests := []struct {
name string
payload []byte
headers http.Header
requestMetadata map[string]any
optionMetadata map[string]any
}{
{
name: "session header avoids malformed body parsing",
payload: []byte(`not-json`),
headers: http.Header{"X-Session-ID": []string{"header-session"}},
},
{
name: "Claude Code session header",
payload: []byte(`{"messages":[{"role":"user","content":"hello"}]}`),
headers: http.Header{"X-Claude-Code-Session-Id": []string{"claude-session"}},
},
{
name: "later valid multi-value session header",
payload: []byte(`{"messages":[{"role":"user","content":"hello"}]}`),
headers: http.Header{"X-Session-Affinity": []string{"", "later-valid-session"}},
},
{
name: "OpenCode affinity header",
payload: []byte(`{"messages":[{"role":"user","content":"hello"}]}`),
headers: http.Header{"X-Session-Affinity": []string{"opencode-session"}},
},
{
name: "Responses conversation object",
payload: []byte(`{"conversation":{"id":"conversation-session"},"messages":[{"role":"user","content":"hello"}]}`),
},
{
name: "Responses conversation string",
payload: []byte(`{"conversation":"conversation-session","messages":[{"role":"user","content":"hello"}]}`),
},
{
name: "metadata user id",
payload: []byte(`{"metadata":{"user_id":"explicit-user"},"messages":[{"role":"user","content":"hello"}]}`),
},
{
name: "long legacy Claude metadata session",
payload: []byte(`{"metadata":{"user_id":"` + strings.Repeat("x", 300) +
`_session_ac980658-63bd-4fb3-97ba-8da64cb1e344"},"messages":[{"role":"user","content":"hello"}]}`),
},
{
name: "JSON metadata user id without nested session",
payload: []byte(`{"metadata":{"user_id":"{\"device_id\":\"abc123\"}"},"messages":[{"role":"user","content":"hello"}]}`),
},
{
name: "body session id",
payload: []byte(`{"session_id":"body-session","messages":[{"role":"user","content":"hello"}]}`),
},
{
name: "prompt cache key",
payload: []byte(`{"prompt_cache_key":"cache-session","input":"hello"}`),
},
{
name: "execution session option metadata",
payload: []byte(`{"messages":[{"role":"user","content":"hello"}]}`),
optionMetadata: map[string]any{cliproxyexecutor.ExecutionSessionMetadataKey: "execution-session"},
},
{
name: "execution session request metadata",
payload: []byte(`{"messages":[{"role":"user","content":"hello"}]}`),
requestMetadata: map[string]any{cliproxyexecutor.ExecutionSessionMetadataKey: "execution-session"},
},
{
name: "explicit header removes stale derived identity",
payload: []byte(`{"messages":[{"role":"user","content":"hello"}]}`),
headers: http.Header{"x-session-id": []string{"header-session"}},
optionMetadata: map[string]any{
cliproxyexecutor.DerivedSessionIDMetadataKey: "ctx:v1:stale",
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
req := cliproxyexecutor.Request{Payload: test.payload, Metadata: test.requestMetadata}
opts := cliproxyexecutor.Options{
OriginalRequest: test.payload,
SourceFormat: sdktranslator.FormatOpenAI,
Headers: test.headers,
Metadata: test.optionMetadata,
}
enrichedReq, enrichedOpts := Enrich(req, opts)
if got := DerivedID(enrichedReq.Metadata); got != "" {
t.Fatalf("request DerivedSessionID = %q, want empty", got)
}
if got := DerivedID(enrichedOpts.Metadata); got != "" {
t.Fatalf("options DerivedSessionID = %q, want empty", got)
}
if test.name == "execution session option metadata" || test.name == "execution session request metadata" {
if got := metadataString(enrichedReq.Metadata, cliproxyexecutor.ExecutionSessionMetadataKey); got != "execution-session" {
t.Fatalf("request execution session = %q, want execution-session", got)
}
if got := metadataString(enrichedOpts.Metadata, cliproxyexecutor.ExecutionSessionMetadataKey); got != "execution-session" {
t.Fatalf("options execution session = %q, want execution-session", got)
}
}
})
}
}
func TestEnrichDerivesAfterInvalidSessionIdentity(t *testing.T) {
t.Parallel()
baseMessages := `"input":"hello"`
tests := []struct {
name string
payload []byte
headers http.Header
requestMetadata map[string]any
optionMetadata map[string]any
}{
{
name: "oversized prompt cache key",
payload: []byte(`{"prompt_cache_key":"` + strings.Repeat("x", 257) + `",` + baseMessages + `}`),
},
{
name: "trailing control character prompt cache key",
payload: []byte(`{"prompt_cache_key":"tenant\n",` + baseMessages + `}`),
},
{
name: "leading control character prompt cache key",
payload: []byte(`{"prompt_cache_key":"\ttenant",` + baseMessages + `}`),
},
{
name: "control character session header",
payload: []byte(`{` + baseMessages + `}`),
headers: http.Header{"X-Session-Affinity": []string{"bad\nsession"}},
},
{
name: "oversized execution session option metadata",
payload: []byte(`{"input":"hello"}`),
optionMetadata: map[string]any{cliproxyexecutor.ExecutionSessionMetadataKey: strings.Repeat("x", 257)},
},
{
name: "control character execution session request metadata",
payload: []byte(`{"input":"hello"}`),
requestMetadata: map[string]any{cliproxyexecutor.ExecutionSessionMetadataKey: "bad\nsession"},
},
{
name: "oversized retained derived session option metadata",
payload: []byte(`{"input":"hello"}`),
optionMetadata: map[string]any{cliproxyexecutor.DerivedSessionIDMetadataKey: strings.Repeat("x", 257)},
},
{
name: "control character retained derived session request metadata",
payload: []byte(`{"input":"hello"}`),
requestMetadata: map[string]any{cliproxyexecutor.DerivedSessionIDMetadataKey: "bad\nsession"},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
req := cliproxyexecutor.Request{Payload: test.payload, Metadata: test.requestMetadata}
opts := cliproxyexecutor.Options{
OriginalRequest: test.payload,
SourceFormat: sdktranslator.FormatOpenAIResponse,
Headers: test.headers,
Metadata: test.optionMetadata,
}
enrichedReq, enrichedOpts := Enrich(req, opts)
requestID := DerivedID(enrichedReq.Metadata)
optionsID := DerivedID(enrichedOpts.Metadata)
wantID := DeriveID(sdktranslator.FormatOpenAIResponse, test.payload, "")
if requestID != wantID || optionsID != wantID {
t.Fatalf("derived identities = request:%q options:%q, want %q", requestID, optionsID, wantID)
}
if got := metadataString(enrichedReq.Metadata, cliproxyexecutor.ExecutionSessionMetadataKey); got != "" {
t.Fatalf("request execution session = %q, want invalid value removed", got)
}
if got := metadataString(enrichedOpts.Metadata, cliproxyexecutor.ExecutionSessionMetadataKey); got != "" {
t.Fatalf("options execution session = %q, want invalid value removed", got)
}
})
}
}
func TestEnrichCopiesDerivedIdentityToRequestAndOptions(t *testing.T) {
t.Parallel()
req := cliproxyexecutor.Request{Payload: []byte(`{"messages":[{"role":"user","content":"hello"}]}`)}
opts := cliproxyexecutor.Options{
OriginalRequest: req.Payload,
SourceFormat: sdktranslator.FormatOpenAI,
Metadata: map[string]any{cliproxyexecutor.CallerScopeMetadataKey: "caller-a"},
}
enrichedReq, enrichedOpts := Enrich(req, opts)
reqID := DerivedID(enrichedReq.Metadata)
optsID := DerivedID(enrichedOpts.Metadata)
if reqID == "" || reqID != optsID {
t.Fatalf("derived metadata mismatch: request=%q options=%q", reqID, optsID)
}
if _, exists := req.Metadata[cliproxyexecutor.DerivedSessionIDMetadataKey]; exists {
t.Fatal("Enrich() mutated original request metadata")
}
}
func TestEnrichCarriesRequestPayloadIntoSelectionOptions(t *testing.T) {
t.Parallel()
payload := []byte(`{"conversation":{"id":"request-only-conversation"},"input":"hello"}`)
_, enrichedOpts := Enrich(
cliproxyexecutor.Request{Payload: payload},
cliproxyexecutor.Options{SourceFormat: sdktranslator.FormatOpenAIResponse},
)
if !bytes.Equal(enrichedOpts.OriginalRequest, payload) {
t.Fatalf("OriginalRequest = %q, want request payload %q", enrichedOpts.OriginalRequest, payload)
}
if len(enrichedOpts.OriginalRequest) > 0 && &enrichedOpts.OriginalRequest[0] == &payload[0] {
t.Fatal("OriginalRequest aliases Request.Payload instead of preserving a snapshot")
}
if got := DerivedID(enrichedOpts.Metadata); got != "" {
t.Fatalf("DerivedSessionID = %q, want explicit conversation to remain authoritative", got)
}
}