@posthog/agent 2.3.709 → 2.3.735
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.js +21 -3
- package/dist/agent.js.map +1 -1
- package/dist/handoff-checkpoint.js.map +1 -1
- package/dist/posthog-api.d.ts +1 -1
- package/dist/posthog-api.js +1 -1
- package/dist/posthog-api.js.map +1 -1
- package/dist/resume.js.map +1 -1
- package/dist/server/agent-server.js +24 -3
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +24 -3
- package/dist/server/bin.cjs.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
- package/src/adapters/claude/UPSTREAM.md +1 -0
- package/src/adapters/claude/claude-agent.slash-command.test.ts +22 -2
- package/src/adapters/claude/claude-agent.ts +31 -1
- package/src/adapters/claude/types.ts +7 -0
- package/src/server/agent-server.configure-environment.test.ts +39 -1
- package/src/server/agent-server.ts +2 -1
- package/src/types.ts +2 -1
- package/src/utils/gateway.ts +8 -1
package/dist/posthog-api.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PostHogAPIConfig, Task, TaskRun, StoredEntry, ArtifactType, TaskRunArtifact } from './types.js';
|
|
2
2
|
import '@posthog/git/handoff';
|
|
3
3
|
|
|
4
|
-
type GatewayProduct = "posthog_code" | "background_agents" | "signals";
|
|
4
|
+
type GatewayProduct = "posthog_code" | "background_agents" | "signals" | "slack_app";
|
|
5
5
|
declare function getLlmGatewayUrl(posthogHost: string, product?: GatewayProduct): string;
|
|
6
6
|
declare function getGatewayUsageUrl(posthogHost: string, product?: GatewayProduct): string;
|
|
7
7
|
declare function getGatewayInvalidatePlanCacheUrl(posthogHost: string, product?: GatewayProduct): string;
|
package/dist/posthog-api.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// package.json
|
|
2
2
|
var package_default = {
|
|
3
3
|
name: "@posthog/agent",
|
|
4
|
-
version: "2.3.
|
|
4
|
+
version: "2.3.735",
|
|
5
5
|
repository: "https://github.com/PostHog/code",
|
|
6
6
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
7
7
|
exports: {
|
package/dist/posthog-api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../package.json","../src/utils/gateway.ts","../src/posthog-api.ts"],"sourcesContent":["{\n \"name\": \"@posthog/agent\",\n \"version\": \"2.3.709\",\n \"repository\": \"https://github.com/PostHog/code\",\n \"description\": \"TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./agent\": {\n \"types\": \"./dist/agent.d.ts\",\n \"import\": \"./dist/agent.js\"\n },\n \"./gateway-models\": {\n \"types\": \"./dist/gateway-models.d.ts\",\n \"import\": \"./dist/gateway-models.js\"\n },\n \"./posthog-api\": {\n \"types\": \"./dist/posthog-api.d.ts\",\n \"import\": \"./dist/posthog-api.js\"\n },\n \"./pr-url-detector\": {\n \"types\": \"./dist/pr-url-detector.d.ts\",\n \"import\": \"./dist/pr-url-detector.js\"\n },\n \"./types\": {\n \"types\": \"./dist/types.d.ts\",\n \"import\": \"./dist/types.js\"\n },\n \"./adapters/claude/questions/utils\": {\n \"types\": \"./dist/adapters/claude/questions/utils.d.ts\",\n \"import\": \"./dist/adapters/claude/questions/utils.js\"\n },\n \"./adapters/claude/permissions/permission-options\": {\n \"types\": \"./dist/adapters/claude/permissions/permission-options.d.ts\",\n \"import\": \"./dist/adapters/claude/permissions/permission-options.js\"\n },\n \"./adapters/claude/tools\": {\n \"types\": \"./dist/adapters/claude/tools.d.ts\",\n \"import\": \"./dist/adapters/claude/tools.js\"\n },\n \"./adapters/claude/conversion/tool-use-to-acp\": {\n \"types\": \"./dist/adapters/claude/conversion/tool-use-to-acp.d.ts\",\n \"import\": \"./dist/adapters/claude/conversion/tool-use-to-acp.js\"\n },\n \"./adapters/claude/session/jsonl-hydration\": {\n \"types\": \"./dist/adapters/claude/session/jsonl-hydration.d.ts\",\n \"import\": \"./dist/adapters/claude/session/jsonl-hydration.js\"\n },\n \"./adapters/claude/session/models\": {\n \"types\": \"./dist/adapters/claude/session/models.d.ts\",\n \"import\": \"./dist/adapters/claude/session/models.js\"\n },\n \"./adapters/reasoning-effort\": {\n \"types\": \"./dist/adapters/reasoning-effort.d.ts\",\n \"import\": \"./dist/adapters/reasoning-effort.js\"\n },\n \"./adapters/claude/mcp/tool-metadata\": {\n \"types\": \"./dist/adapters/claude/mcp/tool-metadata.d.ts\",\n \"import\": \"./dist/adapters/claude/mcp/tool-metadata.js\"\n },\n \"./execution-mode\": {\n \"types\": \"./dist/execution-mode.d.ts\",\n \"import\": \"./dist/execution-mode.js\"\n },\n \"./resume\": {\n \"types\": \"./dist/resume.d.ts\",\n \"import\": \"./dist/resume.js\"\n },\n \"./handoff-checkpoint\": {\n \"types\": \"./dist/handoff-checkpoint.d.ts\",\n \"import\": \"./dist/handoff-checkpoint.js\"\n },\n \"./server\": {\n \"types\": \"./dist/server/agent-server.d.ts\",\n \"import\": \"./dist/server/agent-server.js\"\n },\n \"./server/schemas\": {\n \"types\": \"./dist/server/schemas.d.ts\",\n \"import\": \"./dist/server/schemas.js\"\n }\n },\n \"bin\": {\n \"agent-server\": \"./dist/server/bin.cjs\"\n },\n \"type\": \"module\",\n \"keywords\": [\n \"posthog\",\n \"claude\",\n \"agent\",\n \"ai\",\n \"git\",\n \"typescript\"\n ],\n \"author\": \"PostHog\",\n \"license\": \"MIT\",\n \"scripts\": {\n \"build\": \"node ../../scripts/rimraf.mjs dist && tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"typecheck\": \"pnpm exec tsc --noEmit\",\n \"prepublishOnly\": \"pnpm run build\",\n \"clean\": \"node ../../scripts/rimraf.mjs dist .turbo\"\n },\n \"engines\": {\n \"node\": \">=20.0.0\"\n },\n \"devDependencies\": {\n \"@posthog/shared\": \"workspace:*\",\n \"@posthog/git\": \"workspace:*\",\n \"@posthog/enricher\": \"workspace:*\",\n \"@types/bun\": \"latest\",\n \"@types/tar\": \"^6.1.13\",\n \"msw\": \"^2.12.7\",\n \"tsup\": \"^8.5.1\",\n \"tsx\": \"^4.20.6\",\n \"typescript\": \"^5.5.0\",\n \"vitest\": \"^2.1.8\"\n },\n \"dependencies\": {\n \"@agentclientprotocol/sdk\": \"0.19.0\",\n \"@anthropic-ai/claude-agent-sdk\": \"0.2.112\",\n \"@anthropic-ai/sdk\": \"0.89.0\",\n \"@hono/node-server\": \"^1.19.9\",\n \"@opentelemetry/api-logs\": \"^0.208.0\",\n \"@opentelemetry/exporter-logs-otlp-http\": \"^0.208.0\",\n \"@opentelemetry/resources\": \"^2.0.0\",\n \"@opentelemetry/sdk-logs\": \"^0.208.0\",\n \"@opentelemetry/semantic-conventions\": \"^1.28.0\",\n \"@types/jsonwebtoken\": \"^9.0.10\",\n \"commander\": \"^14.0.2\",\n \"hono\": \"^4.11.7\",\n \"jsonwebtoken\": \"^9.0.2\",\n \"minimatch\": \"^10.0.3\",\n \"@modelcontextprotocol/sdk\": \"1.29.0\",\n \"tar\": \"^7.5.0\",\n \"uuid\": \"13.0.0\",\n \"yoga-wasm-web\": \"^0.3.3\",\n \"zod\": \"^4.2.0\"\n },\n \"files\": [\n \"dist/**/*\",\n \"src/**/*\",\n \"README.md\",\n \"CLAUDE.md\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","export type GatewayProduct = \"posthog_code\" | \"background_agents\" | \"signals\";\n\nexport function resolveGatewayProduct({\n isInternal,\n originProduct,\n}: {\n isInternal?: boolean;\n originProduct?: string | null;\n} = {}): GatewayProduct {\n if (isInternal) {\n return originProduct === \"signal_report\" ? \"signals\" : \"background_agents\";\n }\n return \"posthog_code\";\n}\n\n/**\n * Build `x-posthog-property-<name>: <value>` header lines that the LLM\n * gateway lifts onto the `$ai_generation` event it captures for each call\n * (see `services/llm-gateway/src/llm_gateway/request_context.py`).\n *\n * Returns a newline-joined string ready for `ANTHROPIC_CUSTOM_HEADERS`.\n * `null`/`undefined` property values are dropped.\n */\nexport function buildGatewayPropertyHeaders(\n properties: Record<string, string | number | boolean | null | undefined>,\n): string {\n return Object.entries(properties)\n .filter(([, value]) => value !== null && value !== undefined)\n .map(([key, value]) => `x-posthog-property-${key}: ${value}`)\n .join(\"\\n\");\n}\n\nfunction getGatewayBaseUrl(posthogHost: string): string {\n const url = new URL(posthogHost);\n const hostname = url.hostname;\n\n if (hostname === \"localhost\" || hostname === \"127.0.0.1\") {\n return `${url.protocol}//localhost:3308`;\n }\n\n if (hostname === \"host.docker.internal\") {\n return `${url.protocol}//host.docker.internal:3308`;\n }\n\n const region = hostname.match(/^(us|eu)\\.posthog\\.com$/)?.[1] ?? \"us\";\n return `https://gateway.${region}.posthog.com`;\n}\n\nexport function getLlmGatewayUrl(\n posthogHost: string,\n product: GatewayProduct = \"posthog_code\",\n): string {\n return `${getGatewayBaseUrl(posthogHost)}/${product}`;\n}\n\nexport function getGatewayUsageUrl(\n posthogHost: string,\n product: GatewayProduct = \"posthog_code\",\n): string {\n return `${getGatewayBaseUrl(posthogHost)}/v1/usage/${product}`;\n}\n\nexport function getGatewayInvalidatePlanCacheUrl(\n posthogHost: string,\n product: GatewayProduct = \"posthog_code\",\n): string {\n return `${getGatewayBaseUrl(posthogHost)}/v1/usage/${product}/invalidate-plan-cache`;\n}\n","import packageJson from \"../package.json\" with { type: \"json\" };\nimport type {\n ArtifactType,\n PostHogAPIConfig,\n StoredEntry,\n Task,\n TaskRun,\n TaskRunArtifact,\n} from \"./types\";\nimport {\n getGatewayInvalidatePlanCacheUrl,\n getGatewayUsageUrl,\n getLlmGatewayUrl,\n} from \"./utils/gateway\";\n\nexport {\n getGatewayInvalidatePlanCacheUrl,\n getGatewayUsageUrl,\n getLlmGatewayUrl,\n};\n\nconst DEFAULT_USER_AGENT = `posthog/agent.hog.dev; version: ${packageJson.version}`;\n\nexport interface TaskArtifactUploadPayload {\n name: string;\n type: ArtifactType;\n content: string;\n content_type?: string;\n}\n\nexport type TaskRunUpdate = Partial<\n Pick<\n TaskRun,\n | \"status\"\n | \"branch\"\n | \"stage\"\n | \"error_message\"\n | \"output\"\n | \"state\"\n | \"environment\"\n >\n> & {\n state_remove_keys?: string[];\n};\n\nexport class PostHogAPIClient {\n private config: PostHogAPIConfig;\n\n constructor(config: PostHogAPIConfig) {\n this.config = config;\n }\n\n private get baseUrl(): string {\n const host = this.config.apiUrl.endsWith(\"/\")\n ? this.config.apiUrl.slice(0, -1)\n : this.config.apiUrl;\n return host;\n }\n\n private isAuthFailure(status: number): boolean {\n return status === 401 || status === 403;\n }\n\n private async resolveApiKey(forceRefresh = false): Promise<string> {\n if (forceRefresh && this.config.refreshApiKey) {\n return this.config.refreshApiKey();\n }\n\n return this.config.getApiKey();\n }\n\n private async buildHeaders(\n options: RequestInit,\n forceRefresh = false,\n ): Promise<Headers> {\n const headers = new Headers(options.headers);\n headers.set(\n \"Authorization\",\n `Bearer ${await this.resolveApiKey(forceRefresh)}`,\n );\n headers.set(\"Content-Type\", \"application/json\");\n headers.set(\"User-Agent\", this.config.userAgent ?? DEFAULT_USER_AGENT);\n return headers;\n }\n\n private async performRequest(\n endpoint: string,\n options: RequestInit,\n forceRefresh = false,\n ): Promise<Response> {\n const url = `${this.baseUrl}${endpoint}`;\n\n return fetch(url, {\n ...options,\n headers: await this.buildHeaders(options, forceRefresh),\n });\n }\n\n private async performRequestWithRetry(\n endpoint: string,\n options: RequestInit = {},\n ): Promise<Response> {\n let response = await this.performRequest(endpoint, options);\n\n if (!response.ok && this.isAuthFailure(response.status)) {\n response = await this.performRequest(endpoint, options, true);\n }\n\n return response;\n }\n\n private async apiRequest<T>(\n endpoint: string,\n options: RequestInit = {},\n ): Promise<T> {\n const response = await this.performRequestWithRetry(endpoint, options);\n\n if (!response.ok) {\n let errorMessage: string;\n try {\n const errorResponse = await response.json();\n errorMessage = `Failed request: [${response.status}] ${JSON.stringify(errorResponse)}`;\n } catch {\n errorMessage = `Failed request: [${response.status}] ${response.statusText}`;\n }\n throw new Error(errorMessage);\n }\n\n return response.json();\n }\n\n private getTeamId(): number {\n return this.config.projectId;\n }\n\n async getApiKey(forceRefresh = false): Promise<string> {\n return this.resolveApiKey(forceRefresh);\n }\n\n getLlmGatewayUrl(): string {\n return getLlmGatewayUrl(this.baseUrl);\n }\n\n async getTask(taskId: string): Promise<Task> {\n const teamId = this.getTeamId();\n return this.apiRequest<Task>(`/api/projects/${teamId}/tasks/${taskId}/`);\n }\n\n async getTaskRun(taskId: string, runId: string): Promise<TaskRun> {\n const teamId = this.getTeamId();\n return this.apiRequest<TaskRun>(\n `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`,\n );\n }\n\n async resumeRunInCloud(taskId: string, runId: string): Promise<TaskRun> {\n const teamId = this.getTeamId();\n return this.apiRequest<TaskRun>(\n `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/resume_in_cloud/`,\n { method: \"POST\" },\n );\n }\n\n async updateTaskRun(\n taskId: string,\n runId: string,\n payload: TaskRunUpdate,\n ): Promise<TaskRun> {\n const teamId = this.getTeamId();\n return this.apiRequest<TaskRun>(\n `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`,\n {\n method: \"PATCH\",\n body: JSON.stringify(payload),\n },\n );\n }\n\n async setTaskRunOutput(\n taskId: string,\n runId: string,\n output: Record<string, unknown>,\n ): Promise<TaskRun> {\n return this.apiRequest(\n `/api/projects/${this.getTeamId()}/tasks/${taskId}/runs/${runId}/set_output/`,\n {\n method: \"PATCH\",\n body: JSON.stringify(output),\n },\n );\n }\n\n async appendTaskRunLog(\n taskId: string,\n runId: string,\n entries: StoredEntry[],\n ): Promise<TaskRun> {\n const teamId = this.getTeamId();\n return this.apiRequest<TaskRun>(\n `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/append_log/`,\n {\n method: \"POST\",\n body: JSON.stringify({ entries }),\n },\n );\n }\n\n async relayMessage(\n taskId: string,\n runId: string,\n text: string,\n ): Promise<void> {\n const teamId = this.getTeamId();\n await this.apiRequest<{ status: string }>(\n `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/relay_message/`,\n {\n method: \"POST\",\n body: JSON.stringify({ text }),\n },\n );\n }\n\n async uploadTaskArtifacts(\n taskId: string,\n runId: string,\n artifacts: TaskArtifactUploadPayload[],\n ): Promise<TaskRunArtifact[]> {\n if (!artifacts.length) {\n return [];\n }\n\n const teamId = this.getTeamId();\n const response = await this.apiRequest<{ artifacts: TaskRunArtifact[] }>(\n `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/artifacts/`,\n {\n method: \"POST\",\n body: JSON.stringify({ artifacts }),\n },\n );\n\n const manifest = response.artifacts ?? [];\n\n // The backend returns the full run artifact manifest after each upload.\n // Callers want the artifacts corresponding to this upload request only.\n return manifest.slice(-artifacts.length);\n }\n\n /**\n * Download artifact content by storage path\n * Streams the file through the PostHog backend so the sandbox does not need\n * direct access to object storage.\n */\n async downloadArtifact(\n taskId: string,\n runId: string,\n storagePath: string,\n ): Promise<ArrayBuffer | null> {\n const teamId = this.getTeamId();\n\n try {\n const response = await this.performRequestWithRetry(\n `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/artifacts/download/`,\n {\n method: \"POST\",\n body: JSON.stringify({ storage_path: storagePath }),\n },\n );\n if (!response.ok) {\n throw new Error(`Failed to download artifact: ${response.status}`);\n }\n return response.arrayBuffer();\n } catch {\n return null;\n }\n }\n\n /**\n * Fetch logs for a task run via the logs API endpoint\n * @param taskRun - The task run to fetch logs for\n * @returns Array of stored entries, or empty array if no logs available\n */\n async fetchTaskRunLogs(taskRun: TaskRun): Promise<StoredEntry[]> {\n const teamId = this.getTeamId();\n const endpoint = `/api/projects/${teamId}/tasks/${taskRun.task}/runs/${taskRun.id}/logs`;\n\n try {\n const response = await this.performRequestWithRetry(endpoint);\n\n if (!response.ok) {\n if (response.status === 404) {\n return [];\n }\n throw new Error(\n `Failed to fetch logs: ${response.status} ${response.statusText}`,\n );\n }\n\n const content = await response.text();\n\n if (!content.trim()) {\n return [];\n }\n\n // Parse newline-delimited JSON\n return content\n .trim()\n .split(\"\\n\")\n .map((line) => JSON.parse(line) as StoredEntry);\n } catch (error) {\n throw new Error(\n `Failed to fetch task run logs: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../package.json","../src/utils/gateway.ts","../src/posthog-api.ts"],"sourcesContent":["{\n \"name\": \"@posthog/agent\",\n \"version\": \"2.3.735\",\n \"repository\": \"https://github.com/PostHog/code\",\n \"description\": \"TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./agent\": {\n \"types\": \"./dist/agent.d.ts\",\n \"import\": \"./dist/agent.js\"\n },\n \"./gateway-models\": {\n \"types\": \"./dist/gateway-models.d.ts\",\n \"import\": \"./dist/gateway-models.js\"\n },\n \"./posthog-api\": {\n \"types\": \"./dist/posthog-api.d.ts\",\n \"import\": \"./dist/posthog-api.js\"\n },\n \"./pr-url-detector\": {\n \"types\": \"./dist/pr-url-detector.d.ts\",\n \"import\": \"./dist/pr-url-detector.js\"\n },\n \"./types\": {\n \"types\": \"./dist/types.d.ts\",\n \"import\": \"./dist/types.js\"\n },\n \"./adapters/claude/questions/utils\": {\n \"types\": \"./dist/adapters/claude/questions/utils.d.ts\",\n \"import\": \"./dist/adapters/claude/questions/utils.js\"\n },\n \"./adapters/claude/permissions/permission-options\": {\n \"types\": \"./dist/adapters/claude/permissions/permission-options.d.ts\",\n \"import\": \"./dist/adapters/claude/permissions/permission-options.js\"\n },\n \"./adapters/claude/tools\": {\n \"types\": \"./dist/adapters/claude/tools.d.ts\",\n \"import\": \"./dist/adapters/claude/tools.js\"\n },\n \"./adapters/claude/conversion/tool-use-to-acp\": {\n \"types\": \"./dist/adapters/claude/conversion/tool-use-to-acp.d.ts\",\n \"import\": \"./dist/adapters/claude/conversion/tool-use-to-acp.js\"\n },\n \"./adapters/claude/session/jsonl-hydration\": {\n \"types\": \"./dist/adapters/claude/session/jsonl-hydration.d.ts\",\n \"import\": \"./dist/adapters/claude/session/jsonl-hydration.js\"\n },\n \"./adapters/claude/session/models\": {\n \"types\": \"./dist/adapters/claude/session/models.d.ts\",\n \"import\": \"./dist/adapters/claude/session/models.js\"\n },\n \"./adapters/reasoning-effort\": {\n \"types\": \"./dist/adapters/reasoning-effort.d.ts\",\n \"import\": \"./dist/adapters/reasoning-effort.js\"\n },\n \"./adapters/claude/mcp/tool-metadata\": {\n \"types\": \"./dist/adapters/claude/mcp/tool-metadata.d.ts\",\n \"import\": \"./dist/adapters/claude/mcp/tool-metadata.js\"\n },\n \"./execution-mode\": {\n \"types\": \"./dist/execution-mode.d.ts\",\n \"import\": \"./dist/execution-mode.js\"\n },\n \"./resume\": {\n \"types\": \"./dist/resume.d.ts\",\n \"import\": \"./dist/resume.js\"\n },\n \"./handoff-checkpoint\": {\n \"types\": \"./dist/handoff-checkpoint.d.ts\",\n \"import\": \"./dist/handoff-checkpoint.js\"\n },\n \"./server\": {\n \"types\": \"./dist/server/agent-server.d.ts\",\n \"import\": \"./dist/server/agent-server.js\"\n },\n \"./server/schemas\": {\n \"types\": \"./dist/server/schemas.d.ts\",\n \"import\": \"./dist/server/schemas.js\"\n }\n },\n \"bin\": {\n \"agent-server\": \"./dist/server/bin.cjs\"\n },\n \"type\": \"module\",\n \"keywords\": [\n \"posthog\",\n \"claude\",\n \"agent\",\n \"ai\",\n \"git\",\n \"typescript\"\n ],\n \"author\": \"PostHog\",\n \"license\": \"MIT\",\n \"scripts\": {\n \"build\": \"node ../../scripts/rimraf.mjs dist && tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"typecheck\": \"pnpm exec tsc --noEmit\",\n \"prepublishOnly\": \"pnpm run build\",\n \"clean\": \"node ../../scripts/rimraf.mjs dist .turbo\"\n },\n \"engines\": {\n \"node\": \">=20.0.0\"\n },\n \"devDependencies\": {\n \"@posthog/shared\": \"workspace:*\",\n \"@posthog/git\": \"workspace:*\",\n \"@posthog/enricher\": \"workspace:*\",\n \"@types/bun\": \"latest\",\n \"@types/tar\": \"^6.1.13\",\n \"msw\": \"^2.12.7\",\n \"tsup\": \"^8.5.1\",\n \"tsx\": \"^4.20.6\",\n \"typescript\": \"^5.5.0\",\n \"vitest\": \"^2.1.8\"\n },\n \"dependencies\": {\n \"@agentclientprotocol/sdk\": \"0.19.0\",\n \"@anthropic-ai/claude-agent-sdk\": \"0.2.112\",\n \"@anthropic-ai/sdk\": \"0.89.0\",\n \"@hono/node-server\": \"^1.19.9\",\n \"@opentelemetry/api-logs\": \"^0.208.0\",\n \"@opentelemetry/exporter-logs-otlp-http\": \"^0.208.0\",\n \"@opentelemetry/resources\": \"^2.0.0\",\n \"@opentelemetry/sdk-logs\": \"^0.208.0\",\n \"@opentelemetry/semantic-conventions\": \"^1.28.0\",\n \"@types/jsonwebtoken\": \"^9.0.10\",\n \"commander\": \"^14.0.2\",\n \"hono\": \"^4.11.7\",\n \"jsonwebtoken\": \"^9.0.2\",\n \"minimatch\": \"^10.0.3\",\n \"@modelcontextprotocol/sdk\": \"1.29.0\",\n \"tar\": \"^7.5.0\",\n \"uuid\": \"13.0.0\",\n \"yoga-wasm-web\": \"^0.3.3\",\n \"zod\": \"^4.2.0\"\n },\n \"files\": [\n \"dist/**/*\",\n \"src/**/*\",\n \"README.md\",\n \"CLAUDE.md\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","export type GatewayProduct =\n | \"posthog_code\"\n | \"background_agents\"\n | \"signals\"\n | \"slack_app\";\n\nexport function resolveGatewayProduct({\n isInternal,\n originProduct,\n}: {\n isInternal?: boolean;\n originProduct?: string | null;\n} = {}): GatewayProduct {\n if (originProduct === \"slack\") {\n return \"slack_app\";\n }\n if (isInternal) {\n return originProduct === \"signal_report\" ? \"signals\" : \"background_agents\";\n }\n return \"posthog_code\";\n}\n\n/**\n * Build `x-posthog-property-<name>: <value>` header lines that the LLM\n * gateway lifts onto the `$ai_generation` event it captures for each call\n * (see `services/llm-gateway/src/llm_gateway/request_context.py`).\n *\n * Returns a newline-joined string ready for `ANTHROPIC_CUSTOM_HEADERS`.\n * `null`/`undefined` property values are dropped.\n */\nexport function buildGatewayPropertyHeaders(\n properties: Record<string, string | number | boolean | null | undefined>,\n): string {\n return Object.entries(properties)\n .filter(([, value]) => value !== null && value !== undefined)\n .map(([key, value]) => `x-posthog-property-${key}: ${value}`)\n .join(\"\\n\");\n}\n\nfunction getGatewayBaseUrl(posthogHost: string): string {\n const url = new URL(posthogHost);\n const hostname = url.hostname;\n\n if (hostname === \"localhost\" || hostname === \"127.0.0.1\") {\n return `${url.protocol}//localhost:3308`;\n }\n\n if (hostname === \"host.docker.internal\") {\n return `${url.protocol}//host.docker.internal:3308`;\n }\n\n const region = hostname.match(/^(us|eu)\\.posthog\\.com$/)?.[1] ?? \"us\";\n return `https://gateway.${region}.posthog.com`;\n}\n\nexport function getLlmGatewayUrl(\n posthogHost: string,\n product: GatewayProduct = \"posthog_code\",\n): string {\n return `${getGatewayBaseUrl(posthogHost)}/${product}`;\n}\n\nexport function getGatewayUsageUrl(\n posthogHost: string,\n product: GatewayProduct = \"posthog_code\",\n): string {\n return `${getGatewayBaseUrl(posthogHost)}/v1/usage/${product}`;\n}\n\nexport function getGatewayInvalidatePlanCacheUrl(\n posthogHost: string,\n product: GatewayProduct = \"posthog_code\",\n): string {\n return `${getGatewayBaseUrl(posthogHost)}/v1/usage/${product}/invalidate-plan-cache`;\n}\n","import packageJson from \"../package.json\" with { type: \"json\" };\nimport type {\n ArtifactType,\n PostHogAPIConfig,\n StoredEntry,\n Task,\n TaskRun,\n TaskRunArtifact,\n} from \"./types\";\nimport {\n getGatewayInvalidatePlanCacheUrl,\n getGatewayUsageUrl,\n getLlmGatewayUrl,\n} from \"./utils/gateway\";\n\nexport {\n getGatewayInvalidatePlanCacheUrl,\n getGatewayUsageUrl,\n getLlmGatewayUrl,\n};\n\nconst DEFAULT_USER_AGENT = `posthog/agent.hog.dev; version: ${packageJson.version}`;\n\nexport interface TaskArtifactUploadPayload {\n name: string;\n type: ArtifactType;\n content: string;\n content_type?: string;\n}\n\nexport type TaskRunUpdate = Partial<\n Pick<\n TaskRun,\n | \"status\"\n | \"branch\"\n | \"stage\"\n | \"error_message\"\n | \"output\"\n | \"state\"\n | \"environment\"\n >\n> & {\n state_remove_keys?: string[];\n};\n\nexport class PostHogAPIClient {\n private config: PostHogAPIConfig;\n\n constructor(config: PostHogAPIConfig) {\n this.config = config;\n }\n\n private get baseUrl(): string {\n const host = this.config.apiUrl.endsWith(\"/\")\n ? this.config.apiUrl.slice(0, -1)\n : this.config.apiUrl;\n return host;\n }\n\n private isAuthFailure(status: number): boolean {\n return status === 401 || status === 403;\n }\n\n private async resolveApiKey(forceRefresh = false): Promise<string> {\n if (forceRefresh && this.config.refreshApiKey) {\n return this.config.refreshApiKey();\n }\n\n return this.config.getApiKey();\n }\n\n private async buildHeaders(\n options: RequestInit,\n forceRefresh = false,\n ): Promise<Headers> {\n const headers = new Headers(options.headers);\n headers.set(\n \"Authorization\",\n `Bearer ${await this.resolveApiKey(forceRefresh)}`,\n );\n headers.set(\"Content-Type\", \"application/json\");\n headers.set(\"User-Agent\", this.config.userAgent ?? DEFAULT_USER_AGENT);\n return headers;\n }\n\n private async performRequest(\n endpoint: string,\n options: RequestInit,\n forceRefresh = false,\n ): Promise<Response> {\n const url = `${this.baseUrl}${endpoint}`;\n\n return fetch(url, {\n ...options,\n headers: await this.buildHeaders(options, forceRefresh),\n });\n }\n\n private async performRequestWithRetry(\n endpoint: string,\n options: RequestInit = {},\n ): Promise<Response> {\n let response = await this.performRequest(endpoint, options);\n\n if (!response.ok && this.isAuthFailure(response.status)) {\n response = await this.performRequest(endpoint, options, true);\n }\n\n return response;\n }\n\n private async apiRequest<T>(\n endpoint: string,\n options: RequestInit = {},\n ): Promise<T> {\n const response = await this.performRequestWithRetry(endpoint, options);\n\n if (!response.ok) {\n let errorMessage: string;\n try {\n const errorResponse = await response.json();\n errorMessage = `Failed request: [${response.status}] ${JSON.stringify(errorResponse)}`;\n } catch {\n errorMessage = `Failed request: [${response.status}] ${response.statusText}`;\n }\n throw new Error(errorMessage);\n }\n\n return response.json();\n }\n\n private getTeamId(): number {\n return this.config.projectId;\n }\n\n async getApiKey(forceRefresh = false): Promise<string> {\n return this.resolveApiKey(forceRefresh);\n }\n\n getLlmGatewayUrl(): string {\n return getLlmGatewayUrl(this.baseUrl);\n }\n\n async getTask(taskId: string): Promise<Task> {\n const teamId = this.getTeamId();\n return this.apiRequest<Task>(`/api/projects/${teamId}/tasks/${taskId}/`);\n }\n\n async getTaskRun(taskId: string, runId: string): Promise<TaskRun> {\n const teamId = this.getTeamId();\n return this.apiRequest<TaskRun>(\n `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`,\n );\n }\n\n async resumeRunInCloud(taskId: string, runId: string): Promise<TaskRun> {\n const teamId = this.getTeamId();\n return this.apiRequest<TaskRun>(\n `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/resume_in_cloud/`,\n { method: \"POST\" },\n );\n }\n\n async updateTaskRun(\n taskId: string,\n runId: string,\n payload: TaskRunUpdate,\n ): Promise<TaskRun> {\n const teamId = this.getTeamId();\n return this.apiRequest<TaskRun>(\n `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`,\n {\n method: \"PATCH\",\n body: JSON.stringify(payload),\n },\n );\n }\n\n async setTaskRunOutput(\n taskId: string,\n runId: string,\n output: Record<string, unknown>,\n ): Promise<TaskRun> {\n return this.apiRequest(\n `/api/projects/${this.getTeamId()}/tasks/${taskId}/runs/${runId}/set_output/`,\n {\n method: \"PATCH\",\n body: JSON.stringify(output),\n },\n );\n }\n\n async appendTaskRunLog(\n taskId: string,\n runId: string,\n entries: StoredEntry[],\n ): Promise<TaskRun> {\n const teamId = this.getTeamId();\n return this.apiRequest<TaskRun>(\n `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/append_log/`,\n {\n method: \"POST\",\n body: JSON.stringify({ entries }),\n },\n );\n }\n\n async relayMessage(\n taskId: string,\n runId: string,\n text: string,\n ): Promise<void> {\n const teamId = this.getTeamId();\n await this.apiRequest<{ status: string }>(\n `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/relay_message/`,\n {\n method: \"POST\",\n body: JSON.stringify({ text }),\n },\n );\n }\n\n async uploadTaskArtifacts(\n taskId: string,\n runId: string,\n artifacts: TaskArtifactUploadPayload[],\n ): Promise<TaskRunArtifact[]> {\n if (!artifacts.length) {\n return [];\n }\n\n const teamId = this.getTeamId();\n const response = await this.apiRequest<{ artifacts: TaskRunArtifact[] }>(\n `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/artifacts/`,\n {\n method: \"POST\",\n body: JSON.stringify({ artifacts }),\n },\n );\n\n const manifest = response.artifacts ?? [];\n\n // The backend returns the full run artifact manifest after each upload.\n // Callers want the artifacts corresponding to this upload request only.\n return manifest.slice(-artifacts.length);\n }\n\n /**\n * Download artifact content by storage path\n * Streams the file through the PostHog backend so the sandbox does not need\n * direct access to object storage.\n */\n async downloadArtifact(\n taskId: string,\n runId: string,\n storagePath: string,\n ): Promise<ArrayBuffer | null> {\n const teamId = this.getTeamId();\n\n try {\n const response = await this.performRequestWithRetry(\n `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/artifacts/download/`,\n {\n method: \"POST\",\n body: JSON.stringify({ storage_path: storagePath }),\n },\n );\n if (!response.ok) {\n throw new Error(`Failed to download artifact: ${response.status}`);\n }\n return response.arrayBuffer();\n } catch {\n return null;\n }\n }\n\n /**\n * Fetch logs for a task run via the logs API endpoint\n * @param taskRun - The task run to fetch logs for\n * @returns Array of stored entries, or empty array if no logs available\n */\n async fetchTaskRunLogs(taskRun: TaskRun): Promise<StoredEntry[]> {\n const teamId = this.getTeamId();\n const endpoint = `/api/projects/${teamId}/tasks/${taskRun.task}/runs/${taskRun.id}/logs`;\n\n try {\n const response = await this.performRequestWithRetry(endpoint);\n\n if (!response.ok) {\n if (response.status === 404) {\n return [];\n }\n throw new Error(\n `Failed to fetch logs: ${response.status} ${response.statusText}`,\n );\n }\n\n const content = await response.text();\n\n if (!content.trim()) {\n return [];\n }\n\n // Parse newline-delimited JSON\n return content\n .trim()\n .split(\"\\n\")\n .map((line) => JSON.parse(line) as StoredEntry);\n } catch (error) {\n throw new Error(\n `Failed to fetch task run logs: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/resume.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/claude/session/jsonl-hydration.ts","../../shared/src/image.ts","../../shared/src/binary.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","export const IMAGE_MIME_TYPES: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n bmp: \"image/bmp\",\n ico: \"image/x-icon\",\n tiff: \"image/tiff\",\n tif: \"image/tiff\",\n svg: \"image/svg+xml\",\n heic: \"image/heic\",\n heif: \"image/heif\",\n avif: \"image/avif\",\n};\n\nconst IMAGE_EXTENSIONS: ReadonlySet<string> = new Set(\n Object.keys(IMAGE_MIME_TYPES),\n);\n\nconst RASTER_IMAGE_EXTENSIONS: ReadonlySet<string> = new Set([\n \"png\",\n \"jpg\",\n \"jpeg\",\n \"gif\",\n \"webp\",\n \"bmp\",\n \"ico\",\n \"tiff\",\n \"tif\",\n \"avif\",\n]);\n\n// SVG is intentionally excluded — SVG can contain <script> tags that execute\n// when rendered as an <img> from a data URL. Heic/heif are excluded because\n// Chromium cannot decode them in an <img> tag.\nexport const ALLOWED_IMAGE_MIME_TYPES: ReadonlySet<string> = new Set([\n \"image/png\",\n \"image/jpeg\",\n \"image/gif\",\n \"image/webp\",\n \"image/bmp\",\n \"image/x-icon\",\n \"image/vnd.microsoft.icon\",\n \"image/tiff\",\n \"image/avif\",\n]);\n\nexport type ClaudeImageMimeType =\n | \"image/jpeg\"\n | \"image/png\"\n | \"image/gif\"\n | \"image/webp\";\n\nconst CLAUDE_IMAGE_MIME_TYPES: ReadonlySet<string> = new Set([\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n]);\n\nexport const CLAUDE_IMAGE_EXTENSIONS: ReadonlySet<string> = new Set([\n \"png\",\n \"jpg\",\n \"jpeg\",\n \"gif\",\n \"webp\",\n]);\n\nexport function isClaudeImageMimeType(\n mimeType: string,\n): mimeType is ClaudeImageMimeType {\n return CLAUDE_IMAGE_MIME_TYPES.has(mimeType.toLowerCase());\n}\n\nconst DATA_URL_PATTERN =\n /^data:([a-zA-Z]+\\/[a-zA-Z0-9.+-]+)(?:;[a-zA-Z0-9-]+=[^;,]+)*;base64,([A-Za-z0-9+/=\\s]+)$/;\n\nconst MAX_DATA_URL_LENGTH = 20 * 1024 * 1024;\nexport const MAX_IMAGE_BASE64_LENGTH = 15 * 1024 * 1024;\n\nexport interface ParsedImageDataUrl {\n mimeType: string;\n base64: string;\n}\n\nexport function extensionOf(filename: string): string {\n const slash = Math.max(filename.lastIndexOf(\"/\"), filename.lastIndexOf(\"\\\\\"));\n const basename = slash >= 0 ? filename.slice(slash + 1) : filename;\n const cleanBasename = basename.split(/[?#]/)[0];\n const dot = cleanBasename.lastIndexOf(\".\");\n return dot > 0 ? cleanBasename.slice(dot + 1).toLowerCase() : \"\";\n}\n\nexport function isImageFile(filename: string): boolean {\n const ext = extensionOf(filename);\n return ext.length > 0 && IMAGE_EXTENSIONS.has(ext);\n}\n\nexport function isRasterImageFile(filename: string): boolean {\n const ext = extensionOf(filename);\n return ext.length > 0 && RASTER_IMAGE_EXTENSIONS.has(ext);\n}\n\nexport function isClaudeImageFile(filename: string): boolean {\n const ext = extensionOf(filename);\n return ext.length > 0 && CLAUDE_IMAGE_EXTENSIONS.has(ext);\n}\n\nexport function isGifFile(filename: string): boolean {\n return extensionOf(filename) === \"gif\";\n}\n\nexport function getImageMimeType(filePath: string): string {\n return IMAGE_MIME_TYPES[extensionOf(filePath)] ?? \"application/octet-stream\";\n}\n\nexport function isAllowedImageMimeType(mimeType: string): boolean {\n return ALLOWED_IMAGE_MIME_TYPES.has(mimeType.toLowerCase());\n}\n\nexport function parseImageDataUrl(value: string): ParsedImageDataUrl | null {\n if (typeof value !== \"string\" || value.length === 0) return null;\n if (value.length > MAX_DATA_URL_LENGTH) return null;\n if (!/^\\s{0,1024}data:/.test(value)) return null;\n\n const trimmed = value.trim();\n if (trimmed.length === 0) return null;\n\n const match = DATA_URL_PATTERN.exec(trimmed);\n if (!match) return null;\n\n const mimeType = match[1].toLowerCase();\n if (!ALLOWED_IMAGE_MIME_TYPES.has(mimeType)) return null;\n\n const base64 = match[2].replace(/\\s+/g, \"\");\n if (base64.length === 0 || base64.length > MAX_IMAGE_BASE64_LENGTH) {\n return null;\n }\n\n return { mimeType, base64 };\n}\n\nexport function buildImageDataUrl(mimeType: string, base64: string): string {\n return `data:${mimeType};base64,${base64}`;\n}\n","import { extensionOf, IMAGE_MIME_TYPES } from \"./image\";\n\nexport const AUDIO_VIDEO_EXTENSIONS: ReadonlySet<string> = new Set([\n \"mp3\",\n \"mp4\",\n \"wav\",\n \"avi\",\n \"mov\",\n \"mkv\",\n \"webm\",\n \"mpg\",\n \"mpeg\",\n \"flac\",\n \"ogg\",\n \"m4a\",\n \"aac\",\n]);\n\nexport const ARCHIVE_EXTENSIONS: ReadonlySet<string> = new Set([\n \"zip\",\n \"tar\",\n \"gz\",\n \"tgz\",\n \"bz2\",\n \"xz\",\n \"rar\",\n \"7z\",\n]);\n\nexport const EXECUTABLE_EXTENSIONS: ReadonlySet<string> = new Set([\n \"exe\",\n \"dll\",\n \"so\",\n \"dylib\",\n \"wasm\",\n \"bin\",\n \"o\",\n]);\n\nexport const FONT_EXTENSIONS: ReadonlySet<string> = new Set([\n \"ttf\",\n \"otf\",\n \"woff\",\n \"woff2\",\n \"eot\",\n]);\n\nexport const DOCUMENT_BINARY_EXTENSIONS: ReadonlySet<string> = new Set([\"pdf\"]);\n\n// SVG is excluded — it is XML text and consumers like title generation read\n// it as text rather than treating it as opaque bytes.\nexport const BINARY_EXTENSIONS: ReadonlySet<string> = new Set([\n ...Object.keys(IMAGE_MIME_TYPES).filter((ext) => ext !== \"svg\"),\n ...AUDIO_VIDEO_EXTENSIONS,\n ...ARCHIVE_EXTENSIONS,\n ...EXECUTABLE_EXTENSIONS,\n ...FONT_EXTENSIONS,\n ...DOCUMENT_BINARY_EXTENSIONS,\n]);\n\nexport function isBinaryFile(filename: string): boolean {\n const ext = extensionOf(filename);\n return ext.length > 0 && BINARY_EXTENSIONS.has(ext);\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":[]}
|
|
1
|
+
{"version":3,"sources":["../src/adapters/claude/session/jsonl-hydration.ts","../../shared/src/image.ts","../../shared/src/binary.ts","../../shared/src/cloud-prompt.ts","../../shared/src/inbox-prompts.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","export const IMAGE_MIME_TYPES: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n bmp: \"image/bmp\",\n ico: \"image/x-icon\",\n tiff: \"image/tiff\",\n tif: \"image/tiff\",\n svg: \"image/svg+xml\",\n heic: \"image/heic\",\n heif: \"image/heif\",\n avif: \"image/avif\",\n};\n\nconst IMAGE_EXTENSIONS: ReadonlySet<string> = new Set(\n Object.keys(IMAGE_MIME_TYPES),\n);\n\nconst RASTER_IMAGE_EXTENSIONS: ReadonlySet<string> = new Set([\n \"png\",\n \"jpg\",\n \"jpeg\",\n \"gif\",\n \"webp\",\n \"bmp\",\n \"ico\",\n \"tiff\",\n \"tif\",\n \"avif\",\n]);\n\n// SVG is intentionally excluded — SVG can contain <script> tags that execute\n// when rendered as an <img> from a data URL. Heic/heif are excluded because\n// Chromium cannot decode them in an <img> tag.\nexport const ALLOWED_IMAGE_MIME_TYPES: ReadonlySet<string> = new Set([\n \"image/png\",\n \"image/jpeg\",\n \"image/gif\",\n \"image/webp\",\n \"image/bmp\",\n \"image/x-icon\",\n \"image/vnd.microsoft.icon\",\n \"image/tiff\",\n \"image/avif\",\n]);\n\nexport type ClaudeImageMimeType =\n | \"image/jpeg\"\n | \"image/png\"\n | \"image/gif\"\n | \"image/webp\";\n\nconst CLAUDE_IMAGE_MIME_TYPES: ReadonlySet<string> = new Set([\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n]);\n\nexport const CLAUDE_IMAGE_EXTENSIONS: ReadonlySet<string> = new Set([\n \"png\",\n \"jpg\",\n \"jpeg\",\n \"gif\",\n \"webp\",\n]);\n\nexport function isClaudeImageMimeType(\n mimeType: string,\n): mimeType is ClaudeImageMimeType {\n return CLAUDE_IMAGE_MIME_TYPES.has(mimeType.toLowerCase());\n}\n\nconst DATA_URL_PATTERN =\n /^data:([a-zA-Z]+\\/[a-zA-Z0-9.+-]+)(?:;[a-zA-Z0-9-]+=[^;,]+)*;base64,([A-Za-z0-9+/=\\s]+)$/;\n\nconst MAX_DATA_URL_LENGTH = 20 * 1024 * 1024;\nexport const MAX_IMAGE_BASE64_LENGTH = 15 * 1024 * 1024;\n\nexport interface ParsedImageDataUrl {\n mimeType: string;\n base64: string;\n}\n\nexport function extensionOf(filename: string): string {\n const slash = Math.max(filename.lastIndexOf(\"/\"), filename.lastIndexOf(\"\\\\\"));\n const basename = slash >= 0 ? filename.slice(slash + 1) : filename;\n const cleanBasename = basename.split(/[?#]/)[0];\n const dot = cleanBasename.lastIndexOf(\".\");\n return dot > 0 ? cleanBasename.slice(dot + 1).toLowerCase() : \"\";\n}\n\nexport function isImageFile(filename: string): boolean {\n const ext = extensionOf(filename);\n return ext.length > 0 && IMAGE_EXTENSIONS.has(ext);\n}\n\nexport function isRasterImageFile(filename: string): boolean {\n const ext = extensionOf(filename);\n return ext.length > 0 && RASTER_IMAGE_EXTENSIONS.has(ext);\n}\n\nexport function isClaudeImageFile(filename: string): boolean {\n const ext = extensionOf(filename);\n return ext.length > 0 && CLAUDE_IMAGE_EXTENSIONS.has(ext);\n}\n\nexport function isGifFile(filename: string): boolean {\n return extensionOf(filename) === \"gif\";\n}\n\nexport function getImageMimeType(filePath: string): string {\n return IMAGE_MIME_TYPES[extensionOf(filePath)] ?? \"application/octet-stream\";\n}\n\nexport function isAllowedImageMimeType(mimeType: string): boolean {\n return ALLOWED_IMAGE_MIME_TYPES.has(mimeType.toLowerCase());\n}\n\nexport function parseImageDataUrl(value: string): ParsedImageDataUrl | null {\n if (typeof value !== \"string\" || value.length === 0) return null;\n if (value.length > MAX_DATA_URL_LENGTH) return null;\n if (!/^\\s{0,1024}data:/.test(value)) return null;\n\n const trimmed = value.trim();\n if (trimmed.length === 0) return null;\n\n const match = DATA_URL_PATTERN.exec(trimmed);\n if (!match) return null;\n\n const mimeType = match[1].toLowerCase();\n if (!ALLOWED_IMAGE_MIME_TYPES.has(mimeType)) return null;\n\n const base64 = match[2].replace(/\\s+/g, \"\");\n if (base64.length === 0 || base64.length > MAX_IMAGE_BASE64_LENGTH) {\n return null;\n }\n\n return { mimeType, base64 };\n}\n\nexport function buildImageDataUrl(mimeType: string, base64: string): string {\n return `data:${mimeType};base64,${base64}`;\n}\n","import { extensionOf, IMAGE_MIME_TYPES } from \"./image\";\n\nexport const AUDIO_VIDEO_EXTENSIONS: ReadonlySet<string> = new Set([\n \"mp3\",\n \"mp4\",\n \"wav\",\n \"avi\",\n \"mov\",\n \"mkv\",\n \"webm\",\n \"mpg\",\n \"mpeg\",\n \"flac\",\n \"ogg\",\n \"m4a\",\n \"aac\",\n]);\n\nexport const ARCHIVE_EXTENSIONS: ReadonlySet<string> = new Set([\n \"zip\",\n \"tar\",\n \"gz\",\n \"tgz\",\n \"bz2\",\n \"xz\",\n \"rar\",\n \"7z\",\n]);\n\nexport const EXECUTABLE_EXTENSIONS: ReadonlySet<string> = new Set([\n \"exe\",\n \"dll\",\n \"so\",\n \"dylib\",\n \"wasm\",\n \"bin\",\n \"o\",\n]);\n\nexport const FONT_EXTENSIONS: ReadonlySet<string> = new Set([\n \"ttf\",\n \"otf\",\n \"woff\",\n \"woff2\",\n \"eot\",\n]);\n\nexport const DOCUMENT_BINARY_EXTENSIONS: ReadonlySet<string> = new Set([\"pdf\"]);\n\n// SVG is excluded — it is XML text and consumers like title generation read\n// it as text rather than treating it as opaque bytes.\nexport const BINARY_EXTENSIONS: ReadonlySet<string> = new Set([\n ...Object.keys(IMAGE_MIME_TYPES).filter((ext) => ext !== \"svg\"),\n ...AUDIO_VIDEO_EXTENSIONS,\n ...ARCHIVE_EXTENSIONS,\n ...EXECUTABLE_EXTENSIONS,\n ...FONT_EXTENSIONS,\n ...DOCUMENT_BINARY_EXTENSIONS,\n]);\n\nexport function isBinaryFile(filename: string): boolean {\n const ext = extensionOf(filename);\n return ext.length > 0 && BINARY_EXTENSIONS.has(ext);\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","interface BuildDiscussReportPromptOptions {\n reportId: string;\n reportLink: string;\n question?: string;\n}\n\nexport function buildDiscussReportPrompt({\n reportId,\n reportLink,\n question,\n}: BuildDiscussReportPromptOptions): string {\n const trimmedQuestion = question?.trim();\n const intro = `Discuss PostHog inbox report ${reportId} ([inbox item](${reportLink})). Use the inbox MCP tools to fetch the report,`;\n const guard =\n \" If you can't fetch the report, say so instead of guessing what it contains.\";\n const body = trimmedQuestion\n ? `${intro} then answer this first: ${trimmedQuestion}`\n : `${intro} then give me a brief readout and ask what I want to dig into.`;\n return `${body}${guard}`;\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":[]}
|
|
@@ -9315,7 +9315,7 @@ import { z as z5 } from "zod";
|
|
|
9315
9315
|
// package.json
|
|
9316
9316
|
var package_default = {
|
|
9317
9317
|
name: "@posthog/agent",
|
|
9318
|
-
version: "2.3.
|
|
9318
|
+
version: "2.3.735",
|
|
9319
9319
|
repository: "https://github.com/PostHog/code",
|
|
9320
9320
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
9321
9321
|
exports: {
|
|
@@ -18360,6 +18360,14 @@ function readClaudeMdQuietly(cwd, logger) {
|
|
|
18360
18360
|
return void 0;
|
|
18361
18361
|
}
|
|
18362
18362
|
}
|
|
18363
|
+
function collectKnownSlashCommands(commands) {
|
|
18364
|
+
const names = /* @__PURE__ */ new Set();
|
|
18365
|
+
if (!commands) return names;
|
|
18366
|
+
for (const cmd of commands) {
|
|
18367
|
+
if (cmd.name) names.add(cmd.name);
|
|
18368
|
+
}
|
|
18369
|
+
return names;
|
|
18370
|
+
}
|
|
18363
18371
|
function sanitizeTitle(text2) {
|
|
18364
18372
|
const sanitized = text2.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
18365
18373
|
if (sanitized.length <= MAX_TITLE_LENGTH) {
|
|
@@ -18625,7 +18633,9 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
18625
18633
|
}
|
|
18626
18634
|
if (message.subtype === "session_state_changed" && message.state === "idle") {
|
|
18627
18635
|
if (!promptReplayed) {
|
|
18628
|
-
|
|
18636
|
+
const cmdName = commandMatch?.[1].slice(1);
|
|
18637
|
+
const known = cmdName !== void 0 && this.session.knownSlashCommands?.has(cmdName) === true;
|
|
18638
|
+
if (commandMatch && !known) {
|
|
18629
18639
|
const cmd = commandMatch[1];
|
|
18630
18640
|
this.logger.warn(
|
|
18631
18641
|
"Slash command produced no output; treating as unsupported",
|
|
@@ -18644,7 +18654,9 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
18644
18654
|
return { stopReason: "end_turn" };
|
|
18645
18655
|
}
|
|
18646
18656
|
this.logger.debug("Skipping idle state before prompt replay", {
|
|
18647
|
-
sessionId: params.sessionId
|
|
18657
|
+
sessionId: params.sessionId,
|
|
18658
|
+
command: commandMatch?.[1],
|
|
18659
|
+
known
|
|
18648
18660
|
});
|
|
18649
18661
|
break;
|
|
18650
18662
|
}
|
|
@@ -19194,6 +19206,9 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
19194
19206
|
`Session ${forkSession ? "fork" : "resumption"} timed out for sessionId=${sessionId}`
|
|
19195
19207
|
);
|
|
19196
19208
|
}
|
|
19209
|
+
session.knownSlashCommands = collectKnownSlashCommands(
|
|
19210
|
+
result.value.commands
|
|
19211
|
+
);
|
|
19197
19212
|
} catch (err2) {
|
|
19198
19213
|
settingsManager.dispose();
|
|
19199
19214
|
if (err2 instanceof Error && err2.message === "Query closed before response received") {
|
|
@@ -19233,6 +19248,9 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
19233
19248
|
`Session initialization timed out for sessionId=${sessionId}`
|
|
19234
19249
|
);
|
|
19235
19250
|
}
|
|
19251
|
+
session.knownSlashCommands = collectKnownSlashCommands(
|
|
19252
|
+
initResult.value.commands
|
|
19253
|
+
);
|
|
19236
19254
|
} catch (err2) {
|
|
19237
19255
|
settingsManager.dispose();
|
|
19238
19256
|
this.logger.error("Session initialization failed", {
|
|
@@ -21759,6 +21777,9 @@ function resolveGatewayProduct({
|
|
|
21759
21777
|
isInternal,
|
|
21760
21778
|
originProduct
|
|
21761
21779
|
} = {}) {
|
|
21780
|
+
if (originProduct === "slack") {
|
|
21781
|
+
return "slack_app";
|
|
21782
|
+
}
|
|
21762
21783
|
if (isInternal) {
|
|
21763
21784
|
return originProduct === "signal_report" ? "signals" : "background_agents";
|
|
21764
21785
|
}
|