@posthog/agent 2.3.401 → 2.3.403
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/README.md +11 -14
- package/dist/agent.js +1 -7
- package/dist/agent.js.map +1 -1
- package/dist/handoff-checkpoint.d.ts +0 -3
- package/dist/handoff-checkpoint.js +38 -69
- package/dist/handoff-checkpoint.js.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/posthog-api.js +1 -5
- package/dist/posthog-api.js.map +1 -1
- package/dist/resume.d.ts +5 -6
- package/dist/resume.js +2 -41
- package/dist/resume.js.map +1 -1
- package/dist/server/agent-server.d.ts +1 -2
- package/dist/server/agent-server.js +103 -815
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +101 -806
- package/dist/server/bin.cjs.map +1 -1
- package/dist/types.d.ts +2 -13
- package/dist/types.js.map +1 -1
- package/package.json +3 -7
- package/src/acp-extensions.ts +0 -3
- package/src/handoff-checkpoint.test.ts +2 -17
- package/src/handoff-checkpoint.ts +15 -61
- package/src/resume.ts +5 -11
- package/src/sagas/resume-saga.test.ts +27 -77
- package/src/sagas/resume-saga.ts +3 -44
- package/src/sagas/test-fixtures.ts +17 -76
- package/src/server/agent-server.ts +22 -103
- package/src/test/fixtures/api.ts +2 -15
- package/src/types.ts +0 -16
- package/dist/tree-tracker.d.ts +0 -68
- package/dist/tree-tracker.js +0 -6462
- package/dist/tree-tracker.js.map +0 -1
- package/src/sagas/apply-snapshot-saga.test.ts +0 -691
- package/src/sagas/apply-snapshot-saga.ts +0 -114
- package/src/sagas/capture-tree-saga.test.ts +0 -910
- package/src/sagas/capture-tree-saga.ts +0 -165
- package/src/tree-tracker.ts +0 -173
package/dist/resume.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/claude/session/jsonl-hydration.ts","../../shared/src/cloud-prompt.ts","../../shared/src/saga.ts","../src/acp-extensions.ts","../src/sagas/resume-saga.ts","../src/utils/logger.ts","../src/resume.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<boolean> {\n const { posthogAPI, log } = params;\n\n try {\n const jsonlPath = getSessionJsonlPath(params.sessionId, params.cwd);\n try {\n await fs.access(jsonlPath);\n return true;\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 false;\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 false;\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 false;\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 return true;\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 return false;\n }\n}\n","import type { ContentBlock } from \"@agentclientprotocol/sdk\";\n\n/**\n * Wire format prefix for structured cloud prompts.\n * Text-only prompts are sent as plain strings (no prefix) as an optimization.\n * Multi-block prompts (text + attachments) are serialized as `PREFIX + JSON({ blocks })`.\n */\nexport const CLOUD_PROMPT_PREFIX = \"__twig_cloud_prompt_v1__:\";\n\nexport function serializeCloudPrompt(blocks: ContentBlock[]): string {\n if (blocks.length === 1 && blocks[0].type === \"text\") {\n return blocks[0].text.trim();\n }\n\n return `${CLOUD_PROMPT_PREFIX}${JSON.stringify({ blocks })}`;\n}\n\nexport function deserializeCloudPrompt(value: string): ContentBlock[] {\n const trimmed = value.trim();\n if (!trimmed) {\n return [];\n }\n\n if (!trimmed.startsWith(CLOUD_PROMPT_PREFIX)) {\n return [{ type: \"text\", text: trimmed }];\n }\n\n try {\n const parsed = JSON.parse(trimmed.slice(CLOUD_PROMPT_PREFIX.length)) as {\n blocks?: ContentBlock[];\n };\n\n if (Array.isArray(parsed.blocks) && parsed.blocks.length > 0) {\n return parsed.blocks;\n }\n } catch {\n // Fall through to preserve the raw string if the payload is malformed.\n }\n\n return [{ type: \"text\", text: trimmed }];\n}\n\nexport function promptBlocksToText(blocks: ContentBlock[]): string {\n return blocks\n .filter((b): b is ContentBlock & { type: \"text\" } => b.type === \"text\")\n .map((block) => block.text)\n .join(\"\")\n .trim();\n}\n","/**\n * Configuration for a single saga step\n */\nexport interface SagaStep<T> {\n /** Unique name for this step (used in logging) */\n name: string;\n /** The forward action to execute */\n execute: () => Promise<T>;\n /** The rollback action to undo this step (receives the execute result) */\n rollback: (result: T) => Promise<void>;\n}\n\n/**\n * Result of a saga execution\n */\nexport type SagaResult<T, TFailedStep extends string = string> =\n | { success: true; data: T }\n | { success: false; error: string; failedStep: TFailedStep };\n\nexport interface SagaLogger {\n info(message: string, data?: Record<string, unknown>): void;\n debug(message: string, data?: Record<string, unknown>): void;\n error(message: string, data?: Record<string, unknown>): void;\n warn(message: string, data?: Record<string, unknown>): void;\n}\n\nconst consoleLogger: SagaLogger = {\n info: (_message, _data) => {},\n debug: (_message, _data) => {},\n error: (_message, _data) => {},\n warn: (_message, _data) => {},\n};\n\n/**\n * Abstract base class for implementing our Saga pattern.\n *\n * Subclasses implement the `execute` method, using `this.step()` to define\n * each step with its compensating action. If any step throws, all completed\n * steps are automatically rolled back in reverse order.\n *\n * The failed step name is automatically tracked from the step's `name` property.\n *\n * @template TInput - The input type for the saga\n * @template TOutput - The successful output type\n */\nexport abstract class Saga<TInput, TOutput> {\n abstract readonly sagaName: string;\n\n private completedSteps: Array<{\n name: string;\n rollback: () => Promise<void>;\n }> = [];\n private currentStepName = \"unknown\";\n private stepTimings: Array<{ name: string; durationMs: number }> = [];\n protected readonly log: SagaLogger;\n\n constructor(logger?: SagaLogger) {\n this.log = logger ?? consoleLogger;\n }\n\n /**\n * Run the saga with the given input.\n * Returns a discriminated union result - either success with data or failure with error details.\n */\n async run(input: TInput): Promise<SagaResult<TOutput>> {\n this.completedSteps = [];\n this.currentStepName = \"unknown\";\n this.stepTimings = [];\n\n const sagaStart = performance.now();\n\n try {\n const result = await this.execute(input);\n\n const totalDuration = performance.now() - sagaStart;\n this.log.debug(\"Saga completed successfully\", {\n sagaName: this.sagaName,\n stepsCompleted: this.completedSteps.length,\n totalDurationMs: Math.round(totalDuration),\n stepTimings: this.stepTimings,\n });\n\n return { success: true, data: result };\n } catch (error) {\n this.log.error(\"Saga failed, initiating rollback\", {\n sagaName: this.sagaName,\n failedStep: this.currentStepName,\n error: error instanceof Error ? error.message : String(error),\n completedStepTimings: this.stepTimings,\n });\n\n await this.rollback();\n\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n failedStep: this.currentStepName,\n };\n }\n }\n\n /**\n * Implement this method to define the saga's steps.\n * Use `this.step()` to execute each step with its compensating action.\n */\n protected abstract execute(input: TInput): Promise<TOutput>;\n\n /**\n * Execute a step with its rollback action.\n * If the step succeeds, its rollback action is stored for potential rollback.\n * The step name is automatically tracked for error reporting.\n *\n * @param config - Step configuration with name, execute, and rollback functions\n * @returns The result of the execute function\n * @throws Re-throws any error from the execute function (triggers rollback)\n */\n protected async step<T>(config: SagaStep<T>): Promise<T> {\n this.currentStepName = config.name;\n\n const stepStart = performance.now();\n const result = await config.execute();\n const durationMs = Math.round(performance.now() - stepStart);\n\n this.stepTimings.push({ name: config.name, durationMs });\n\n this.completedSteps.push({\n name: config.name,\n rollback: () => config.rollback(result),\n });\n\n return result;\n }\n\n /**\n * Execute a step that doesn't need rollback.\n * Useful for read-only operations or operations that are idempotent.\n * The step name is automatically tracked for error reporting.\n *\n * @param name - Step name for logging and error tracking\n * @param execute - The action to execute\n * @returns The result of the execute function\n */\n protected async readOnlyStep<T>(\n name: string,\n execute: () => Promise<T>,\n ): Promise<T> {\n this.currentStepName = name;\n\n const stepStart = performance.now();\n const result = await execute();\n const durationMs = Math.round(performance.now() - stepStart);\n\n this.stepTimings.push({ name, durationMs });\n return result;\n }\n\n /**\n * Roll back all completed steps in reverse order.\n * Rollback errors are logged but don't stop the rollback of other steps.\n */\n private async rollback(): Promise<void> {\n this.log.info(\"Rolling back saga\", {\n stepsToRollback: this.completedSteps.length,\n });\n\n const stepsReversed = [...this.completedSteps].reverse();\n\n for (const step of stepsReversed) {\n try {\n await step.rollback();\n } catch (error) {\n // Log but continue - we want to attempt all rollbacks\n this.log.error(`Failed to rollback step: ${step.name}`, {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n this.log.info(\"Rollback completed\", {\n stepsAttempted: this.completedSteps.length,\n });\n }\n\n /**\n * Get the number of completed steps (useful for testing)\n */\n protected getCompletedStepCount(): number {\n return this.completedSteps.length;\n }\n}\n","/**\n * PostHog-specific ACP extensions.\n *\n * These follow the ACP extensibility model:\n * - Custom notification methods are prefixed with `_posthog/`\n * - Custom data can be attached via `_meta` fields\n *\n * See: https://agentclientprotocol.com/docs/extensibility\n */\n\n/**\n * Custom notification methods for PostHog-specific events.\n * Used with AgentSideConnection.extNotification() or Client.extNotification()\n */\nexport const POSTHOG_NOTIFICATIONS = {\n /** Git branch was created for a task */\n BRANCH_CREATED: \"_posthog/branch_created\",\n\n /** Task run has started execution */\n RUN_STARTED: \"_posthog/run_started\",\n\n /** Task has completed (success or failure) */\n TASK_COMPLETE: \"_posthog/task_complete\",\n\n /** Agent finished processing a turn (prompt returned, waiting for next input) */\n TURN_COMPLETE: \"_posthog/turn_complete\",\n\n /** Error occurred during task execution */\n ERROR: \"_posthog/error\",\n\n /** Console/log output from the agent */\n CONSOLE: \"_posthog/console\",\n\n /** Maps taskRunId to agent's sessionId and adapter type (for resumption) */\n SDK_SESSION: \"_posthog/sdk_session\",\n\n /** Tree state snapshot captured (git tree hash + file archive) */\n TREE_SNAPSHOT: \"_posthog/tree_snapshot\",\n\n /** Git checkpoint captured for handoff */\n GIT_CHECKPOINT: \"_posthog/git_checkpoint\",\n\n /** Agent mode changed (interactive/background) */\n MODE_CHANGE: \"_posthog/mode_change\",\n\n /** Request to resume a session from previous state */\n SESSION_RESUME: \"_posthog/session/resume\",\n\n /** User message sent from client to agent */\n USER_MESSAGE: \"_posthog/user_message\",\n\n /** Request to cancel current operation */\n CANCEL: \"_posthog/cancel\",\n\n /** Request to close the session */\n CLOSE: \"_posthog/close\",\n\n /** Agent status update (thinking, working, etc.) */\n STATUS: \"_posthog/status\",\n\n /** Structured backend progress notification; events in the same turn group into one card on the client */\n PROGRESS: \"_posthog/progress\",\n\n /** Task-level notification (progress, milestones) */\n TASK_NOTIFICATION: \"_posthog/task_notification\",\n\n /** Marks a boundary for log compaction */\n COMPACT_BOUNDARY: \"_posthog/compact_boundary\",\n\n /** Token usage update for a session turn */\n USAGE_UPDATE: \"_posthog/usage_update\",\n\n /** Response to a relayed permission request (plan approval, question) */\n PERMISSION_RESPONSE: \"_posthog/permission_response\",\n} as const;\n\n/**\n * Custom request methods for PostHog-specific operations that need a response\n * (request/response, not fire-and-forget). Used with\n * ClientSideConnection.extMethod() on the sender and Agent.extMethod() on the\n * receiver.\n */\nexport const POSTHOG_METHODS = {\n /**\n * Client requests a session refresh between turns. Payload may include\n * `mcpServers` to trigger a resume-with-new-options reinit; future fields\n * can extend this without adding new methods. Returns once the refresh has\n * completed so the caller can safely send the next prompt.\n */\n REFRESH_SESSION: \"_posthog/refresh_session\",\n} as const;\n\ntype PosthogNotification =\n (typeof POSTHOG_NOTIFICATIONS)[keyof typeof POSTHOG_NOTIFICATIONS];\n\ntype PosthogMethod = (typeof POSTHOG_METHODS)[keyof typeof POSTHOG_METHODS];\n\n/**\n * Does `method` match `expected`? Shared by notification and method matchers.\n * Handles the `__posthog/` double-prefix that extNotification() can produce.\n */\nfunction matchesExt(method: string | undefined, expected: string): boolean {\n if (!method) return false;\n return method === expected || method === `_${expected}`;\n}\n\n/** Dispatcher check for incoming `extNotification` calls on the agent side. */\nexport function isNotification(\n method: string | undefined,\n expected: PosthogNotification,\n): boolean {\n return matchesExt(method, expected);\n}\n\n/** Dispatcher check for incoming `extMethod` calls on the agent side. */\nexport function isMethod(\n method: string | undefined,\n expected: PosthogMethod,\n): boolean {\n return matchesExt(method, expected);\n}\n","import type { ContentBlock } from \"@agentclientprotocol/sdk\";\nimport { Saga } from \"@posthog/shared\";\nimport { isNotification, POSTHOG_NOTIFICATIONS } from \"../acp-extensions\";\nimport type { PostHogAPIClient } from \"../posthog-api\";\nimport type {\n DeviceInfo,\n GitCheckpointEvent,\n StoredNotification,\n TreeSnapshotEvent,\n} from \"../types\";\nimport type { Logger } from \"../utils/logger\";\n\nexport interface ConversationTurn {\n role: \"user\" | \"assistant\";\n content: ContentBlock[];\n toolCalls?: ToolCallInfo[];\n}\n\nexport interface ToolCallInfo {\n toolCallId: string;\n toolName: string;\n input: unknown;\n result?: unknown;\n}\n\nexport interface ResumeInput {\n taskId: string;\n runId: string;\n repositoryPath?: string;\n apiClient: PostHogAPIClient;\n logger?: Logger;\n}\n\nexport interface ResumeOutput {\n conversation: ConversationTurn[];\n latestSnapshot: TreeSnapshotEvent | null;\n latestGitCheckpoint: GitCheckpointEvent | null;\n interrupted: boolean;\n lastDevice?: DeviceInfo;\n logEntryCount: number;\n}\n\nexport class ResumeSaga extends Saga<ResumeInput, ResumeOutput> {\n readonly sagaName = \"ResumeSaga\";\n\n protected async execute(input: ResumeInput): Promise<ResumeOutput> {\n const { taskId, runId, apiClient } = input;\n\n // Step 1: Fetch task run (read-only)\n const taskRun = await this.readOnlyStep(\"fetch_task_run\", () =>\n apiClient.getTaskRun(taskId, runId),\n );\n\n if (!taskRun.log_url) {\n this.log.info(\"No log URL found, starting fresh\");\n return this.emptyResult();\n }\n\n // Step 2: Fetch log entries (read-only)\n const entries = await this.readOnlyStep(\"fetch_logs\", () =>\n apiClient.fetchTaskRunLogs(taskRun),\n );\n\n if (entries.length === 0) {\n this.log.info(\"No log entries found, starting fresh\");\n return this.emptyResult();\n }\n\n this.log.info(\"Fetched log entries\", { count: entries.length });\n\n // Step 3: Find latest snapshot (read-only, pure computation)\n const latestSnapshot = await this.readOnlyStep(\"find_snapshot\", () =>\n Promise.resolve(this.findLatestTreeSnapshot(entries)),\n );\n\n const latestGitCheckpoint = await this.readOnlyStep(\n \"find_git_checkpoint\",\n () => Promise.resolve(this.findLatestGitCheckpoint(entries)),\n );\n\n // Step 4: Apply snapshot if present (wrapped in step for consistent logging)\n if (latestSnapshot) {\n this.log.info(\"Found tree snapshot\", {\n treeHash: latestSnapshot.treeHash,\n hasArchiveUrl: !!latestSnapshot.archiveUrl,\n changes: latestSnapshot.changes?.length ?? 0,\n });\n }\n\n if (latestGitCheckpoint) {\n this.log.info(\"Found git checkpoint\", {\n checkpointId: latestGitCheckpoint.checkpointId,\n branch: latestGitCheckpoint.branch,\n });\n }\n\n const conversation = await this.readOnlyStep(\"rebuild_conversation\", () =>\n Promise.resolve(this.rebuildConversation(entries)),\n );\n\n // Step 6: Find device info (read-only, pure computation)\n const lastDevice = await this.readOnlyStep(\"find_device\", () =>\n Promise.resolve(this.findLastDeviceInfo(entries)),\n );\n\n this.log.info(\"Resume state rebuilt\", {\n turns: conversation.length,\n hasSnapshot: !!latestSnapshot,\n hasGitCheckpoint: !!latestGitCheckpoint,\n interrupted: latestSnapshot?.interrupted ?? false,\n });\n\n return {\n conversation,\n latestSnapshot,\n latestGitCheckpoint,\n interrupted: latestSnapshot?.interrupted ?? false,\n lastDevice,\n logEntryCount: entries.length,\n };\n }\n\n private emptyResult(): ResumeOutput {\n return {\n conversation: [],\n latestSnapshot: null,\n latestGitCheckpoint: null,\n interrupted: false,\n logEntryCount: 0,\n };\n }\n\n private findLatestTreeSnapshot(\n entries: StoredNotification[],\n ): TreeSnapshotEvent | null {\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n if (\n isNotification(\n entry.notification?.method,\n POSTHOG_NOTIFICATIONS.TREE_SNAPSHOT,\n )\n ) {\n const params = entry.notification.params as\n | TreeSnapshotEvent\n | undefined;\n if (params?.treeHash) {\n return params;\n }\n }\n }\n return null;\n }\n\n private findLatestGitCheckpoint(\n entries: StoredNotification[],\n ): GitCheckpointEvent | null {\n const sdkPrefixedMethod = `_${POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT}`;\n\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n const method = entry.notification?.method;\n if (\n method === sdkPrefixedMethod ||\n method === POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT\n ) {\n const params = entry.notification?.params as\n | GitCheckpointEvent\n | undefined;\n if (params?.checkpointId && params?.checkpointRef) {\n return params;\n }\n }\n }\n return null;\n }\n\n private findLastDeviceInfo(\n entries: StoredNotification[],\n ): DeviceInfo | undefined {\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n const params = entry.notification?.params as\n | { device?: DeviceInfo }\n | undefined;\n if (params?.device) {\n return params.device;\n }\n }\n return undefined;\n }\n\n private rebuildConversation(\n entries: StoredNotification[],\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 Record<string, unknown>;\n const sessionUpdate = update.sessionUpdate as string;\n\n switch (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 as ContentBlock | ContentBlock[];\n const contentArray = Array.isArray(content) ? content : [content];\n turns.push({\n role: \"user\",\n content: contentArray,\n });\n break;\n }\n\n case \"agent_message\": {\n const content = update.content as ContentBlock | undefined;\n if (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 \"agent_message_chunk\": {\n // Backward compatibility with older logs that have individual chunks\n const content = update.content as ContentBlock | undefined;\n if (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 as Record<string, unknown>)\n ?.claudeCode as Record<string, unknown> | undefined;\n if (meta) {\n const toolCallId = meta.toolCallId as string | undefined;\n const toolName = meta.toolName as string | undefined;\n const toolInput = meta.toolInput;\n const toolResponse = meta.toolResponse;\n\n if (toolCallId && toolName) {\n let toolCall = currentToolCalls.find(\n (tc) => tc.toolCallId === toolCallId,\n );\n if (!toolCall) {\n toolCall = {\n toolCallId,\n toolName,\n input: toolInput,\n };\n currentToolCalls.push(toolCall);\n }\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 as Record<string, unknown>)\n ?.claudeCode as Record<string, unknown> | undefined;\n if (meta) {\n const toolCallId = meta.toolCallId as string | undefined;\n const toolResponse = meta.toolResponse;\n\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}\n","import type { LogLevel as LogLevelType, OnLogCallback } from \"../types\";\n\nexport interface LoggerConfig {\n debug?: boolean;\n prefix?: string;\n scope?: string;\n onLog?: OnLogCallback;\n}\n\nexport class Logger {\n private debugEnabled: boolean;\n private prefix: string;\n private scope: string;\n private onLog?: OnLogCallback;\n\n constructor(config: LoggerConfig = {}) {\n this.debugEnabled = config.debug ?? false;\n this.prefix = config.prefix ?? \"[PostHog Agent]\";\n this.scope = config.scope ?? \"agent\";\n this.onLog = config.onLog;\n }\n\n private formatMessage(\n level: string,\n message: string,\n data?: unknown,\n ): string {\n const timestamp = new Date().toISOString();\n const base = `${timestamp} ${this.prefix} [${level}] ${message}`;\n\n if (data !== undefined) {\n return `${base} ${JSON.stringify(data, null, 2)}`;\n }\n\n return base;\n }\n\n private emitLog(level: LogLevelType, message: string, data?: unknown) {\n if (this.onLog) {\n this.onLog(level, this.scope, message, data);\n return;\n }\n\n const shouldLog = this.debugEnabled || level === \"error\";\n\n if (shouldLog) {\n console[level](this.formatMessage(level.toLowerCase(), message, data));\n }\n }\n\n error(message: string, error?: Error | unknown) {\n const data =\n error instanceof Error\n ? { message: error.message, stack: error.stack }\n : error;\n\n this.emitLog(\"error\", message, data);\n }\n\n warn(message: string, data?: unknown) {\n this.emitLog(\"warn\", message, data);\n }\n\n info(message: string, data?: unknown) {\n this.emitLog(\"info\", message, data);\n }\n\n debug(message: string, data?: unknown) {\n this.emitLog(\"debug\", message, data);\n }\n\n child(childPrefix: string): Logger {\n return new Logger({\n debug: this.debugEnabled,\n prefix: `${this.prefix} [${childPrefix}]`,\n scope: `${this.scope}:${childPrefix}`,\n onLog: this.onLog,\n });\n }\n}\n","/**\n * Resume - Restore agent state from persisted log\n *\n * Handles resuming a task from any point:\n * - Fetches log via the PostHog API\n * - Finds latest tree_snapshot event\n * - Rebuilds conversation from log events\n * - Restores working tree from snapshot\n *\n * Uses Saga pattern for atomic operations with clear success/failure tracking.\n *\n * The log is the single source of truth for:\n * - Conversation history (user_message, agent_message_chunk, tool_call, tool_result)\n * - Working tree state (tree_snapshot events)\n * - Session metadata (device info, mode changes)\n */\n\nimport type { ContentBlock } from \"@agentclientprotocol/sdk\";\nimport { selectRecentTurns } from \"./adapters/claude/session/jsonl-hydration\";\nimport type { PostHogAPIClient } from \"./posthog-api\";\nimport { ResumeSaga } from \"./sagas/resume-saga\";\nimport type {\n DeviceInfo,\n GitCheckpointEvent,\n TreeSnapshotEvent,\n} from \"./types\";\nimport { Logger } from \"./utils/logger\";\n\nexport interface ResumeState {\n conversation: ConversationTurn[];\n latestSnapshot: TreeSnapshotEvent | null;\n latestGitCheckpoint: GitCheckpointEvent | null;\n interrupted: boolean;\n lastDevice?: DeviceInfo;\n logEntryCount: number;\n}\n\nexport interface ConversationTurn {\n role: \"user\" | \"assistant\";\n content: ContentBlock[];\n toolCalls?: ToolCallInfo[];\n}\n\nexport interface ToolCallInfo {\n toolCallId: string;\n toolName: string;\n input: unknown;\n result?: unknown;\n}\n\nexport interface ResumeConfig {\n taskId: string;\n runId: string;\n repositoryPath?: string;\n apiClient: PostHogAPIClient;\n logger?: Logger;\n}\n\n/**\n * Resume a task from its persisted log.\n * Returns the rebuilt state for the agent to continue from.\n * Snapshot and checkpoint application happens in the agent server after SSE connects.\n */\nexport async function resumeFromLog(\n config: ResumeConfig,\n): Promise<ResumeState> {\n const logger =\n config.logger || new Logger({ debug: false, prefix: \"[Resume]\" });\n\n logger.info(\"Resuming from log\", {\n taskId: config.taskId,\n runId: config.runId,\n });\n\n const saga = new ResumeSaga(logger);\n\n const result = await saga.run({\n taskId: config.taskId,\n runId: config.runId,\n repositoryPath: config.repositoryPath,\n apiClient: config.apiClient,\n logger,\n });\n\n if (!result.success) {\n logger.error(\"Failed to resume from log\", {\n error: result.error,\n failedStep: result.failedStep,\n });\n throw new Error(\n `Failed to resume at step '${result.failedStep}': ${result.error}`,\n );\n }\n\n return {\n conversation: result.data.conversation as ConversationTurn[],\n latestSnapshot: result.data.latestSnapshot,\n latestGitCheckpoint: result.data.latestGitCheckpoint,\n interrupted: result.data.interrupted,\n lastDevice: result.data.lastDevice,\n logEntryCount: result.data.logEntryCount,\n };\n}\n\n/**\n * Convert resumed conversation back to API format for continuation.\n */\nexport function conversationToPromptHistory(\n conversation: ConversationTurn[],\n): Array<{ role: \"user\" | \"assistant\"; content: ContentBlock[] }> {\n return conversation.map((turn) => ({\n role: turn.role,\n content: turn.content,\n }));\n}\n\nconst RESUME_HISTORY_TOKEN_BUDGET = 50_000;\nconst TOOL_RESULT_MAX_CHARS = 2000;\n\nconst RESUME_CONTEXT_MARKERS = [\n \"You are resuming a previous conversation\",\n \"Here is the conversation history from the\",\n \"Continue from where you left off\",\n];\n\nfunction isResumeContextTurn(turn: ConversationTurn): boolean {\n if (turn.role !== \"user\") return false;\n const text = turn.content\n .filter((b) => b.type === \"text\")\n .map((b) => (b as { type: \"text\"; text: string }).text)\n .join(\"\");\n return RESUME_CONTEXT_MARKERS.some((marker) => text.includes(marker));\n}\n\nexport function formatConversationForResume(\n conversation: ConversationTurn[],\n): string {\n const filtered = conversation.filter((turn) => !isResumeContextTurn(turn));\n const selected = selectRecentTurns(filtered, RESUME_HISTORY_TOKEN_BUDGET);\n const parts: string[] = [];\n\n if (selected.length < filtered.length) {\n parts.push(\n `*(${filtered.length - selected.length} earlier turns omitted)*`,\n );\n }\n\n for (const turn of selected) {\n const role = turn.role === \"user\" ? \"User\" : \"Assistant\";\n\n const textParts = turn.content\n .filter((block) => block.type === \"text\")\n .map((block) => (block as { type: \"text\"; text: string }).text);\n\n if (textParts.length > 0) {\n parts.push(`**${role}**: ${textParts.join(\"\\n\")}`);\n }\n\n if (turn.toolCalls?.length) {\n const toolSummary = turn.toolCalls\n .map((tc) => {\n let resultStr = \"\";\n if (tc.result !== undefined) {\n const raw =\n typeof tc.result === \"string\"\n ? tc.result\n : JSON.stringify(tc.result);\n resultStr =\n raw.length > TOOL_RESULT_MAX_CHARS\n ? ` → ${raw.substring(0, TOOL_RESULT_MAX_CHARS)}...(truncated)`\n : ` → ${raw}`;\n }\n return ` - ${tc.toolName}${resultStr}`;\n })\n .join(\"\\n\");\n parts.push(`**${role} (tools)**:\\n${toolSummary}`);\n }\n }\n\n return parts.join(\"\\n\\n\");\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AAC3B,YAAY,QAAQ;AACpB,YAAY,QAAQ;AACpB,YAAY,UAAU;AA0LtB,IAAM,kBAAkB;AACxB,IAAM,qBAAqB;AAG3B,SAAS,mBAAmB,MAAgC;AAC1D,MAAI,QAAQ;AACZ,aAAW,SAAS,KAAK,SAAS;AAChC,QAAI,UAAU,SAAS,OAAO,MAAM,SAAS,UAAU;AACrD,eAAS,MAAM,KAAK;AAAA,IACtB;AAAA,EACF;AACA,MAAI,KAAK,WAAW;AAClB,eAAW,MAAM,KAAK,WAAW;AAC/B,eAAS,KAAK,UAAU,GAAG,SAAS,EAAE,EAAE;AACxC,UAAI,GAAG,WAAW,QAAW;AAC3B,iBACE,OAAO,GAAG,WAAW,WACjB,GAAG,OAAO,SACV,KAAK,UAAU,GAAG,MAAM,EAAE;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AACA,SAAO,KAAK,KAAK,QAAQ,eAAe;AAC1C;AAEO,SAAS,kBACd,OACA,YAAY,oBACQ;AACpB,MAAI,SAAS;AACb,MAAI,aAAa,MAAM;AAEvB,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,UAAM,OAAO,mBAAmB,MAAM,CAAC,CAAC;AACxC,QAAI,OAAO,OAAQ;AACnB,cAAU;AACV,iBAAa;AAAA,EACf;AAGA,SAAO,aAAa,MAAM,UAAU,MAAM,UAAU,EAAE,SAAS,QAAQ;AACrE;AAAA,EACF;AAEA,SAAO,MAAM,MAAM,UAAU;AAC/B;;;AEhNA,IAAM,gBAA4B;EAChC,MAAM,CAAC,UAAU,UAAU;EAAC;EAC5B,OAAO,CAAC,UAAU,UAAU;EAAC;EAC7B,OAAO,CAAC,UAAU,UAAU;EAAC;EAC7B,MAAM,CAAC,UAAU,UAAU;EAAC;AAC9B;AAcO,IAAe,OAAf,MAAqC;EAGlC,iBAGH,CAAC;EACE,kBAAkB;EAClB,cAA2D,CAAC;EACjD;EAEnB,YAAY,QAAqB;AAC/B,SAAK,MAAM,UAAU;EACvB;;;;;EAMA,MAAM,IAAI,OAA6C;AACrD,SAAK,iBAAiB,CAAC;AACvB,SAAK,kBAAkB;AACvB,SAAK,cAAc,CAAC;AAEpB,UAAM,YAAY,YAAY,IAAI;AAElC,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,QAAQ,KAAK;AAEvC,YAAM,gBAAgB,YAAY,IAAI,IAAI;AAC1C,WAAK,IAAI,MAAM,+BAA+B;QAC5C,UAAU,KAAK;QACf,gBAAgB,KAAK,eAAe;QACpC,iBAAiB,KAAK,MAAM,aAAa;QACzC,aAAa,KAAK;MACpB,CAAC;AAED,aAAO,EAAE,SAAS,MAAM,MAAM,OAAO;IACvC,SAAS,OAAO;AACd,WAAK,IAAI,MAAM,oCAAoC;QACjD,UAAU,KAAK;QACf,YAAY,KAAK;QACjB,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;QAC5D,sBAAsB,KAAK;MAC7B,CAAC;AAED,YAAM,KAAK,SAAS;AAEpB,aAAO;QACL,SAAS;QACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;QAC5D,YAAY,KAAK;MACnB;IACF;EACF;;;;;;;;;;EAiBA,MAAgB,KAAQ,QAAiC;AACvD,SAAK,kBAAkB,OAAO;AAE9B,UAAM,YAAY,YAAY,IAAI;AAClC,UAAM,SAAS,MAAM,OAAO,QAAQ;AACpC,UAAM,aAAa,KAAK,MAAM,YAAY,IAAI,IAAI,SAAS;AAE3D,SAAK,YAAY,KAAK,EAAE,MAAM,OAAO,MAAM,WAAW,CAAC;AAEvD,SAAK,eAAe,KAAK;MACvB,MAAM,OAAO;MACb,UAAU,MAAM,OAAO,SAAS,MAAM;IACxC,CAAC;AAED,WAAO;EACT;;;;;;;;;;EAWA,MAAgB,aACd,MACA,SACY;AACZ,SAAK,kBAAkB;AAEvB,UAAM,YAAY,YAAY,IAAI;AAClC,UAAM,SAAS,MAAM,QAAQ;AAC7B,UAAM,aAAa,KAAK,MAAM,YAAY,IAAI,IAAI,SAAS;AAE3D,SAAK,YAAY,KAAK,EAAE,MAAM,WAAW,CAAC;AAC1C,WAAO;EACT;;;;;EAMA,MAAc,WAA0B;AACtC,SAAK,IAAI,KAAK,qBAAqB;MACjC,iBAAiB,KAAK,eAAe;IACvC,CAAC;AAED,UAAM,gBAAgB,CAAC,GAAG,KAAK,cAAc,EAAE,QAAQ;AAEvD,eAAW,QAAQ,eAAe;AAChC,UAAI;AACF,cAAM,KAAK,SAAS;MACtB,SAAS,OAAO;AAEd,aAAK,IAAI,MAAM,4BAA4B,KAAK,IAAI,IAAI;UACtD,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;QAC9D,CAAC;MACH;IACF;AAEA,SAAK,IAAI,KAAK,sBAAsB;MAClC,gBAAgB,KAAK,eAAe;IACtC,CAAC;EACH;;;;EAKU,wBAAgC;AACxC,WAAO,KAAK,eAAe;EAC7B;AACF;;;AC/KO,IAAM,wBAAwB;AAAA;AAAA,EAEnC,gBAAgB;AAAA;AAAA,EAGhB,aAAa;AAAA;AAAA,EAGb,eAAe;AAAA;AAAA,EAGf,eAAe;AAAA;AAAA,EAGf,OAAO;AAAA;AAAA,EAGP,SAAS;AAAA;AAAA,EAGT,aAAa;AAAA;AAAA,EAGb,eAAe;AAAA;AAAA,EAGf,gBAAgB;AAAA;AAAA,EAGhB,aAAa;AAAA;AAAA,EAGb,gBAAgB;AAAA;AAAA,EAGhB,cAAc;AAAA;AAAA,EAGd,QAAQ;AAAA;AAAA,EAGR,OAAO;AAAA;AAAA,EAGP,QAAQ;AAAA;AAAA,EAGR,UAAU;AAAA;AAAA,EAGV,mBAAmB;AAAA;AAAA,EAGnB,kBAAkB;AAAA;AAAA,EAGlB,cAAc;AAAA;AAAA,EAGd,qBAAqB;AACvB;AA2BA,SAAS,WAAW,QAA4B,UAA2B;AACzE,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,WAAW,YAAY,WAAW,IAAI,QAAQ;AACvD;AAGO,SAAS,eACd,QACA,UACS;AACT,SAAO,WAAW,QAAQ,QAAQ;AACpC;;;ACtEO,IAAM,aAAN,cAAyB,KAAgC;AAAA,EACrD,WAAW;AAAA,EAEpB,MAAgB,QAAQ,OAA2C;AACjE,UAAM,EAAE,QAAQ,OAAO,UAAU,IAAI;AAGrC,UAAM,UAAU,MAAM,KAAK;AAAA,MAAa;AAAA,MAAkB,MACxD,UAAU,WAAW,QAAQ,KAAK;AAAA,IACpC;AAEA,QAAI,CAAC,QAAQ,SAAS;AACpB,WAAK,IAAI,KAAK,kCAAkC;AAChD,aAAO,KAAK,YAAY;AAAA,IAC1B;AAGA,UAAM,UAAU,MAAM,KAAK;AAAA,MAAa;AAAA,MAAc,MACpD,UAAU,iBAAiB,OAAO;AAAA,IACpC;AAEA,QAAI,QAAQ,WAAW,GAAG;AACxB,WAAK,IAAI,KAAK,sCAAsC;AACpD,aAAO,KAAK,YAAY;AAAA,IAC1B;AAEA,SAAK,IAAI,KAAK,uBAAuB,EAAE,OAAO,QAAQ,OAAO,CAAC;AAG9D,UAAM,iBAAiB,MAAM,KAAK;AAAA,MAAa;AAAA,MAAiB,MAC9D,QAAQ,QAAQ,KAAK,uBAAuB,OAAO,CAAC;AAAA,IACtD;AAEA,UAAM,sBAAsB,MAAM,KAAK;AAAA,MACrC;AAAA,MACA,MAAM,QAAQ,QAAQ,KAAK,wBAAwB,OAAO,CAAC;AAAA,IAC7D;AAGA,QAAI,gBAAgB;AAClB,WAAK,IAAI,KAAK,uBAAuB;AAAA,QACnC,UAAU,eAAe;AAAA,QACzB,eAAe,CAAC,CAAC,eAAe;AAAA,QAChC,SAAS,eAAe,SAAS,UAAU;AAAA,MAC7C,CAAC;AAAA,IACH;AAEA,QAAI,qBAAqB;AACvB,WAAK,IAAI,KAAK,wBAAwB;AAAA,QACpC,cAAc,oBAAoB;AAAA,QAClC,QAAQ,oBAAoB;AAAA,MAC9B,CAAC;AAAA,IACH;AAEA,UAAM,eAAe,MAAM,KAAK;AAAA,MAAa;AAAA,MAAwB,MACnE,QAAQ,QAAQ,KAAK,oBAAoB,OAAO,CAAC;AAAA,IACnD;AAGA,UAAM,aAAa,MAAM,KAAK;AAAA,MAAa;AAAA,MAAe,MACxD,QAAQ,QAAQ,KAAK,mBAAmB,OAAO,CAAC;AAAA,IAClD;AAEA,SAAK,IAAI,KAAK,wBAAwB;AAAA,MACpC,OAAO,aAAa;AAAA,MACpB,aAAa,CAAC,CAAC;AAAA,MACf,kBAAkB,CAAC,CAAC;AAAA,MACpB,aAAa,gBAAgB,eAAe;AAAA,IAC9C,CAAC;AAED,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,gBAAgB,eAAe;AAAA,MAC5C;AAAA,MACA,eAAe,QAAQ;AAAA,IACzB;AAAA,EACF;AAAA,EAEQ,cAA4B;AAClC,WAAO;AAAA,MACL,cAAc,CAAC;AAAA,MACf,gBAAgB;AAAA,MAChB,qBAAqB;AAAA,MACrB,aAAa;AAAA,MACb,eAAe;AAAA,IACjB;AAAA,EACF;AAAA,EAEQ,uBACN,SAC0B;AAC1B,aAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC5C,YAAM,QAAQ,QAAQ,CAAC;AACvB,UACE;AAAA,QACE,MAAM,cAAc;AAAA,QACpB,sBAAsB;AAAA,MACxB,GACA;AACA,cAAM,SAAS,MAAM,aAAa;AAGlC,YAAI,QAAQ,UAAU;AACpB,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,wBACN,SAC2B;AAC3B,UAAM,oBAAoB,IAAI,sBAAsB,cAAc;AAElE,aAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC5C,YAAM,QAAQ,QAAQ,CAAC;AACvB,YAAM,SAAS,MAAM,cAAc;AACnC,UACE,WAAW,qBACX,WAAW,sBAAsB,gBACjC;AACA,cAAM,SAAS,MAAM,cAAc;AAGnC,YAAI,QAAQ,gBAAgB,QAAQ,eAAe;AACjD,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,mBACN,SACwB;AACxB,aAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC5C,YAAM,QAAQ,QAAQ,CAAC;AACvB,YAAM,SAAS,MAAM,cAAc;AAGnC,UAAI,QAAQ,QAAQ;AAClB,eAAO,OAAO;AAAA,MAChB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,oBACN,SACoB;AACpB,UAAM,QAA4B,CAAC;AACnC,QAAI,0BAA0C,CAAC;AAC/C,QAAI,mBAAmC,CAAC;AAExC,eAAW,SAAS,SAAS;AAC3B,YAAM,SAAS,MAAM,cAAc;AACnC,YAAM,SAAS,MAAM,cAAc;AAEnC,UAAI,WAAW,oBAAoB,QAAQ,QAAQ;AACjD,cAAM,SAAS,OAAO;AACtB,cAAM,gBAAgB,OAAO;AAE7B,gBAAQ,eAAe;AAAA,UACrB,KAAK;AAAA,UACL,KAAK,sBAAsB;AACzB,gBACE,wBAAwB,SAAS,KACjC,iBAAiB,SAAS,GAC1B;AACA,oBAAM,KAAK;AAAA,gBACT,MAAM;AAAA,gBACN,SAAS;AAAA,gBACT,WACE,iBAAiB,SAAS,IAAI,mBAAmB;AAAA,cACrD,CAAC;AACD,wCAA0B,CAAC;AAC3B,iCAAmB,CAAC;AAAA,YACtB;AAEA,kBAAM,UAAU,OAAO;AACvB,kBAAM,eAAe,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,OAAO;AAChE,kBAAM,KAAK;AAAA,cACT,MAAM;AAAA,cACN,SAAS;AAAA,YACX,CAAC;AACD;AAAA,UACF;AAAA,UAEA,KAAK,iBAAiB;AACpB,kBAAM,UAAU,OAAO;AACvB,gBAAI,SAAS;AACX,kBACE,QAAQ,SAAS,UACjB,wBAAwB,SAAS,KACjC,wBAAwB,wBAAwB,SAAS,CAAC,EACvD,SAAS,QACZ;AACA,sBAAM,YAAY,wBAChB,wBAAwB,SAAS,CACnC;AACA,0BAAU,QACR,QACA;AAAA,cACJ,OAAO;AACL,wCAAwB,KAAK,OAAO;AAAA,cACtC;AAAA,YACF;AACA;AAAA,UACF;AAAA,UAEA,KAAK,uBAAuB;AAE1B,kBAAM,UAAU,OAAO;AACvB,gBAAI,SAAS;AACX,kBACE,QAAQ,SAAS,UACjB,wBAAwB,SAAS,KACjC,wBAAwB,wBAAwB,SAAS,CAAC,EACvD,SAAS,QACZ;AACA,sBAAM,YAAY,wBAChB,wBAAwB,SAAS,CACnC;AACA,0BAAU,QACR,QACA;AAAA,cACJ,OAAO;AACL,wCAAwB,KAAK,OAAO;AAAA,cACtC;AAAA,YACF;AACA;AAAA,UACF;AAAA,UAEA,KAAK;AAAA,UACL,KAAK,oBAAoB;AACvB,kBAAM,OAAQ,OAAO,OACjB;AACJ,gBAAI,MAAM;AACR,oBAAM,aAAa,KAAK;AACxB,oBAAM,WAAW,KAAK;AACtB,oBAAM,YAAY,KAAK;AACvB,oBAAM,eAAe,KAAK;AAE1B,kBAAI,cAAc,UAAU;AAC1B,oBAAI,WAAW,iBAAiB;AAAA,kBAC9B,CAAC,OAAO,GAAG,eAAe;AAAA,gBAC5B;AACA,oBAAI,CAAC,UAAU;AACb,6BAAW;AAAA,oBACT;AAAA,oBACA;AAAA,oBACA,OAAO;AAAA,kBACT;AACA,mCAAiB,KAAK,QAAQ;AAAA,gBAChC;AAEA,oBAAI,iBAAiB,QAAW;AAC9B,2BAAS,SAAS;AAAA,gBACpB;AAAA,cACF;AAAA,YACF;AACA;AAAA,UACF;AAAA,UAEA,KAAK,eAAe;AAClB,kBAAM,OAAQ,OAAO,OACjB;AACJ,gBAAI,MAAM;AACR,oBAAM,aAAa,KAAK;AACxB,oBAAM,eAAe,KAAK;AAE1B,kBAAI,YAAY;AACd,sBAAM,WAAW,iBAAiB;AAAA,kBAChC,CAAC,OAAO,GAAG,eAAe;AAAA,gBAC5B;AACA,oBAAI,YAAY,iBAAiB,QAAW;AAC1C,2BAAS,SAAS;AAAA,gBACpB;AAAA,cACF;AAAA,YACF;AACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,wBAAwB,SAAS,KAAK,iBAAiB,SAAS,GAAG;AACrE,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,SAAS;AAAA,QACT,WAAW,iBAAiB,SAAS,IAAI,mBAAmB;AAAA,MAC9D,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AACF;;;AC5UO,IAAM,SAAN,MAAM,QAAO;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,SAAuB,CAAC,GAAG;AACrC,SAAK,eAAe,OAAO,SAAS;AACpC,SAAK,SAAS,OAAO,UAAU;AAC/B,SAAK,QAAQ,OAAO,SAAS;AAC7B,SAAK,QAAQ,OAAO;AAAA,EACtB;AAAA,EAEQ,cACN,OACA,SACA,MACQ;AACR,UAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,UAAM,OAAO,GAAG,SAAS,IAAI,KAAK,MAAM,KAAK,KAAK,KAAK,OAAO;AAE9D,QAAI,SAAS,QAAW;AACtB,aAAO,GAAG,IAAI,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,IACjD;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,QAAQ,OAAqB,SAAiB,MAAgB;AACpE,QAAI,KAAK,OAAO;AACd,WAAK,MAAM,OAAO,KAAK,OAAO,SAAS,IAAI;AAC3C;AAAA,IACF;AAEA,UAAM,YAAY,KAAK,gBAAgB,UAAU;AAEjD,QAAI,WAAW;AACb,cAAQ,KAAK,EAAE,KAAK,cAAc,MAAM,YAAY,GAAG,SAAS,IAAI,CAAC;AAAA,IACvE;AAAA,EACF;AAAA,EAEA,MAAM,SAAiB,OAAyB;AAC9C,UAAM,OACJ,iBAAiB,QACb,EAAE,SAAS,MAAM,SAAS,OAAO,MAAM,MAAM,IAC7C;AAEN,SAAK,QAAQ,SAAS,SAAS,IAAI;AAAA,EACrC;AAAA,EAEA,KAAK,SAAiB,MAAgB;AACpC,SAAK,QAAQ,QAAQ,SAAS,IAAI;AAAA,EACpC;AAAA,EAEA,KAAK,SAAiB,MAAgB;AACpC,SAAK,QAAQ,QAAQ,SAAS,IAAI;AAAA,EACpC;AAAA,EAEA,MAAM,SAAiB,MAAgB;AACrC,SAAK,QAAQ,SAAS,SAAS,IAAI;AAAA,EACrC;AAAA,EAEA,MAAM,aAA6B;AACjC,WAAO,IAAI,QAAO;AAAA,MAChB,OAAO,KAAK;AAAA,MACZ,QAAQ,GAAG,KAAK,MAAM,KAAK,WAAW;AAAA,MACtC,OAAO,GAAG,KAAK,KAAK,IAAI,WAAW;AAAA,MACnC,OAAO,KAAK;AAAA,IACd,CAAC;AAAA,EACH;AACF;;;AChBA,eAAsB,cACpB,QACsB;AACtB,QAAM,SACJ,OAAO,UAAU,IAAI,OAAO,EAAE,OAAO,OAAO,QAAQ,WAAW,CAAC;AAElE,SAAO,KAAK,qBAAqB;AAAA,IAC/B,QAAQ,OAAO;AAAA,IACf,OAAO,OAAO;AAAA,EAChB,CAAC;AAED,QAAM,OAAO,IAAI,WAAW,MAAM;AAElC,QAAM,SAAS,MAAM,KAAK,IAAI;AAAA,IAC5B,QAAQ,OAAO;AAAA,IACf,OAAO,OAAO;AAAA,IACd,gBAAgB,OAAO;AAAA,IACvB,WAAW,OAAO;AAAA,IAClB;AAAA,EACF,CAAC;AAED,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,MAAM,6BAA6B;AAAA,MACxC,OAAO,OAAO;AAAA,MACd,YAAY,OAAO;AAAA,IACrB,CAAC;AACD,UAAM,IAAI;AAAA,MACR,6BAA6B,OAAO,UAAU,MAAM,OAAO,KAAK;AAAA,IAClE;AAAA,EACF;AAEA,SAAO;AAAA,IACL,cAAc,OAAO,KAAK;AAAA,IAC1B,gBAAgB,OAAO,KAAK;AAAA,IAC5B,qBAAqB,OAAO,KAAK;AAAA,IACjC,aAAa,OAAO,KAAK;AAAA,IACzB,YAAY,OAAO,KAAK;AAAA,IACxB,eAAe,OAAO,KAAK;AAAA,EAC7B;AACF;AAKO,SAAS,4BACd,cACgE;AAChE,SAAO,aAAa,IAAI,CAAC,UAAU;AAAA,IACjC,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,EAChB,EAAE;AACJ;AAEA,IAAM,8BAA8B;AACpC,IAAM,wBAAwB;AAE9B,IAAM,yBAAyB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,oBAAoB,MAAiC;AAC5D,MAAI,KAAK,SAAS,OAAQ,QAAO;AACjC,QAAM,OAAO,KAAK,QACf,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM,EAC/B,IAAI,CAAC,MAAO,EAAqC,IAAI,EACrD,KAAK,EAAE;AACV,SAAO,uBAAuB,KAAK,CAAC,WAAW,KAAK,SAAS,MAAM,CAAC;AACtE;AAEO,SAAS,4BACd,cACQ;AACR,QAAM,WAAW,aAAa,OAAO,CAAC,SAAS,CAAC,oBAAoB,IAAI,CAAC;AACzE,QAAM,WAAW,kBAAkB,UAAU,2BAA2B;AACxE,QAAM,QAAkB,CAAC;AAEzB,MAAI,SAAS,SAAS,SAAS,QAAQ;AACrC,UAAM;AAAA,MACJ,KAAK,SAAS,SAAS,SAAS,MAAM;AAAA,IACxC;AAAA,EACF;AAEA,aAAW,QAAQ,UAAU;AAC3B,UAAM,OAAO,KAAK,SAAS,SAAS,SAAS;AAE7C,UAAM,YAAY,KAAK,QACpB,OAAO,CAAC,UAAU,MAAM,SAAS,MAAM,EACvC,IAAI,CAAC,UAAW,MAAyC,IAAI;AAEhE,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,KAAK,KAAK,IAAI,OAAO,UAAU,KAAK,IAAI,CAAC,EAAE;AAAA,IACnD;AAEA,QAAI,KAAK,WAAW,QAAQ;AAC1B,YAAM,cAAc,KAAK,UACtB,IAAI,CAAC,OAAO;AACX,YAAI,YAAY;AAChB,YAAI,GAAG,WAAW,QAAW;AAC3B,gBAAM,MACJ,OAAO,GAAG,WAAW,WACjB,GAAG,SACH,KAAK,UAAU,GAAG,MAAM;AAC9B,sBACE,IAAI,SAAS,wBACT,WAAM,IAAI,UAAU,GAAG,qBAAqB,CAAC,mBAC7C,WAAM,GAAG;AAAA,QACjB;AACA,eAAO,OAAO,GAAG,QAAQ,GAAG,SAAS;AAAA,MACvC,CAAC,EACA,KAAK,IAAI;AACZ,YAAM,KAAK,KAAK,IAAI;AAAA,EAAgB,WAAW,EAAE;AAAA,IACnD;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,MAAM;AAC1B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/adapters/claude/session/jsonl-hydration.ts","../../shared/src/cloud-prompt.ts","../../shared/src/saga.ts","../src/acp-extensions.ts","../src/sagas/resume-saga.ts","../src/utils/logger.ts","../src/resume.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<boolean> {\n const { posthogAPI, log } = params;\n\n try {\n const jsonlPath = getSessionJsonlPath(params.sessionId, params.cwd);\n try {\n await fs.access(jsonlPath);\n return true;\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 false;\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 false;\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 false;\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 return true;\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 return false;\n }\n}\n","import type { ContentBlock } from \"@agentclientprotocol/sdk\";\n\n/**\n * Wire format prefix for structured cloud prompts.\n * Text-only prompts are sent as plain strings (no prefix) as an optimization.\n * Multi-block prompts (text + attachments) are serialized as `PREFIX + JSON({ blocks })`.\n */\nexport const CLOUD_PROMPT_PREFIX = \"__twig_cloud_prompt_v1__:\";\n\nexport function serializeCloudPrompt(blocks: ContentBlock[]): string {\n if (blocks.length === 1 && blocks[0].type === \"text\") {\n return blocks[0].text.trim();\n }\n\n return `${CLOUD_PROMPT_PREFIX}${JSON.stringify({ blocks })}`;\n}\n\nexport function deserializeCloudPrompt(value: string): ContentBlock[] {\n const trimmed = value.trim();\n if (!trimmed) {\n return [];\n }\n\n if (!trimmed.startsWith(CLOUD_PROMPT_PREFIX)) {\n return [{ type: \"text\", text: trimmed }];\n }\n\n try {\n const parsed = JSON.parse(trimmed.slice(CLOUD_PROMPT_PREFIX.length)) as {\n blocks?: ContentBlock[];\n };\n\n if (Array.isArray(parsed.blocks) && parsed.blocks.length > 0) {\n return parsed.blocks;\n }\n } catch {\n // Fall through to preserve the raw string if the payload is malformed.\n }\n\n return [{ type: \"text\", text: trimmed }];\n}\n\nexport function promptBlocksToText(blocks: ContentBlock[]): string {\n return blocks\n .filter((b): b is ContentBlock & { type: \"text\" } => b.type === \"text\")\n .map((block) => block.text)\n .join(\"\")\n .trim();\n}\n","/**\n * Configuration for a single saga step\n */\nexport interface SagaStep<T> {\n /** Unique name for this step (used in logging) */\n name: string;\n /** The forward action to execute */\n execute: () => Promise<T>;\n /** The rollback action to undo this step (receives the execute result) */\n rollback: (result: T) => Promise<void>;\n}\n\n/**\n * Result of a saga execution\n */\nexport type SagaResult<T, TFailedStep extends string = string> =\n | { success: true; data: T }\n | { success: false; error: string; failedStep: TFailedStep };\n\nexport interface SagaLogger {\n info(message: string, data?: Record<string, unknown>): void;\n debug(message: string, data?: Record<string, unknown>): void;\n error(message: string, data?: Record<string, unknown>): void;\n warn(message: string, data?: Record<string, unknown>): void;\n}\n\nconst consoleLogger: SagaLogger = {\n info: (_message, _data) => {},\n debug: (_message, _data) => {},\n error: (_message, _data) => {},\n warn: (_message, _data) => {},\n};\n\n/**\n * Abstract base class for implementing our Saga pattern.\n *\n * Subclasses implement the `execute` method, using `this.step()` to define\n * each step with its compensating action. If any step throws, all completed\n * steps are automatically rolled back in reverse order.\n *\n * The failed step name is automatically tracked from the step's `name` property.\n *\n * @template TInput - The input type for the saga\n * @template TOutput - The successful output type\n */\nexport abstract class Saga<TInput, TOutput> {\n abstract readonly sagaName: string;\n\n private completedSteps: Array<{\n name: string;\n rollback: () => Promise<void>;\n }> = [];\n private currentStepName = \"unknown\";\n private stepTimings: Array<{ name: string; durationMs: number }> = [];\n protected readonly log: SagaLogger;\n\n constructor(logger?: SagaLogger) {\n this.log = logger ?? consoleLogger;\n }\n\n /**\n * Run the saga with the given input.\n * Returns a discriminated union result - either success with data or failure with error details.\n */\n async run(input: TInput): Promise<SagaResult<TOutput>> {\n this.completedSteps = [];\n this.currentStepName = \"unknown\";\n this.stepTimings = [];\n\n const sagaStart = performance.now();\n\n try {\n const result = await this.execute(input);\n\n const totalDuration = performance.now() - sagaStart;\n this.log.debug(\"Saga completed successfully\", {\n sagaName: this.sagaName,\n stepsCompleted: this.completedSteps.length,\n totalDurationMs: Math.round(totalDuration),\n stepTimings: this.stepTimings,\n });\n\n return { success: true, data: result };\n } catch (error) {\n this.log.error(\"Saga failed, initiating rollback\", {\n sagaName: this.sagaName,\n failedStep: this.currentStepName,\n error: error instanceof Error ? error.message : String(error),\n completedStepTimings: this.stepTimings,\n });\n\n await this.rollback();\n\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n failedStep: this.currentStepName,\n };\n }\n }\n\n /**\n * Implement this method to define the saga's steps.\n * Use `this.step()` to execute each step with its compensating action.\n */\n protected abstract execute(input: TInput): Promise<TOutput>;\n\n /**\n * Execute a step with its rollback action.\n * If the step succeeds, its rollback action is stored for potential rollback.\n * The step name is automatically tracked for error reporting.\n *\n * @param config - Step configuration with name, execute, and rollback functions\n * @returns The result of the execute function\n * @throws Re-throws any error from the execute function (triggers rollback)\n */\n protected async step<T>(config: SagaStep<T>): Promise<T> {\n this.currentStepName = config.name;\n\n const stepStart = performance.now();\n const result = await config.execute();\n const durationMs = Math.round(performance.now() - stepStart);\n\n this.stepTimings.push({ name: config.name, durationMs });\n\n this.completedSteps.push({\n name: config.name,\n rollback: () => config.rollback(result),\n });\n\n return result;\n }\n\n /**\n * Execute a step that doesn't need rollback.\n * Useful for read-only operations or operations that are idempotent.\n * The step name is automatically tracked for error reporting.\n *\n * @param name - Step name for logging and error tracking\n * @param execute - The action to execute\n * @returns The result of the execute function\n */\n protected async readOnlyStep<T>(\n name: string,\n execute: () => Promise<T>,\n ): Promise<T> {\n this.currentStepName = name;\n\n const stepStart = performance.now();\n const result = await execute();\n const durationMs = Math.round(performance.now() - stepStart);\n\n this.stepTimings.push({ name, durationMs });\n return result;\n }\n\n /**\n * Roll back all completed steps in reverse order.\n * Rollback errors are logged but don't stop the rollback of other steps.\n */\n private async rollback(): Promise<void> {\n this.log.info(\"Rolling back saga\", {\n stepsToRollback: this.completedSteps.length,\n });\n\n const stepsReversed = [...this.completedSteps].reverse();\n\n for (const step of stepsReversed) {\n try {\n await step.rollback();\n } catch (error) {\n // Log but continue - we want to attempt all rollbacks\n this.log.error(`Failed to rollback step: ${step.name}`, {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n this.log.info(\"Rollback completed\", {\n stepsAttempted: this.completedSteps.length,\n });\n }\n\n /**\n * Get the number of completed steps (useful for testing)\n */\n protected getCompletedStepCount(): number {\n return this.completedSteps.length;\n }\n}\n","/**\n * PostHog-specific ACP extensions.\n *\n * These follow the ACP extensibility model:\n * - Custom notification methods are prefixed with `_posthog/`\n * - Custom data can be attached via `_meta` fields\n *\n * See: https://agentclientprotocol.com/docs/extensibility\n */\n\n/**\n * Custom notification methods for PostHog-specific events.\n * Used with AgentSideConnection.extNotification() or Client.extNotification()\n */\nexport const POSTHOG_NOTIFICATIONS = {\n /** Git branch was created for a task */\n BRANCH_CREATED: \"_posthog/branch_created\",\n\n /** Task run has started execution */\n RUN_STARTED: \"_posthog/run_started\",\n\n /** Task has completed (success or failure) */\n TASK_COMPLETE: \"_posthog/task_complete\",\n\n /** Agent finished processing a turn (prompt returned, waiting for next input) */\n TURN_COMPLETE: \"_posthog/turn_complete\",\n\n /** Error occurred during task execution */\n ERROR: \"_posthog/error\",\n\n /** Console/log output from the agent */\n CONSOLE: \"_posthog/console\",\n\n /** Maps taskRunId to agent's sessionId and adapter type (for resumption) */\n SDK_SESSION: \"_posthog/sdk_session\",\n\n /** Git checkpoint captured for handoff */\n GIT_CHECKPOINT: \"_posthog/git_checkpoint\",\n\n /** Agent mode changed (interactive/background) */\n MODE_CHANGE: \"_posthog/mode_change\",\n\n /** Request to resume a session from previous state */\n SESSION_RESUME: \"_posthog/session/resume\",\n\n /** User message sent from client to agent */\n USER_MESSAGE: \"_posthog/user_message\",\n\n /** Request to cancel current operation */\n CANCEL: \"_posthog/cancel\",\n\n /** Request to close the session */\n CLOSE: \"_posthog/close\",\n\n /** Agent status update (thinking, working, etc.) */\n STATUS: \"_posthog/status\",\n\n /** Structured backend progress notification; events in the same turn group into one card on the client */\n PROGRESS: \"_posthog/progress\",\n\n /** Task-level notification (progress, milestones) */\n TASK_NOTIFICATION: \"_posthog/task_notification\",\n\n /** Marks a boundary for log compaction */\n COMPACT_BOUNDARY: \"_posthog/compact_boundary\",\n\n /** Token usage update for a session turn */\n USAGE_UPDATE: \"_posthog/usage_update\",\n\n /** Response to a relayed permission request (plan approval, question) */\n PERMISSION_RESPONSE: \"_posthog/permission_response\",\n} as const;\n\n/**\n * Custom request methods for PostHog-specific operations that need a response\n * (request/response, not fire-and-forget). Used with\n * ClientSideConnection.extMethod() on the sender and Agent.extMethod() on the\n * receiver.\n */\nexport const POSTHOG_METHODS = {\n /**\n * Client requests a session refresh between turns. Payload may include\n * `mcpServers` to trigger a resume-with-new-options reinit; future fields\n * can extend this without adding new methods. Returns once the refresh has\n * completed so the caller can safely send the next prompt.\n */\n REFRESH_SESSION: \"_posthog/refresh_session\",\n} as const;\n\ntype PosthogNotification =\n (typeof POSTHOG_NOTIFICATIONS)[keyof typeof POSTHOG_NOTIFICATIONS];\n\ntype PosthogMethod = (typeof POSTHOG_METHODS)[keyof typeof POSTHOG_METHODS];\n\n/**\n * Does `method` match `expected`? Shared by notification and method matchers.\n * Handles the `__posthog/` double-prefix that extNotification() can produce.\n */\nfunction matchesExt(method: string | undefined, expected: string): boolean {\n if (!method) return false;\n return method === expected || method === `_${expected}`;\n}\n\n/** Dispatcher check for incoming `extNotification` calls on the agent side. */\nexport function isNotification(\n method: string | undefined,\n expected: PosthogNotification,\n): boolean {\n return matchesExt(method, expected);\n}\n\n/** Dispatcher check for incoming `extMethod` calls on the agent side. */\nexport function isMethod(\n method: string | undefined,\n expected: PosthogMethod,\n): boolean {\n return matchesExt(method, expected);\n}\n","import type { ContentBlock } from \"@agentclientprotocol/sdk\";\nimport { Saga } from \"@posthog/shared\";\nimport { POSTHOG_NOTIFICATIONS } from \"../acp-extensions\";\nimport type { PostHogAPIClient } from \"../posthog-api\";\nimport type {\n DeviceInfo,\n GitCheckpointEvent,\n StoredNotification,\n} from \"../types\";\nimport type { Logger } from \"../utils/logger\";\n\nexport interface ConversationTurn {\n role: \"user\" | \"assistant\";\n content: ContentBlock[];\n toolCalls?: ToolCallInfo[];\n}\n\nexport interface ToolCallInfo {\n toolCallId: string;\n toolName: string;\n input: unknown;\n result?: unknown;\n}\n\nexport interface ResumeInput {\n taskId: string;\n runId: string;\n repositoryPath?: string;\n apiClient: PostHogAPIClient;\n logger?: Logger;\n}\n\nexport interface ResumeOutput {\n conversation: ConversationTurn[];\n latestGitCheckpoint: GitCheckpointEvent | null;\n interrupted: boolean;\n lastDevice?: DeviceInfo;\n logEntryCount: number;\n}\n\nexport class ResumeSaga extends Saga<ResumeInput, ResumeOutput> {\n readonly sagaName = \"ResumeSaga\";\n\n protected async execute(input: ResumeInput): Promise<ResumeOutput> {\n const { taskId, runId, apiClient } = input;\n\n // Step 1: Fetch task run (read-only)\n const taskRun = await this.readOnlyStep(\"fetch_task_run\", () =>\n apiClient.getTaskRun(taskId, runId),\n );\n\n if (!taskRun.log_url) {\n this.log.info(\"No log URL found, starting fresh\");\n return this.emptyResult();\n }\n\n // Step 2: Fetch log entries (read-only)\n const entries = await this.readOnlyStep(\"fetch_logs\", () =>\n apiClient.fetchTaskRunLogs(taskRun),\n );\n\n if (entries.length === 0) {\n this.log.info(\"No log entries found, starting fresh\");\n return this.emptyResult();\n }\n\n this.log.info(\"Fetched log entries\", { count: entries.length });\n\n const latestGitCheckpoint = await this.readOnlyStep(\n \"find_git_checkpoint\",\n () => Promise.resolve(this.findLatestGitCheckpoint(entries)),\n );\n\n if (latestGitCheckpoint) {\n this.log.info(\"Found git checkpoint\", {\n checkpointId: latestGitCheckpoint.checkpointId,\n branch: latestGitCheckpoint.branch,\n });\n }\n\n const conversation = await this.readOnlyStep(\"rebuild_conversation\", () =>\n Promise.resolve(this.rebuildConversation(entries)),\n );\n\n // Step 6: Find device info (read-only, pure computation)\n const lastDevice = await this.readOnlyStep(\"find_device\", () =>\n Promise.resolve(this.findLastDeviceInfo(entries)),\n );\n\n this.log.info(\"Resume state rebuilt\", {\n turns: conversation.length,\n hasGitCheckpoint: !!latestGitCheckpoint,\n interrupted: false,\n });\n\n return {\n conversation,\n latestGitCheckpoint,\n interrupted: false,\n lastDevice,\n logEntryCount: entries.length,\n };\n }\n\n private emptyResult(): ResumeOutput {\n return {\n conversation: [],\n latestGitCheckpoint: null,\n interrupted: false,\n logEntryCount: 0,\n };\n }\n\n private findLatestGitCheckpoint(\n entries: StoredNotification[],\n ): GitCheckpointEvent | null {\n const sdkPrefixedMethod = `_${POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT}`;\n\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n const method = entry.notification?.method;\n if (\n method === sdkPrefixedMethod ||\n method === POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT\n ) {\n const params = entry.notification?.params as\n | GitCheckpointEvent\n | undefined;\n if (params?.checkpointId && params?.checkpointRef) {\n return params;\n }\n }\n }\n return null;\n }\n\n private findLastDeviceInfo(\n entries: StoredNotification[],\n ): DeviceInfo | undefined {\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n const params = entry.notification?.params as\n | { device?: DeviceInfo }\n | undefined;\n if (params?.device) {\n return params.device;\n }\n }\n return undefined;\n }\n\n private rebuildConversation(\n entries: StoredNotification[],\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 Record<string, unknown>;\n const sessionUpdate = update.sessionUpdate as string;\n\n switch (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 as ContentBlock | ContentBlock[];\n const contentArray = Array.isArray(content) ? content : [content];\n turns.push({\n role: \"user\",\n content: contentArray,\n });\n break;\n }\n\n case \"agent_message\": {\n const content = update.content as ContentBlock | undefined;\n if (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 \"agent_message_chunk\": {\n // Backward compatibility with older logs that have individual chunks\n const content = update.content as ContentBlock | undefined;\n if (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 as Record<string, unknown>)\n ?.claudeCode as Record<string, unknown> | undefined;\n if (meta) {\n const toolCallId = meta.toolCallId as string | undefined;\n const toolName = meta.toolName as string | undefined;\n const toolInput = meta.toolInput;\n const toolResponse = meta.toolResponse;\n\n if (toolCallId && toolName) {\n let toolCall = currentToolCalls.find(\n (tc) => tc.toolCallId === toolCallId,\n );\n if (!toolCall) {\n toolCall = {\n toolCallId,\n toolName,\n input: toolInput,\n };\n currentToolCalls.push(toolCall);\n }\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 as Record<string, unknown>)\n ?.claudeCode as Record<string, unknown> | undefined;\n if (meta) {\n const toolCallId = meta.toolCallId as string | undefined;\n const toolResponse = meta.toolResponse;\n\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}\n","import type { LogLevel as LogLevelType, OnLogCallback } from \"../types\";\n\nexport interface LoggerConfig {\n debug?: boolean;\n prefix?: string;\n scope?: string;\n onLog?: OnLogCallback;\n}\n\nexport class Logger {\n private debugEnabled: boolean;\n private prefix: string;\n private scope: string;\n private onLog?: OnLogCallback;\n\n constructor(config: LoggerConfig = {}) {\n this.debugEnabled = config.debug ?? false;\n this.prefix = config.prefix ?? \"[PostHog Agent]\";\n this.scope = config.scope ?? \"agent\";\n this.onLog = config.onLog;\n }\n\n private formatMessage(\n level: string,\n message: string,\n data?: unknown,\n ): string {\n const timestamp = new Date().toISOString();\n const base = `${timestamp} ${this.prefix} [${level}] ${message}`;\n\n if (data !== undefined) {\n return `${base} ${JSON.stringify(data, null, 2)}`;\n }\n\n return base;\n }\n\n private emitLog(level: LogLevelType, message: string, data?: unknown) {\n if (this.onLog) {\n this.onLog(level, this.scope, message, data);\n return;\n }\n\n const shouldLog = this.debugEnabled || level === \"error\";\n\n if (shouldLog) {\n console[level](this.formatMessage(level.toLowerCase(), message, data));\n }\n }\n\n error(message: string, error?: Error | unknown) {\n const data =\n error instanceof Error\n ? { message: error.message, stack: error.stack }\n : error;\n\n this.emitLog(\"error\", message, data);\n }\n\n warn(message: string, data?: unknown) {\n this.emitLog(\"warn\", message, data);\n }\n\n info(message: string, data?: unknown) {\n this.emitLog(\"info\", message, data);\n }\n\n debug(message: string, data?: unknown) {\n this.emitLog(\"debug\", message, data);\n }\n\n child(childPrefix: string): Logger {\n return new Logger({\n debug: this.debugEnabled,\n prefix: `${this.prefix} [${childPrefix}]`,\n scope: `${this.scope}:${childPrefix}`,\n onLog: this.onLog,\n });\n }\n}\n","/**\n * Resume - Restore agent state from persisted log\n *\n * Handles resuming a task from any point:\n * - Fetches log via the PostHog API\n * - Finds latest git_checkpoint event\n * - Rebuilds conversation from log events\n * - Restores working tree from checkpoint\n *\n * Uses Saga pattern for atomic operations with clear success/failure tracking.\n *\n * The log is the single source of truth for:\n * - Conversation history (user_message, agent_message_chunk, tool_call, tool_result)\n * - Working tree state (git_checkpoint events)\n * - Session metadata (device info, mode changes)\n */\n\nimport type { ContentBlock } from \"@agentclientprotocol/sdk\";\nimport { selectRecentTurns } from \"./adapters/claude/session/jsonl-hydration\";\nimport type { PostHogAPIClient } from \"./posthog-api\";\nimport { ResumeSaga } from \"./sagas/resume-saga\";\nimport type { DeviceInfo, GitCheckpointEvent } from \"./types\";\nimport { Logger } from \"./utils/logger\";\n\nexport interface ResumeState {\n conversation: ConversationTurn[];\n latestGitCheckpoint: GitCheckpointEvent | null;\n interrupted: boolean;\n lastDevice?: DeviceInfo;\n logEntryCount: number;\n}\n\nexport interface ConversationTurn {\n role: \"user\" | \"assistant\";\n content: ContentBlock[];\n toolCalls?: ToolCallInfo[];\n}\n\nexport interface ToolCallInfo {\n toolCallId: string;\n toolName: string;\n input: unknown;\n result?: unknown;\n}\n\nexport interface ResumeConfig {\n taskId: string;\n runId: string;\n repositoryPath?: string;\n apiClient: PostHogAPIClient;\n logger?: Logger;\n}\n\n/**\n * Resume a task from its persisted log.\n * Returns the rebuilt state for the agent to continue from.\n * Checkpoint application happens in the agent server after SSE connects.\n */\nexport async function resumeFromLog(\n config: ResumeConfig,\n): Promise<ResumeState> {\n const logger =\n config.logger || new Logger({ debug: false, prefix: \"[Resume]\" });\n\n logger.info(\"Resuming from log\", {\n taskId: config.taskId,\n runId: config.runId,\n });\n\n const saga = new ResumeSaga(logger);\n\n const result = await saga.run({\n taskId: config.taskId,\n runId: config.runId,\n repositoryPath: config.repositoryPath,\n apiClient: config.apiClient,\n logger,\n });\n\n if (!result.success) {\n logger.error(\"Failed to resume from log\", {\n error: result.error,\n failedStep: result.failedStep,\n });\n throw new Error(\n `Failed to resume at step '${result.failedStep}': ${result.error}`,\n );\n }\n\n return {\n conversation: result.data.conversation as ConversationTurn[],\n latestGitCheckpoint: result.data.latestGitCheckpoint,\n interrupted: result.data.interrupted,\n lastDevice: result.data.lastDevice,\n logEntryCount: result.data.logEntryCount,\n };\n}\n\n/**\n * Convert resumed conversation back to API format for continuation.\n */\nexport function conversationToPromptHistory(\n conversation: ConversationTurn[],\n): Array<{ role: \"user\" | \"assistant\"; content: ContentBlock[] }> {\n return conversation.map((turn) => ({\n role: turn.role,\n content: turn.content,\n }));\n}\n\nconst RESUME_HISTORY_TOKEN_BUDGET = 50_000;\nconst TOOL_RESULT_MAX_CHARS = 2000;\n\nconst RESUME_CONTEXT_MARKERS = [\n \"You are resuming a previous conversation\",\n \"Here is the conversation history from the\",\n \"Continue from where you left off\",\n];\n\nfunction isResumeContextTurn(turn: ConversationTurn): boolean {\n if (turn.role !== \"user\") return false;\n const text = turn.content\n .filter((b) => b.type === \"text\")\n .map((b) => (b as { type: \"text\"; text: string }).text)\n .join(\"\");\n return RESUME_CONTEXT_MARKERS.some((marker) => text.includes(marker));\n}\n\nexport function formatConversationForResume(\n conversation: ConversationTurn[],\n): string {\n const filtered = conversation.filter((turn) => !isResumeContextTurn(turn));\n const selected = selectRecentTurns(filtered, RESUME_HISTORY_TOKEN_BUDGET);\n const parts: string[] = [];\n\n if (selected.length < filtered.length) {\n parts.push(\n `*(${filtered.length - selected.length} earlier turns omitted)*`,\n );\n }\n\n for (const turn of selected) {\n const role = turn.role === \"user\" ? \"User\" : \"Assistant\";\n\n const textParts = turn.content\n .filter((block) => block.type === \"text\")\n .map((block) => (block as { type: \"text\"; text: string }).text);\n\n if (textParts.length > 0) {\n parts.push(`**${role}**: ${textParts.join(\"\\n\")}`);\n }\n\n if (turn.toolCalls?.length) {\n const toolSummary = turn.toolCalls\n .map((tc) => {\n let resultStr = \"\";\n if (tc.result !== undefined) {\n const raw =\n typeof tc.result === \"string\"\n ? tc.result\n : JSON.stringify(tc.result);\n resultStr =\n raw.length > TOOL_RESULT_MAX_CHARS\n ? ` → ${raw.substring(0, TOOL_RESULT_MAX_CHARS)}...(truncated)`\n : ` → ${raw}`;\n }\n return ` - ${tc.toolName}${resultStr}`;\n })\n .join(\"\\n\");\n parts.push(`**${role} (tools)**:\\n${toolSummary}`);\n }\n }\n\n return parts.join(\"\\n\\n\");\n}\n"],"mappings":"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","names":[]}
|
|
@@ -103,8 +103,7 @@ declare class AgentServer {
|
|
|
103
103
|
private isQuestionMeta;
|
|
104
104
|
private detectAndAttachPrUrl;
|
|
105
105
|
private cleanupSession;
|
|
106
|
-
private
|
|
107
|
-
private captureHandoffCheckpoint;
|
|
106
|
+
private captureCheckpointState;
|
|
108
107
|
private extractHandoffLocalGitState;
|
|
109
108
|
private broadcastTurnComplete;
|
|
110
109
|
private broadcastEvent;
|