@posthog/agent 2.3.72 → 2.3.73
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.
- package/dist/adapters/claude/session/jsonl-hydration.js.map +1 -1
- package/dist/agent.js +55 -26
- package/dist/agent.js.map +1 -1
- package/dist/posthog-api.js +1 -1
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.js +55 -26
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +55 -26
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +1 -1
- package/src/adapters/base-acp-agent.ts +11 -2
- package/src/adapters/claude/claude-agent.ts +47 -16
- package/src/adapters/claude/conversion/sdk-to-acp.ts +2 -1
- package/src/adapters/claude/session/models.ts +0 -4
- package/src/adapters/claude/types.ts +2 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/adapters/claude/session/jsonl-hydration.ts","../../../../src/adapters/claude/session/models.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { ContentBlock } from \"@agentclientprotocol/sdk\";\nimport type { PostHogAPIClient } from \"../../../posthog-api\";\nimport type { StoredEntry } from \"../../../types\";\nimport { supports1MContext } from \"./models\";\n\ninterface ConversationTurn {\n role: \"user\" | \"assistant\";\n content: ContentBlock[];\n toolCalls?: ToolCallInfo[];\n}\n\ninterface ToolCallInfo {\n toolCallId: string;\n toolName: string;\n input: unknown;\n result?: unknown;\n}\n\ninterface JsonlConfig {\n sessionId: string;\n cwd: string;\n model?: string;\n version?: string;\n gitBranch?: string;\n slug?: string;\n permissionMode?: string;\n}\n\ninterface ClaudeCodeMeta {\n toolCallId?: string;\n toolName?: string;\n toolInput?: unknown;\n toolResponse?: unknown;\n}\n\ninterface SessionUpdate {\n sessionUpdate: string;\n content?: ContentBlock | ContentBlock[];\n _meta?: { claudeCode?: ClaudeCodeMeta };\n}\n\nconst MAX_PROJECT_KEY_LENGTH = 200;\n\nfunction hashString(s: string): string {\n let hash = 0;\n for (let i = 0; i < s.length; i++) {\n hash = (hash << 5) - hash + s.charCodeAt(i);\n hash |= 0;\n }\n return Math.abs(hash).toString(36);\n}\n\nexport function getSessionJsonlPath(sessionId: string, cwd: string): string {\n const configDir =\n process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), \".claude\");\n let projectKey = cwd.replace(/[^a-zA-Z0-9]/g, \"-\");\n if (projectKey.length > MAX_PROJECT_KEY_LENGTH) {\n projectKey = `${projectKey.slice(0, MAX_PROJECT_KEY_LENGTH)}-${hashString(cwd)}`;\n }\n return path.join(configDir, \"projects\", projectKey, `${sessionId}.jsonl`);\n}\n\nexport function rebuildConversation(\n entries: StoredEntry[],\n): ConversationTurn[] {\n const turns: ConversationTurn[] = [];\n let currentAssistantContent: ContentBlock[] = [];\n let currentToolCalls: ToolCallInfo[] = [];\n\n for (const entry of entries) {\n const method = entry.notification?.method;\n const params = entry.notification?.params as Record<string, unknown>;\n\n if (method === \"session/update\" && params?.update) {\n const update = params.update as SessionUpdate;\n\n switch (update.sessionUpdate) {\n case \"user_message\":\n case \"user_message_chunk\": {\n if (\n currentAssistantContent.length > 0 ||\n currentToolCalls.length > 0\n ) {\n turns.push({\n role: \"assistant\",\n content: currentAssistantContent,\n toolCalls:\n currentToolCalls.length > 0 ? currentToolCalls : undefined,\n });\n currentAssistantContent = [];\n currentToolCalls = [];\n }\n\n const content = update.content;\n const contentArray = Array.isArray(content)\n ? content\n : content\n ? [content]\n : [];\n\n const lastTurn = turns[turns.length - 1];\n if (lastTurn?.role === \"user\") {\n lastTurn.content.push(...contentArray);\n } else {\n turns.push({ role: \"user\", content: contentArray });\n }\n break;\n }\n\n case \"agent_message\":\n case \"agent_message_chunk\":\n case \"agent_thought_chunk\": {\n const content = update.content;\n if (content && !Array.isArray(content)) {\n if (\n content.type === \"text\" &&\n currentAssistantContent.length > 0 &&\n currentAssistantContent[currentAssistantContent.length - 1]\n .type === \"text\"\n ) {\n const lastBlock = currentAssistantContent[\n currentAssistantContent.length - 1\n ] as { type: \"text\"; text: string };\n lastBlock.text += (\n content as { type: \"text\"; text: string }\n ).text;\n } else {\n currentAssistantContent.push(content);\n }\n }\n break;\n }\n\n case \"tool_call\":\n case \"tool_call_update\": {\n const meta = update._meta?.claudeCode;\n if (meta) {\n const { toolCallId, toolName, toolInput, toolResponse } = meta;\n\n if (toolCallId && toolName) {\n let toolCall = currentToolCalls.find(\n (tc) => tc.toolCallId === toolCallId,\n );\n if (!toolCall) {\n toolCall = { toolCallId, toolName, input: toolInput };\n currentToolCalls.push(toolCall);\n }\n if (toolResponse !== undefined) {\n toolCall.result = toolResponse;\n }\n }\n }\n break;\n }\n\n case \"tool_result\": {\n const meta = update._meta?.claudeCode;\n if (meta) {\n const { toolCallId, toolResponse } = meta;\n if (toolCallId) {\n const toolCall = currentToolCalls.find(\n (tc) => tc.toolCallId === toolCallId,\n );\n if (toolCall && toolResponse !== undefined) {\n toolCall.result = toolResponse;\n }\n }\n }\n break;\n }\n }\n }\n }\n\n if (currentAssistantContent.length > 0 || currentToolCalls.length > 0) {\n turns.push({\n role: \"assistant\",\n content: currentAssistantContent,\n toolCalls: currentToolCalls.length > 0 ? currentToolCalls : undefined,\n });\n }\n\n return turns;\n}\n\nconst CHARS_PER_TOKEN = 4;\nconst DEFAULT_MAX_TOKENS = 150_000;\nconst LARGE_CONTEXT_MAX_TOKENS = 800_000;\n\nfunction estimateTurnTokens(turn: ConversationTurn): number {\n let chars = 0;\n for (const block of turn.content) {\n if (\"text\" in block && typeof block.text === \"string\") {\n chars += block.text.length;\n }\n }\n if (turn.toolCalls) {\n for (const tc of turn.toolCalls) {\n chars += JSON.stringify(tc.input ?? \"\").length;\n if (tc.result !== undefined) {\n chars +=\n typeof tc.result === \"string\"\n ? tc.result.length\n : JSON.stringify(tc.result).length;\n }\n }\n }\n return Math.ceil(chars / CHARS_PER_TOKEN);\n}\n\nexport function selectRecentTurns(\n turns: ConversationTurn[],\n maxTokens = DEFAULT_MAX_TOKENS,\n): ConversationTurn[] {\n let budget = maxTokens;\n let startIndex = turns.length;\n\n for (let i = turns.length - 1; i >= 0; i--) {\n const cost = estimateTurnTokens(turns[i]);\n if (cost > budget) break;\n budget -= cost;\n startIndex = i;\n }\n\n // Ensure we start on a user turn so the conversation is well-formed\n while (startIndex < turns.length && turns[startIndex].role !== \"user\") {\n startIndex++;\n }\n\n return turns.slice(startIndex);\n}\n\nconst BASE62 = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\nfunction generateMessageId(): string {\n const bytes = new Uint8Array(24);\n crypto.getRandomValues(bytes);\n let id = \"msg_01\";\n for (const b of bytes) {\n id += BASE62[b % 62];\n }\n return id;\n}\n\nconst ADJECTIVES = [\n \"bright\",\n \"calm\",\n \"daring\",\n \"eager\",\n \"fair\",\n \"gentle\",\n \"happy\",\n \"keen\",\n \"lively\",\n \"merry\",\n \"noble\",\n \"polite\",\n \"quick\",\n \"sharp\",\n \"warm\",\n \"witty\",\n];\nconst VERBS = [\n \"blazing\",\n \"crafting\",\n \"dashing\",\n \"flowing\",\n \"gliding\",\n \"humming\",\n \"jumping\",\n \"linking\",\n \"melting\",\n \"nesting\",\n \"pacing\",\n \"roaming\",\n \"sailing\",\n \"turning\",\n \"waving\",\n \"zoning\",\n];\nconst NOUNS = [\n \"aurora\",\n \"breeze\",\n \"cedar\",\n \"delta\",\n \"ember\",\n \"frost\",\n \"grove\",\n \"haven\",\n \"inlet\",\n \"jewel\",\n \"knoll\",\n \"lotus\",\n \"maple\",\n \"nexus\",\n \"oasis\",\n \"prism\",\n];\n\nfunction generateSlug(): string {\n const pick = (arr: string[]) => arr[Math.floor(Math.random() * arr.length)];\n return `${pick(ADJECTIVES)}-${pick(VERBS)}-${pick(NOUNS)}`;\n}\n\nexport function conversationTurnsToJsonlEntries(\n turns: ConversationTurn[],\n config: JsonlConfig,\n): string[] {\n const lines: string[] = [];\n let parentUuid: string | null = null;\n const model = config.model ?? \"claude-opus-4-6\";\n const version = config.version ?? \"2.1.63\";\n const gitBranch = config.gitBranch ?? \"\";\n const slug = config.slug ?? generateSlug();\n const permissionMode = config.permissionMode ?? \"default\";\n const baseTime = Date.now() - turns.length * 3000;\n let turnIndex = 0;\n\n for (const turn of turns) {\n const timestamp = new Date(baseTime + turnIndex * 3000).toISOString();\n turnIndex++;\n if (turn.role === \"user\") {\n lines.push(\n JSON.stringify({\n type: \"queue-operation\",\n operation: \"enqueue\",\n timestamp,\n sessionId: config.sessionId,\n }),\n );\n lines.push(\n JSON.stringify({\n type: \"queue-operation\",\n operation: \"dequeue\",\n timestamp,\n sessionId: config.sessionId,\n }),\n );\n\n const uuid = randomUUID();\n const textParts = turn.content\n .filter(\n (block) =>\n \"text\" in block && typeof block.text === \"string\" && block.text,\n )\n .map((block) => (block as { text: string }).text);\n\n const userText = textParts.length > 0 ? textParts.join(\"\") : \" \";\n\n lines.push(\n JSON.stringify({\n parentUuid,\n isSidechain: false,\n userType: \"external\",\n cwd: config.cwd,\n sessionId: config.sessionId,\n version,\n gitBranch,\n slug,\n type: \"user\",\n message: {\n role: \"user\",\n content: [{ type: \"text\", text: userText }],\n },\n uuid,\n timestamp,\n permissionMode,\n }),\n );\n parentUuid = uuid;\n } else {\n const allBlocks: unknown[] = [];\n\n for (const block of turn.content) {\n const blockType = (block as { type: string }).type;\n if (blockType === \"thinking\" || blockType === \"text\") {\n allBlocks.push(block);\n }\n }\n\n if (turn.toolCalls) {\n for (const tc of turn.toolCalls) {\n allBlocks.push({\n type: \"tool_use\",\n id: tc.toolCallId,\n name: tc.toolName,\n input: tc.input,\n });\n }\n }\n\n const msgId = generateMessageId();\n const hasToolUse = allBlocks.some(\n (b) => (b as { type: string }).type === \"tool_use\",\n );\n const lastStopReason = hasToolUse ? \"tool_use\" : \"end_turn\";\n\n for (let i = 0; i < allBlocks.length; i++) {\n const block = allBlocks[i];\n const isLast = i === allBlocks.length - 1;\n const uuid = randomUUID();\n\n lines.push(\n JSON.stringify({\n parentUuid,\n isSidechain: false,\n userType: \"external\",\n cwd: config.cwd,\n sessionId: config.sessionId,\n version,\n gitBranch,\n slug,\n type: \"assistant\",\n message: {\n model,\n id: msgId,\n type: \"message\",\n role: \"assistant\",\n content: [block],\n stop_reason: isLast ? lastStopReason : null,\n stop_sequence: null,\n usage: {\n input_tokens: 0,\n cache_creation_input_tokens: 0,\n cache_read_input_tokens: 0,\n output_tokens: 0,\n },\n },\n uuid,\n timestamp,\n }),\n );\n parentUuid = uuid;\n }\n\n if (turn.toolCalls) {\n for (const tc of turn.toolCalls) {\n if (tc.result === undefined) continue;\n\n const uuid = randomUUID();\n const resultText =\n typeof tc.result === \"string\"\n ? tc.result\n : JSON.stringify(tc.result);\n\n lines.push(\n JSON.stringify({\n parentUuid,\n isSidechain: false,\n userType: \"external\",\n cwd: config.cwd,\n sessionId: config.sessionId,\n version,\n gitBranch,\n slug,\n type: \"user\",\n message: {\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: tc.toolCallId,\n content: resultText,\n },\n ],\n },\n uuid,\n timestamp,\n }),\n );\n parentUuid = uuid;\n }\n }\n }\n }\n\n return lines;\n}\n\ninterface HydrationLog {\n info: (msg: string, data?: unknown) => void;\n warn: (msg: string, data?: unknown) => void;\n}\n\nexport async function hydrateSessionJsonl(params: {\n sessionId: string;\n cwd: string;\n taskId: string;\n runId: string;\n model?: string;\n gitBranch?: string;\n permissionMode?: string;\n posthogAPI: PostHogAPIClient;\n log: HydrationLog;\n}): Promise<void> {\n const { posthogAPI, log } = params;\n\n try {\n const jsonlPath = getSessionJsonlPath(params.sessionId, params.cwd);\n try {\n await fs.access(jsonlPath);\n log.info(\"Local JSONL exists, skipping S3 hydration\", {\n sessionId: params.sessionId,\n });\n return;\n } catch {\n // File doesn't exist, proceed with hydration\n }\n\n const taskRun = await posthogAPI.getTaskRun(params.taskId, params.runId);\n if (!taskRun.log_url) {\n log.info(\"No log URL, skipping JSONL hydration\");\n return;\n }\n\n const entries = await posthogAPI.fetchTaskRunLogs(taskRun);\n if (entries.length === 0) {\n log.info(\"No S3 log entries, skipping JSONL hydration\");\n return;\n }\n\n const entryCounts: Record<string, number> = {};\n for (const entry of entries) {\n const method = entry.notification?.method ?? \"unknown\";\n const entryParams = entry.notification?.params as\n | Record<string, unknown>\n | undefined;\n const update = entryParams?.update as\n | { sessionUpdate?: string }\n | undefined;\n const key = update?.sessionUpdate\n ? `${method}:${update.sessionUpdate}`\n : method;\n entryCounts[key] = (entryCounts[key] ?? 0) + 1;\n }\n log.info(\"S3 log entry breakdown\", {\n totalEntries: entries.length,\n types: entryCounts,\n });\n\n const allTurns = rebuildConversation(entries);\n if (allTurns.length === 0) {\n log.info(\"No conversation in S3 logs, skipping JSONL hydration\");\n return;\n }\n\n const maxTokens = supports1MContext(params.model ?? \"\")\n ? LARGE_CONTEXT_MAX_TOKENS\n : DEFAULT_MAX_TOKENS;\n const conversation = selectRecentTurns(allTurns, maxTokens);\n log.info(\"Selected recent turns for hydration\", {\n totalTurns: allTurns.length,\n selectedTurns: conversation.length,\n turnRoles: conversation.map((t) => t.role),\n });\n\n const jsonlLines = conversationTurnsToJsonlEntries(conversation, {\n sessionId: params.sessionId,\n cwd: params.cwd,\n model: params.model,\n gitBranch: params.gitBranch,\n permissionMode: params.permissionMode,\n });\n\n await fs.mkdir(path.dirname(jsonlPath), { recursive: true });\n\n const tmpPath = `${jsonlPath}.tmp.${Date.now()}`;\n await fs.writeFile(tmpPath, `${jsonlLines.join(\"\\n\")}\\n`);\n await fs.rename(tmpPath, jsonlPath);\n\n log.info(\"Hydrated session JSONL from S3\", {\n sessionId: params.sessionId,\n turns: conversation.length,\n lines: jsonlLines.length,\n });\n } catch (err) {\n log.warn(\"Failed to hydrate session JSONL, continuing\", {\n sessionId: params.sessionId,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n}\n","export const DEFAULT_MODEL = \"opus\";\n\nconst GATEWAY_TO_SDK_MODEL: Record<string, string> = {\n \"claude-opus-4-5\": \"opus\",\n \"claude-opus-4-6\": \"opus\",\n \"claude-sonnet-4-5\": \"sonnet\",\n \"claude-sonnet-4-6\": \"sonnet\",\n \"claude-haiku-4-5\": \"haiku\",\n};\n\nexport function toSdkModelId(modelId: string): string {\n return GATEWAY_TO_SDK_MODEL[modelId] ?? modelId;\n}\n\nconst MODELS_WITH_1M_CONTEXT = new Set([\n \"claude-opus-4-6\",\n \"claude-sonnet-4-6\",\n]);\n\nexport function supports1MContext(modelId: string): boolean {\n return MODELS_WITH_1M_CONTEXT.has(modelId);\n}\n\nexport function getDefaultContextWindow(modelId: string): number {\n return supports1MContext(modelId) ? 1_000_000 : 200_000;\n}\n\nconst MODELS_WITH_EFFORT = new Set([\n \"claude-opus-4-5\",\n \"claude-opus-4-6\",\n \"claude-sonnet-4-6\",\n]);\n\nconst MODELS_WITH_MAX_EFFORT = new Set([\"claude-opus-4-6\"]);\n\nexport function supportsEffort(modelId: string): boolean {\n return MODELS_WITH_EFFORT.has(modelId);\n}\n\nexport function supportsMaxEffort(modelId: string): boolean {\n return MODELS_WITH_MAX_EFFORT.has(modelId);\n}\n\ninterface EffortOption {\n value: string;\n name: string;\n}\n\nexport function getEffortOptions(modelId: string): EffortOption[] | null {\n if (!supportsEffort(modelId)) return null;\n\n const options: EffortOption[] = [\n { value: \"low\", name: \"Low\" },\n { value: \"medium\", name: \"Medium\" },\n { value: \"high\", name: \"High\" },\n ];\n\n if (supportsMaxEffort(modelId)) {\n options.push({ value: \"max\", name: \"Max\" });\n }\n\n return options;\n}\n\n// Model alias resolution — lets callers use human-friendly aliases like\n// \"opus\" or \"sonnet\" instead of full model IDs like \"claude-opus-4-6\".\n\nconst MODEL_CONTEXT_HINT_PATTERN = /\\[(\\d+m)\\]$/i;\n\nfunction tokenizeModelPreference(model: string): {\n tokens: string[];\n contextHint?: string;\n} {\n const lower = model.trim().toLowerCase();\n const contextHint = lower\n .match(MODEL_CONTEXT_HINT_PATTERN)?.[1]\n ?.toLowerCase();\n\n const normalized = lower.replace(MODEL_CONTEXT_HINT_PATTERN, \" $1 \");\n const rawTokens = normalized.split(/[^a-z0-9]+/).filter(Boolean);\n const tokens = rawTokens\n .map((token) => {\n if (token === \"opusplan\") return \"opus\";\n if (token === \"best\" || token === \"default\") return \"\";\n return token;\n })\n .filter((token) => token && token !== \"claude\")\n .filter((token) => /[a-z]/.test(token) || token.endsWith(\"m\"));\n\n return { tokens, contextHint };\n}\n\ninterface ModelOption {\n value: string;\n name?: string;\n description?: string;\n}\n\nfunction scoreModelMatch(\n model: ModelOption,\n tokens: string[],\n contextHint?: string,\n): number {\n const haystack = `${model.value} ${model.name ?? \"\"}`.toLowerCase();\n let score = 0;\n for (const token of tokens) {\n if (haystack.includes(token)) {\n score += token === contextHint ? 3 : 1;\n }\n }\n return score;\n}\n\nexport function resolveModelPreference(\n preference: string,\n options: ModelOption[],\n): string | null {\n const trimmed = preference.trim();\n if (!trimmed) return null;\n\n const lower = trimmed.toLowerCase();\n\n // Exact match on value or display name\n const directMatch = options.find(\n (o) =>\n o.value === trimmed ||\n o.value.toLowerCase() === lower ||\n (o.name && o.name.toLowerCase() === lower),\n );\n if (directMatch) return directMatch.value;\n\n // Substring match\n const includesMatch = options.find((o) => {\n const value = o.value.toLowerCase();\n const display = (o.name ?? \"\").toLowerCase();\n return (\n value.includes(lower) || display.includes(lower) || lower.includes(value)\n );\n });\n if (includesMatch) return includesMatch.value;\n\n // Tokenized matching for aliases like \"opus[1m]\"\n const { tokens, contextHint } = tokenizeModelPreference(trimmed);\n if (tokens.length === 0) return null;\n\n let bestMatch: ModelOption | null = null;\n let bestScore = 0;\n for (const model of options) {\n const score = scoreModelMatch(model, tokens, contextHint);\n if (0 < score && (!bestMatch || bestScore < score)) {\n bestMatch = model;\n bestScore = score;\n }\n }\n\n return bestMatch?.value ?? null;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/adapters/claude/session/jsonl-hydration.ts","../../../../src/adapters/claude/session/models.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { ContentBlock } from \"@agentclientprotocol/sdk\";\nimport type { PostHogAPIClient } from \"../../../posthog-api\";\nimport type { StoredEntry } from \"../../../types\";\nimport { supports1MContext } from \"./models\";\n\ninterface ConversationTurn {\n role: \"user\" | \"assistant\";\n content: ContentBlock[];\n toolCalls?: ToolCallInfo[];\n}\n\ninterface ToolCallInfo {\n toolCallId: string;\n toolName: string;\n input: unknown;\n result?: unknown;\n}\n\ninterface JsonlConfig {\n sessionId: string;\n cwd: string;\n model?: string;\n version?: string;\n gitBranch?: string;\n slug?: string;\n permissionMode?: string;\n}\n\ninterface ClaudeCodeMeta {\n toolCallId?: string;\n toolName?: string;\n toolInput?: unknown;\n toolResponse?: unknown;\n}\n\ninterface SessionUpdate {\n sessionUpdate: string;\n content?: ContentBlock | ContentBlock[];\n _meta?: { claudeCode?: ClaudeCodeMeta };\n}\n\nconst MAX_PROJECT_KEY_LENGTH = 200;\n\nfunction hashString(s: string): string {\n let hash = 0;\n for (let i = 0; i < s.length; i++) {\n hash = (hash << 5) - hash + s.charCodeAt(i);\n hash |= 0;\n }\n return Math.abs(hash).toString(36);\n}\n\nexport function getSessionJsonlPath(sessionId: string, cwd: string): string {\n const configDir =\n process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), \".claude\");\n let projectKey = cwd.replace(/[^a-zA-Z0-9]/g, \"-\");\n if (projectKey.length > MAX_PROJECT_KEY_LENGTH) {\n projectKey = `${projectKey.slice(0, MAX_PROJECT_KEY_LENGTH)}-${hashString(cwd)}`;\n }\n return path.join(configDir, \"projects\", projectKey, `${sessionId}.jsonl`);\n}\n\nexport function rebuildConversation(\n entries: StoredEntry[],\n): ConversationTurn[] {\n const turns: ConversationTurn[] = [];\n let currentAssistantContent: ContentBlock[] = [];\n let currentToolCalls: ToolCallInfo[] = [];\n\n for (const entry of entries) {\n const method = entry.notification?.method;\n const params = entry.notification?.params as Record<string, unknown>;\n\n if (method === \"session/update\" && params?.update) {\n const update = params.update as SessionUpdate;\n\n switch (update.sessionUpdate) {\n case \"user_message\":\n case \"user_message_chunk\": {\n if (\n currentAssistantContent.length > 0 ||\n currentToolCalls.length > 0\n ) {\n turns.push({\n role: \"assistant\",\n content: currentAssistantContent,\n toolCalls:\n currentToolCalls.length > 0 ? currentToolCalls : undefined,\n });\n currentAssistantContent = [];\n currentToolCalls = [];\n }\n\n const content = update.content;\n const contentArray = Array.isArray(content)\n ? content\n : content\n ? [content]\n : [];\n\n const lastTurn = turns[turns.length - 1];\n if (lastTurn?.role === \"user\") {\n lastTurn.content.push(...contentArray);\n } else {\n turns.push({ role: \"user\", content: contentArray });\n }\n break;\n }\n\n case \"agent_message\":\n case \"agent_message_chunk\":\n case \"agent_thought_chunk\": {\n const content = update.content;\n if (content && !Array.isArray(content)) {\n if (\n content.type === \"text\" &&\n currentAssistantContent.length > 0 &&\n currentAssistantContent[currentAssistantContent.length - 1]\n .type === \"text\"\n ) {\n const lastBlock = currentAssistantContent[\n currentAssistantContent.length - 1\n ] as { type: \"text\"; text: string };\n lastBlock.text += (\n content as { type: \"text\"; text: string }\n ).text;\n } else {\n currentAssistantContent.push(content);\n }\n }\n break;\n }\n\n case \"tool_call\":\n case \"tool_call_update\": {\n const meta = update._meta?.claudeCode;\n if (meta) {\n const { toolCallId, toolName, toolInput, toolResponse } = meta;\n\n if (toolCallId && toolName) {\n let toolCall = currentToolCalls.find(\n (tc) => tc.toolCallId === toolCallId,\n );\n if (!toolCall) {\n toolCall = { toolCallId, toolName, input: toolInput };\n currentToolCalls.push(toolCall);\n }\n if (toolResponse !== undefined) {\n toolCall.result = toolResponse;\n }\n }\n }\n break;\n }\n\n case \"tool_result\": {\n const meta = update._meta?.claudeCode;\n if (meta) {\n const { toolCallId, toolResponse } = meta;\n if (toolCallId) {\n const toolCall = currentToolCalls.find(\n (tc) => tc.toolCallId === toolCallId,\n );\n if (toolCall && toolResponse !== undefined) {\n toolCall.result = toolResponse;\n }\n }\n }\n break;\n }\n }\n }\n }\n\n if (currentAssistantContent.length > 0 || currentToolCalls.length > 0) {\n turns.push({\n role: \"assistant\",\n content: currentAssistantContent,\n toolCalls: currentToolCalls.length > 0 ? currentToolCalls : undefined,\n });\n }\n\n return turns;\n}\n\nconst CHARS_PER_TOKEN = 4;\nconst DEFAULT_MAX_TOKENS = 150_000;\nconst LARGE_CONTEXT_MAX_TOKENS = 800_000;\n\nfunction estimateTurnTokens(turn: ConversationTurn): number {\n let chars = 0;\n for (const block of turn.content) {\n if (\"text\" in block && typeof block.text === \"string\") {\n chars += block.text.length;\n }\n }\n if (turn.toolCalls) {\n for (const tc of turn.toolCalls) {\n chars += JSON.stringify(tc.input ?? \"\").length;\n if (tc.result !== undefined) {\n chars +=\n typeof tc.result === \"string\"\n ? tc.result.length\n : JSON.stringify(tc.result).length;\n }\n }\n }\n return Math.ceil(chars / CHARS_PER_TOKEN);\n}\n\nexport function selectRecentTurns(\n turns: ConversationTurn[],\n maxTokens = DEFAULT_MAX_TOKENS,\n): ConversationTurn[] {\n let budget = maxTokens;\n let startIndex = turns.length;\n\n for (let i = turns.length - 1; i >= 0; i--) {\n const cost = estimateTurnTokens(turns[i]);\n if (cost > budget) break;\n budget -= cost;\n startIndex = i;\n }\n\n // Ensure we start on a user turn so the conversation is well-formed\n while (startIndex < turns.length && turns[startIndex].role !== \"user\") {\n startIndex++;\n }\n\n return turns.slice(startIndex);\n}\n\nconst BASE62 = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\nfunction generateMessageId(): string {\n const bytes = new Uint8Array(24);\n crypto.getRandomValues(bytes);\n let id = \"msg_01\";\n for (const b of bytes) {\n id += BASE62[b % 62];\n }\n return id;\n}\n\nconst ADJECTIVES = [\n \"bright\",\n \"calm\",\n \"daring\",\n \"eager\",\n \"fair\",\n \"gentle\",\n \"happy\",\n \"keen\",\n \"lively\",\n \"merry\",\n \"noble\",\n \"polite\",\n \"quick\",\n \"sharp\",\n \"warm\",\n \"witty\",\n];\nconst VERBS = [\n \"blazing\",\n \"crafting\",\n \"dashing\",\n \"flowing\",\n \"gliding\",\n \"humming\",\n \"jumping\",\n \"linking\",\n \"melting\",\n \"nesting\",\n \"pacing\",\n \"roaming\",\n \"sailing\",\n \"turning\",\n \"waving\",\n \"zoning\",\n];\nconst NOUNS = [\n \"aurora\",\n \"breeze\",\n \"cedar\",\n \"delta\",\n \"ember\",\n \"frost\",\n \"grove\",\n \"haven\",\n \"inlet\",\n \"jewel\",\n \"knoll\",\n \"lotus\",\n \"maple\",\n \"nexus\",\n \"oasis\",\n \"prism\",\n];\n\nfunction generateSlug(): string {\n const pick = (arr: string[]) => arr[Math.floor(Math.random() * arr.length)];\n return `${pick(ADJECTIVES)}-${pick(VERBS)}-${pick(NOUNS)}`;\n}\n\nexport function conversationTurnsToJsonlEntries(\n turns: ConversationTurn[],\n config: JsonlConfig,\n): string[] {\n const lines: string[] = [];\n let parentUuid: string | null = null;\n const model = config.model ?? \"claude-opus-4-6\";\n const version = config.version ?? \"2.1.63\";\n const gitBranch = config.gitBranch ?? \"\";\n const slug = config.slug ?? generateSlug();\n const permissionMode = config.permissionMode ?? \"default\";\n const baseTime = Date.now() - turns.length * 3000;\n let turnIndex = 0;\n\n for (const turn of turns) {\n const timestamp = new Date(baseTime + turnIndex * 3000).toISOString();\n turnIndex++;\n if (turn.role === \"user\") {\n lines.push(\n JSON.stringify({\n type: \"queue-operation\",\n operation: \"enqueue\",\n timestamp,\n sessionId: config.sessionId,\n }),\n );\n lines.push(\n JSON.stringify({\n type: \"queue-operation\",\n operation: \"dequeue\",\n timestamp,\n sessionId: config.sessionId,\n }),\n );\n\n const uuid = randomUUID();\n const textParts = turn.content\n .filter(\n (block) =>\n \"text\" in block && typeof block.text === \"string\" && block.text,\n )\n .map((block) => (block as { text: string }).text);\n\n const userText = textParts.length > 0 ? textParts.join(\"\") : \" \";\n\n lines.push(\n JSON.stringify({\n parentUuid,\n isSidechain: false,\n userType: \"external\",\n cwd: config.cwd,\n sessionId: config.sessionId,\n version,\n gitBranch,\n slug,\n type: \"user\",\n message: {\n role: \"user\",\n content: [{ type: \"text\", text: userText }],\n },\n uuid,\n timestamp,\n permissionMode,\n }),\n );\n parentUuid = uuid;\n } else {\n const allBlocks: unknown[] = [];\n\n for (const block of turn.content) {\n const blockType = (block as { type: string }).type;\n if (blockType === \"thinking\" || blockType === \"text\") {\n allBlocks.push(block);\n }\n }\n\n if (turn.toolCalls) {\n for (const tc of turn.toolCalls) {\n allBlocks.push({\n type: \"tool_use\",\n id: tc.toolCallId,\n name: tc.toolName,\n input: tc.input,\n });\n }\n }\n\n const msgId = generateMessageId();\n const hasToolUse = allBlocks.some(\n (b) => (b as { type: string }).type === \"tool_use\",\n );\n const lastStopReason = hasToolUse ? \"tool_use\" : \"end_turn\";\n\n for (let i = 0; i < allBlocks.length; i++) {\n const block = allBlocks[i];\n const isLast = i === allBlocks.length - 1;\n const uuid = randomUUID();\n\n lines.push(\n JSON.stringify({\n parentUuid,\n isSidechain: false,\n userType: \"external\",\n cwd: config.cwd,\n sessionId: config.sessionId,\n version,\n gitBranch,\n slug,\n type: \"assistant\",\n message: {\n model,\n id: msgId,\n type: \"message\",\n role: \"assistant\",\n content: [block],\n stop_reason: isLast ? lastStopReason : null,\n stop_sequence: null,\n usage: {\n input_tokens: 0,\n cache_creation_input_tokens: 0,\n cache_read_input_tokens: 0,\n output_tokens: 0,\n },\n },\n uuid,\n timestamp,\n }),\n );\n parentUuid = uuid;\n }\n\n if (turn.toolCalls) {\n for (const tc of turn.toolCalls) {\n if (tc.result === undefined) continue;\n\n const uuid = randomUUID();\n const resultText =\n typeof tc.result === \"string\"\n ? tc.result\n : JSON.stringify(tc.result);\n\n lines.push(\n JSON.stringify({\n parentUuid,\n isSidechain: false,\n userType: \"external\",\n cwd: config.cwd,\n sessionId: config.sessionId,\n version,\n gitBranch,\n slug,\n type: \"user\",\n message: {\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: tc.toolCallId,\n content: resultText,\n },\n ],\n },\n uuid,\n timestamp,\n }),\n );\n parentUuid = uuid;\n }\n }\n }\n }\n\n return lines;\n}\n\ninterface HydrationLog {\n info: (msg: string, data?: unknown) => void;\n warn: (msg: string, data?: unknown) => void;\n}\n\nexport async function hydrateSessionJsonl(params: {\n sessionId: string;\n cwd: string;\n taskId: string;\n runId: string;\n model?: string;\n gitBranch?: string;\n permissionMode?: string;\n posthogAPI: PostHogAPIClient;\n log: HydrationLog;\n}): Promise<void> {\n const { posthogAPI, log } = params;\n\n try {\n const jsonlPath = getSessionJsonlPath(params.sessionId, params.cwd);\n try {\n await fs.access(jsonlPath);\n log.info(\"Local JSONL exists, skipping S3 hydration\", {\n sessionId: params.sessionId,\n });\n return;\n } catch {\n // File doesn't exist, proceed with hydration\n }\n\n const taskRun = await posthogAPI.getTaskRun(params.taskId, params.runId);\n if (!taskRun.log_url) {\n log.info(\"No log URL, skipping JSONL hydration\");\n return;\n }\n\n const entries = await posthogAPI.fetchTaskRunLogs(taskRun);\n if (entries.length === 0) {\n log.info(\"No S3 log entries, skipping JSONL hydration\");\n return;\n }\n\n const entryCounts: Record<string, number> = {};\n for (const entry of entries) {\n const method = entry.notification?.method ?? \"unknown\";\n const entryParams = entry.notification?.params as\n | Record<string, unknown>\n | undefined;\n const update = entryParams?.update as\n | { sessionUpdate?: string }\n | undefined;\n const key = update?.sessionUpdate\n ? `${method}:${update.sessionUpdate}`\n : method;\n entryCounts[key] = (entryCounts[key] ?? 0) + 1;\n }\n log.info(\"S3 log entry breakdown\", {\n totalEntries: entries.length,\n types: entryCounts,\n });\n\n const allTurns = rebuildConversation(entries);\n if (allTurns.length === 0) {\n log.info(\"No conversation in S3 logs, skipping JSONL hydration\");\n return;\n }\n\n const maxTokens = supports1MContext(params.model ?? \"\")\n ? LARGE_CONTEXT_MAX_TOKENS\n : DEFAULT_MAX_TOKENS;\n const conversation = selectRecentTurns(allTurns, maxTokens);\n log.info(\"Selected recent turns for hydration\", {\n totalTurns: allTurns.length,\n selectedTurns: conversation.length,\n turnRoles: conversation.map((t) => t.role),\n });\n\n const jsonlLines = conversationTurnsToJsonlEntries(conversation, {\n sessionId: params.sessionId,\n cwd: params.cwd,\n model: params.model,\n gitBranch: params.gitBranch,\n permissionMode: params.permissionMode,\n });\n\n await fs.mkdir(path.dirname(jsonlPath), { recursive: true });\n\n const tmpPath = `${jsonlPath}.tmp.${Date.now()}`;\n await fs.writeFile(tmpPath, `${jsonlLines.join(\"\\n\")}\\n`);\n await fs.rename(tmpPath, jsonlPath);\n\n log.info(\"Hydrated session JSONL from S3\", {\n sessionId: params.sessionId,\n turns: conversation.length,\n lines: jsonlLines.length,\n });\n } catch (err) {\n log.warn(\"Failed to hydrate session JSONL, continuing\", {\n sessionId: params.sessionId,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n}\n","export const DEFAULT_MODEL = \"opus\";\n\nconst GATEWAY_TO_SDK_MODEL: Record<string, string> = {\n \"claude-opus-4-5\": \"opus\",\n \"claude-opus-4-6\": \"opus\",\n \"claude-sonnet-4-5\": \"sonnet\",\n \"claude-sonnet-4-6\": \"sonnet\",\n \"claude-haiku-4-5\": \"haiku\",\n};\n\nexport function toSdkModelId(modelId: string): string {\n return GATEWAY_TO_SDK_MODEL[modelId] ?? modelId;\n}\n\nconst MODELS_WITH_1M_CONTEXT = new Set([\n \"claude-opus-4-6\",\n \"claude-sonnet-4-6\",\n]);\n\nexport function supports1MContext(modelId: string): boolean {\n return MODELS_WITH_1M_CONTEXT.has(modelId);\n}\n\nconst MODELS_WITH_EFFORT = new Set([\n \"claude-opus-4-5\",\n \"claude-opus-4-6\",\n \"claude-sonnet-4-6\",\n]);\n\nconst MODELS_WITH_MAX_EFFORT = new Set([\"claude-opus-4-6\"]);\n\nexport function supportsEffort(modelId: string): boolean {\n return MODELS_WITH_EFFORT.has(modelId);\n}\n\nexport function supportsMaxEffort(modelId: string): boolean {\n return MODELS_WITH_MAX_EFFORT.has(modelId);\n}\n\ninterface EffortOption {\n value: string;\n name: string;\n}\n\nexport function getEffortOptions(modelId: string): EffortOption[] | null {\n if (!supportsEffort(modelId)) return null;\n\n const options: EffortOption[] = [\n { value: \"low\", name: \"Low\" },\n { value: \"medium\", name: \"Medium\" },\n { value: \"high\", name: \"High\" },\n ];\n\n if (supportsMaxEffort(modelId)) {\n options.push({ value: \"max\", name: \"Max\" });\n }\n\n return options;\n}\n\n// Model alias resolution — lets callers use human-friendly aliases like\n// \"opus\" or \"sonnet\" instead of full model IDs like \"claude-opus-4-6\".\n\nconst MODEL_CONTEXT_HINT_PATTERN = /\\[(\\d+m)\\]$/i;\n\nfunction tokenizeModelPreference(model: string): {\n tokens: string[];\n contextHint?: string;\n} {\n const lower = model.trim().toLowerCase();\n const contextHint = lower\n .match(MODEL_CONTEXT_HINT_PATTERN)?.[1]\n ?.toLowerCase();\n\n const normalized = lower.replace(MODEL_CONTEXT_HINT_PATTERN, \" $1 \");\n const rawTokens = normalized.split(/[^a-z0-9]+/).filter(Boolean);\n const tokens = rawTokens\n .map((token) => {\n if (token === \"opusplan\") return \"opus\";\n if (token === \"best\" || token === \"default\") return \"\";\n return token;\n })\n .filter((token) => token && token !== \"claude\")\n .filter((token) => /[a-z]/.test(token) || token.endsWith(\"m\"));\n\n return { tokens, contextHint };\n}\n\ninterface ModelOption {\n value: string;\n name?: string;\n description?: string;\n}\n\nfunction scoreModelMatch(\n model: ModelOption,\n tokens: string[],\n contextHint?: string,\n): number {\n const haystack = `${model.value} ${model.name ?? \"\"}`.toLowerCase();\n let score = 0;\n for (const token of tokens) {\n if (haystack.includes(token)) {\n score += token === contextHint ? 3 : 1;\n }\n }\n return score;\n}\n\nexport function resolveModelPreference(\n preference: string,\n options: ModelOption[],\n): string | null {\n const trimmed = preference.trim();\n if (!trimmed) return null;\n\n const lower = trimmed.toLowerCase();\n\n // Exact match on value or display name\n const directMatch = options.find(\n (o) =>\n o.value === trimmed ||\n o.value.toLowerCase() === lower ||\n (o.name && o.name.toLowerCase() === lower),\n );\n if (directMatch) return directMatch.value;\n\n // Substring match\n const includesMatch = options.find((o) => {\n const value = o.value.toLowerCase();\n const display = (o.name ?? \"\").toLowerCase();\n return (\n value.includes(lower) || display.includes(lower) || lower.includes(value)\n );\n });\n if (includesMatch) return includesMatch.value;\n\n // Tokenized matching for aliases like \"opus[1m]\"\n const { tokens, contextHint } = tokenizeModelPreference(trimmed);\n if (tokens.length === 0) return null;\n\n let bestMatch: ModelOption | null = null;\n let bestScore = 0;\n for (const model of options) {\n const score = scoreModelMatch(model, tokens, contextHint);\n if (0 < score && (!bestMatch || bestScore < score)) {\n bestMatch = model;\n bestScore = score;\n }\n }\n\n return bestMatch?.value ?? null;\n}\n"],"mappings":"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","names":[]}
|
package/dist/agent.js
CHANGED
|
@@ -281,7 +281,7 @@ import { v7 as uuidv7 } from "uuid";
|
|
|
281
281
|
// package.json
|
|
282
282
|
var package_default = {
|
|
283
283
|
name: "@posthog/agent",
|
|
284
|
-
version: "2.3.
|
|
284
|
+
version: "2.3.73",
|
|
285
285
|
repository: "https://github.com/PostHog/code",
|
|
286
286
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
287
287
|
exports: {
|
|
@@ -508,12 +508,14 @@ function formatGatewayModelName(model) {
|
|
|
508
508
|
}
|
|
509
509
|
|
|
510
510
|
// src/adapters/base-acp-agent.ts
|
|
511
|
+
var DEFAULT_CONTEXT_WINDOW = 2e5;
|
|
511
512
|
var BaseAcpAgent = class {
|
|
512
513
|
session;
|
|
513
514
|
sessionId;
|
|
514
515
|
client;
|
|
515
516
|
logger;
|
|
516
517
|
fileContentCache = {};
|
|
518
|
+
gatewayModels = [];
|
|
517
519
|
constructor(client) {
|
|
518
520
|
this.client = client;
|
|
519
521
|
this.logger = new Logger({ debug: true, prefix: "[BaseAcpAgent]" });
|
|
@@ -566,8 +568,8 @@ var BaseAcpAgent = class {
|
|
|
566
568
|
throw new Error("Method not implemented.");
|
|
567
569
|
}
|
|
568
570
|
async getModelConfigOptions(currentModelOverride) {
|
|
569
|
-
|
|
570
|
-
const options = gatewayModels.filter((model) => isAnthropicModel(model)).map((model) => ({
|
|
571
|
+
this.gatewayModels = await fetchGatewayModels();
|
|
572
|
+
const options = this.gatewayModels.filter((model) => isAnthropicModel(model)).map((model) => ({
|
|
571
573
|
value: model.id,
|
|
572
574
|
name: formatGatewayModelName(model),
|
|
573
575
|
description: `Context: ${model.context_window.toLocaleString()} tokens`
|
|
@@ -588,6 +590,10 @@ var BaseAcpAgent = class {
|
|
|
588
590
|
}
|
|
589
591
|
return { currentModelId, options };
|
|
590
592
|
}
|
|
593
|
+
getContextWindowForModel(modelId) {
|
|
594
|
+
const match = this.gatewayModels.find((m) => m.id === modelId);
|
|
595
|
+
return match?.context_window ?? DEFAULT_CONTEXT_WINDOW;
|
|
596
|
+
}
|
|
591
597
|
};
|
|
592
598
|
|
|
593
599
|
// src/adapters/claude/conversion/acp-to-sdk.ts
|
|
@@ -1763,7 +1769,7 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileCon
|
|
|
1763
1769
|
}
|
|
1764
1770
|
}
|
|
1765
1771
|
async function handleSystemMessage(message, context) {
|
|
1766
|
-
const { sessionId, client, logger } = context;
|
|
1772
|
+
const { session, sessionId, client, logger } = context;
|
|
1767
1773
|
switch (message.subtype) {
|
|
1768
1774
|
case "init":
|
|
1769
1775
|
break;
|
|
@@ -1771,7 +1777,8 @@ async function handleSystemMessage(message, context) {
|
|
|
1771
1777
|
await client.extNotification("_posthog/compact_boundary", {
|
|
1772
1778
|
sessionId,
|
|
1773
1779
|
trigger: message.compact_metadata.trigger,
|
|
1774
|
-
preTokens: message.compact_metadata.pre_tokens
|
|
1780
|
+
preTokens: message.compact_metadata.pre_tokens,
|
|
1781
|
+
contextSize: session.contextSize
|
|
1775
1782
|
});
|
|
1776
1783
|
break;
|
|
1777
1784
|
case "hook_response":
|
|
@@ -2596,16 +2603,6 @@ var GATEWAY_TO_SDK_MODEL = {
|
|
|
2596
2603
|
function toSdkModelId(modelId) {
|
|
2597
2604
|
return GATEWAY_TO_SDK_MODEL[modelId] ?? modelId;
|
|
2598
2605
|
}
|
|
2599
|
-
var MODELS_WITH_1M_CONTEXT = /* @__PURE__ */ new Set([
|
|
2600
|
-
"claude-opus-4-6",
|
|
2601
|
-
"claude-sonnet-4-6"
|
|
2602
|
-
]);
|
|
2603
|
-
function supports1MContext(modelId) {
|
|
2604
|
-
return MODELS_WITH_1M_CONTEXT.has(modelId);
|
|
2605
|
-
}
|
|
2606
|
-
function getDefaultContextWindow(modelId) {
|
|
2607
|
-
return supports1MContext(modelId) ? 1e6 : 2e5;
|
|
2608
|
-
}
|
|
2609
2606
|
var MODELS_WITH_EFFORT = /* @__PURE__ */ new Set([
|
|
2610
2607
|
"claude-opus-4-5",
|
|
2611
2608
|
"claude-opus-4-6",
|
|
@@ -3338,6 +3335,16 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3338
3335
|
this.session.promptRunning = true;
|
|
3339
3336
|
let handedOff = false;
|
|
3340
3337
|
let lastAssistantTotalUsage = null;
|
|
3338
|
+
if (this.session.lastContextWindowSize == null) {
|
|
3339
|
+
this.session.lastContextWindowSize = this.getContextWindowForModel(
|
|
3340
|
+
this.session.modelId ?? ""
|
|
3341
|
+
);
|
|
3342
|
+
this.logger.debug("Initial context window size from gateway", {
|
|
3343
|
+
modelId: this.session.modelId,
|
|
3344
|
+
contextWindowSize: this.session.lastContextWindowSize
|
|
3345
|
+
});
|
|
3346
|
+
}
|
|
3347
|
+
let lastContextWindowSize = this.session.lastContextWindowSize;
|
|
3341
3348
|
const supportsTerminalOutput = this.clientCapabilities?._meta?.terminal_output === true;
|
|
3342
3349
|
const context = {
|
|
3343
3350
|
session: this.session,
|
|
@@ -3389,8 +3396,19 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3389
3396
|
const contextWindows = Object.values(message.modelUsage).map(
|
|
3390
3397
|
(m) => m.contextWindow
|
|
3391
3398
|
);
|
|
3392
|
-
|
|
3393
|
-
|
|
3399
|
+
if (contextWindows.length > 0) {
|
|
3400
|
+
const sdkContextWindow = Math.min(...contextWindows);
|
|
3401
|
+
if (sdkContextWindow > lastContextWindowSize) {
|
|
3402
|
+
lastContextWindowSize = sdkContextWindow;
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
this.session.lastContextWindowSize = lastContextWindowSize;
|
|
3406
|
+
this.logger.debug("Context window size from result", {
|
|
3407
|
+
sdkReported: contextWindows,
|
|
3408
|
+
resolved: lastContextWindowSize,
|
|
3409
|
+
modelId: this.session.modelId
|
|
3410
|
+
});
|
|
3411
|
+
this.session.contextSize = lastContextWindowSize;
|
|
3394
3412
|
if (lastAssistantTotalUsage !== null) {
|
|
3395
3413
|
this.session.contextUsed = lastAssistantTotalUsage;
|
|
3396
3414
|
}
|
|
@@ -3400,7 +3418,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3400
3418
|
update: {
|
|
3401
3419
|
sessionUpdate: "usage_update",
|
|
3402
3420
|
used: lastAssistantTotalUsage,
|
|
3403
|
-
size:
|
|
3421
|
+
size: lastContextWindowSize,
|
|
3404
3422
|
cost: {
|
|
3405
3423
|
amount: message.total_cost_usd,
|
|
3406
3424
|
currency: "USD"
|
|
@@ -3466,7 +3484,16 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3466
3484
|
}
|
|
3467
3485
|
if ("usage" in message.message && message.parent_tool_use_id === null) {
|
|
3468
3486
|
const usage = message.message.usage;
|
|
3469
|
-
lastAssistantTotalUsage = usage.input_tokens + usage.
|
|
3487
|
+
lastAssistantTotalUsage = usage.input_tokens + usage.cache_read_input_tokens + usage.cache_creation_input_tokens;
|
|
3488
|
+
await this.client.sessionUpdate({
|
|
3489
|
+
sessionId: params.sessionId,
|
|
3490
|
+
update: {
|
|
3491
|
+
sessionUpdate: "usage_update",
|
|
3492
|
+
used: lastAssistantTotalUsage,
|
|
3493
|
+
size: lastContextWindowSize,
|
|
3494
|
+
cost: null
|
|
3495
|
+
}
|
|
3496
|
+
});
|
|
3470
3497
|
}
|
|
3471
3498
|
const result = await handleUserAssistantMessage(message, context);
|
|
3472
3499
|
if (result.error) throw result.error;
|
|
@@ -3524,9 +3551,11 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3524
3551
|
await this.session.query.interrupt();
|
|
3525
3552
|
}
|
|
3526
3553
|
async unstable_setSessionModel(params) {
|
|
3527
|
-
|
|
3528
|
-
await this.session.query.setModel(sdkModelId);
|
|
3554
|
+
await this.session.query.setModel(toSdkModelId(params.modelId));
|
|
3529
3555
|
this.session.modelId = params.modelId;
|
|
3556
|
+
this.session.lastContextWindowSize = this.getContextWindowForModel(
|
|
3557
|
+
params.modelId
|
|
3558
|
+
);
|
|
3530
3559
|
this.rebuildEffortConfigOption(params.modelId);
|
|
3531
3560
|
await this.updateConfigOption("model", params.modelId);
|
|
3532
3561
|
return {};
|
|
@@ -3577,6 +3606,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3577
3606
|
const sdkModelId = toSdkModelId(resolvedValue);
|
|
3578
3607
|
await this.session.query.setModel(sdkModelId);
|
|
3579
3608
|
this.session.modelId = resolvedValue;
|
|
3609
|
+
this.session.lastContextWindowSize = this.getContextWindowForModel(resolvedValue);
|
|
3580
3610
|
this.rebuildEffortConfigOption(resolvedValue);
|
|
3581
3611
|
} else if (params.configId === "effort") {
|
|
3582
3612
|
const newEffort = resolvedValue;
|
|
@@ -3737,11 +3767,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3737
3767
|
const modelOptions = await this.getModelConfigOptions();
|
|
3738
3768
|
const resolvedModelId = settingsModel || modelOptions.currentModelId;
|
|
3739
3769
|
session.modelId = resolvedModelId;
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
}
|
|
3770
|
+
session.lastContextWindowSize = this.getContextWindowForModel(resolvedModelId);
|
|
3771
|
+
const resolvedSdkModel = toSdkModelId(resolvedModelId);
|
|
3772
|
+
if (!isResume && resolvedSdkModel !== DEFAULT_MODEL) {
|
|
3773
|
+
await this.session.query.setModel(resolvedSdkModel);
|
|
3745
3774
|
}
|
|
3746
3775
|
const availableModes2 = getAvailableModes();
|
|
3747
3776
|
const modes = {
|