{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../../src/recorder/extension.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAoB,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAGpF,OAAO,EAAE,KAAK,QAAQ,EAAe,MAAM,aAAa,CAAC;AAUzD,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpE;AAkBD,MAAM,WAAW,wBAAwB;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACnC;AAmHD,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,wBAA6B,GAAG,gBAAgB,CA4ThG","sourcesContent":["import { access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { ExtensionContext, ExtensionFactory } from \"@ch1nyzzz/pi-coding-agent\";\nimport { resolveSessionBundleDigest } from \"../bundle/runtime.ts\";\nimport { initializeInboxLifecycle } from \"../inbox.ts\";\nimport { type EvoPaths, getEvoPaths } from \"../paths.ts\";\nimport { BundleRegistry } from \"../registry/registry.ts\";\nimport type { UsageSummary } from \"../types.ts\";\nimport { buildSessionDigest } from \"./digest.ts\";\nimport type { RecorderSessionReference, VerificationKind, VerificationRecord } from \"./schema.ts\";\nimport { createRecorderStore, type RecorderStore, readSessionLog, resolveStoredPayload } from \"./store.ts\";\n\nconst DURABLE_PREFERENCE_PATTERN =\n\t/以后|今后|从现在开始|每次|始终|一律|默认|记住|不要再|别再|优先|注意不要|第一性原理|不要.{0,12}过度|from\\s+now\\s+on|going\\s+forward|every\\s+time|by\\s+default|\\balways\\b|\\bnever\\b|\\bremember\\b|\\bprefer\\b/i;\n\nexport function isDurablePreferenceInstruction(text: string): boolean {\n\treturn DURABLE_PREFERENCE_PATTERN.test(text);\n}\n\nasync function isPrivacyExcluded(cwd: string): Promise<boolean> {\n\ttry {\n\t\tawait access(join(cwd, \".pi\", \"evo-private\"));\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\ninterface PendingTool {\n\tinput: unknown;\n\tresult?: unknown;\n\tisError?: boolean;\n\tstartedAtMs: number;\n}\n\nexport interface RecorderExtensionOptions {\n\troot?: string;\n\tpaths?: EvoPaths;\n\tbundleDigest?: string;\n\tartifactThresholdBytes?: number;\n\tpreviewCharacters?: number;\n\tgitDiffTimeoutMs?: number;\n\tnow?: () => Date;\n\tonError?: (error: unknown) => void;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction getString(value: unknown, key: string): string | undefined {\n\tif (!isRecord(value)) return undefined;\n\treturn typeof value[key] === \"string\" ? value[key] : undefined;\n}\n\nfunction getNumber(value: unknown, key: string): number {\n\tif (!isRecord(value)) return 0;\n\tconst candidate = value[key];\n\treturn typeof candidate === \"number\" && Number.isFinite(candidate) ? candidate : 0;\n}\n\nfunction classifyVerificationCommand(command: string): VerificationKind | undefined {\n\tconst normalized = command.toLowerCase();\n\tif (\n\t\t/\\b(?:typecheck|type-check|tsc)(?:\\s|$)/.test(normalized) ||\n\t\t/\\b(?:npm|pnpm|yarn|bun)\\s+(?:run\\s+)?(?:check:types|check-types)(?:\\s|$)/.test(normalized)\n\t) {\n\t\treturn \"typecheck\";\n\t}\n\tif (\n\t\t/\\b(?:eslint|biome\\s+check|ruff\\s+check|golangci-lint)(?:\\s|$)/.test(normalized) ||\n\t\t/\\b(?:npm|pnpm|yarn|bun)\\s+(?:run\\s+)?lint(?:\\s|$)/.test(normalized)\n\t) {\n\t\treturn \"lint\";\n\t}\n\tif (\n\t\t/\\b(?:npm|pnpm|yarn|bun)\\s+(?:run\\s+)?build(?:\\s|$)/.test(normalized) ||\n\t\t/\\b(?:cargo\\s+build|go\\s+build|cmake\\s+--build|gradle\\w*\\s+build|mvn\\w*\\s+package)(?:\\s|$)/.test(normalized)\n\t) {\n\t\treturn \"build\";\n\t}\n\tif (\n\t\t/\\b(?:vitest|pytest|jest|mocha)(?:\\s|$)/.test(normalized) ||\n\t\t/\\b(?:npm|pnpm|yarn|bun)\\s+(?:run\\s+)?test(?:\\s|$)/.test(normalized) ||\n\t\t/\\b(?:cargo|go|dotnet)\\s+test(?:\\s|$)/.test(normalized)\n\t) {\n\t\treturn \"test\";\n\t}\n\tif (/\\b(?:npm|pnpm|yarn|bun)\\s+(?:run\\s+)?check(?:\\s|$)/.test(normalized)) return \"check\";\n\treturn undefined;\n}\n\nfunction collectText(value: unknown, visited: WeakSet<object> = new WeakSet<object>()): string {\n\tif (typeof value === \"string\") return value;\n\tif (value instanceof Error) return value.message;\n\tif (!isRecord(value) && !Array.isArray(value)) return \"\";\n\tif (visited.has(value)) return \"\";\n\tvisited.add(value);\n\ttry {\n\t\tif (Array.isArray(value))\n\t\t\treturn value\n\t\t\t\t.map((item) => collectText(item, visited))\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\"\\n\");\n\t\tconst directText = typeof value.text === \"string\" ? value.text : \"\";\n\t\tconst contentText = \"content\" in value ? collectText(value.content, visited) : \"\";\n\t\treturn [directText, contentText].filter(Boolean).join(\"\\n\");\n\t} finally {\n\t\tvisited.delete(value);\n\t}\n}\n\nfunction messageFingerprint(message: unknown): string | undefined {\n\tif (!isRecord(message)) return undefined;\n\tconst role = getString(message, \"role\");\n\tif (!role) return undefined;\n\tconst timestamp = typeof message.timestamp === \"number\" ? String(message.timestamp) : \"\";\n\treturn `${role}\\0${timestamp}\\0${collectText(message.content)}`;\n}\n\nfunction inferBashExitCode(result: unknown, isError: boolean): number | null {\n\tif (!isError) return 0;\n\tconst match = /Command exited with code\\s+(-?\\d+)/.exec(collectText(result));\n\tif (!match?.[1]) return null;\n\tconst exitCode = Number(match[1]);\n\treturn Number.isSafeInteger(exitCode) ? exitCode : null;\n}\n\nfunction getUsage(message: unknown): UsageSummary | undefined {\n\tif (getString(message, \"role\") !== \"assistant\" || !isRecord(message) || !isRecord(message.usage)) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tinput: getNumber(message.usage, \"input\"),\n\t\toutput: getNumber(message.usage, \"output\"),\n\t\tcacheRead: getNumber(message.usage, \"cacheRead\"),\n\t\tcacheWrite: getNumber(message.usage, \"cacheWrite\"),\n\t\ttotalTokens: getNumber(message.usage, \"totalTokens\"),\n\t};\n}\n\nfunction getVerification(\n\ttoolName: string,\n\tinput: unknown,\n\tresult: unknown,\n\tisError: boolean,\n): VerificationRecord | undefined {\n\tif (toolName !== \"bash\") return undefined;\n\tconst command = getString(input, \"command\");\n\tif (!command) return undefined;\n\tconst kind = classifyVerificationCommand(command);\n\tif (!kind) return undefined;\n\treturn {\n\t\tkind,\n\t\tcommand,\n\t\texitCode: inferBashExitCode(result, isError),\n\t};\n}\n\nexport function createRecorderExtension(options: RecorderExtensionOptions = {}): ExtensionFactory {\n\tconst paths = options.paths ?? getEvoPaths(options.root);\n\tconst now = options.now ?? (() => new Date());\n\tconst pendingTools = new Map<string, PendingTool>();\n\tconst registry = new BundleRegistry(paths);\n\tlet store: RecorderStore | undefined;\n\tlet excludedSessionId: string | undefined;\n\tlet compactionStartedAtMs: number | undefined;\n\tlet queue = Promise.resolve();\n\n\tfunction reportError(error: unknown): void {\n\t\ttry {\n\t\t\toptions.onError?.(error);\n\t\t} catch {\n\t\t\t// Recorder diagnostics must never alter agent execution.\n\t\t}\n\t}\n\n\tfunction enqueue(operation: () => Promise<void>): Promise<void> {\n\t\tconst next = queue.then(operation);\n\t\tqueue = next.catch(reportError);\n\t\treturn queue;\n\t}\n\n\treturn (pi) => {\n\t\tasync function backfillMissingUserMessages(activeStore: RecorderStore, ctx: ExtensionContext): Promise<void> {\n\t\t\tconst recorded = await readSessionLog(paths, activeStore.sessionId);\n\t\t\tconst sourceIds = new Set(\n\t\t\t\trecorded.flatMap((event) => (event.type === \"message\" && event.sourceEntryId ? [event.sourceEntryId] : [])),\n\t\t\t);\n\t\t\tconst fingerprints = new Set<string>();\n\t\t\tfor (const event of recorded) {\n\t\t\t\tif (event.type !== \"message\") continue;\n\t\t\t\tconst fingerprint = messageFingerprint(await resolveStoredPayload(paths, event.message));\n\t\t\t\tif (fingerprint) fingerprints.add(fingerprint);\n\t\t\t}\n\t\t\tfor (const entry of ctx.sessionManager.getEntries()) {\n\t\t\t\tif (entry.type !== \"message\" || entry.message.role !== \"user\" || sourceIds.has(entry.id)) continue;\n\t\t\t\tconst fingerprint = messageFingerprint(entry.message);\n\t\t\t\tif (fingerprint && fingerprints.has(fingerprint)) continue;\n\t\t\t\tawait activeStore.append({\n\t\t\t\t\ttype: \"message\",\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tmessage: await activeStore.storePayload(entry.message),\n\t\t\t\t\tsourceEntryId: entry.id,\n\t\t\t\t});\n\t\t\t\tif (fingerprint) fingerprints.add(fingerprint);\n\t\t\t}\n\t\t}\n\n\t\tasync function getFallbackBundleDigest(): Promise<string | undefined> {\n\t\t\tlet bundleDigest = options.bundleDigest;\n\t\t\tif (bundleDigest === undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tbundleDigest = await registry.readStableDigest();\n\t\t\t\t} catch (error) {\n\t\t\t\t\treportError(error);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn bundleDigest;\n\t\t}\n\n\t\tasync function openStore(sessionId: string, bundleDigest: string | undefined): Promise<RecorderStore> {\n\t\t\tstore = await createRecorderStore({\n\t\t\t\tpaths,\n\t\t\t\tsessionId,\n\t\t\t\tbundleDigest,\n\t\t\t\tartifactThresholdBytes: options.artifactThresholdBytes,\n\t\t\t\tpreviewCharacters: options.previewCharacters,\n\t\t\t\tnow,\n\t\t\t});\n\t\t\tpendingTools.clear();\n\t\t\tcompactionStartedAtMs = undefined;\n\t\t\treturn store;\n\t\t}\n\n\t\tasync function getStore(sessionId: string): Promise<RecorderStore> {\n\t\t\tif (store?.sessionId === sessionId) return store;\n\t\t\treturn openStore(sessionId, await getFallbackBundleDigest());\n\t\t}\n\n\t\tpi.on(\"session_start\", (event, ctx) =>\n\t\t\tenqueue(async () => {\n\t\t\t\tconst sessionId = ctx.sessionManager.getSessionId();\n\t\t\t\tif (await isPrivacyExcluded(ctx.cwd)) {\n\t\t\t\t\texcludedSessionId = sessionId;\n\t\t\t\t\tpendingTools.clear();\n\t\t\t\t\tstore = undefined;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\texcludedSessionId = undefined;\n\t\t\t\tconst recordedDigest = resolveSessionBundleDigest(ctx.sessionManager.getEntries(), sessionId, event.reason);\n\t\t\t\tconst activeStore = await openStore(sessionId, recordedDigest ?? (await getFallbackBundleDigest()));\n\t\t\t\tawait activeStore.append({\n\t\t\t\t\ttype: \"session_start\",\n\t\t\t\t\treason: event.reason,\n\t\t\t\t\tcwd: ctx.cwd,\n\t\t\t\t\tsessionFile: ctx.sessionManager.getSessionFile(),\n\t\t\t\t\tpreviousSessionFile: event.previousSessionFile,\n\t\t\t\t});\n\t\t\t\tif (event.reason === \"resume\" || event.reason === \"reload\") {\n\t\t\t\t\tawait backfillMissingUserMessages(activeStore, ctx);\n\t\t\t\t}\n\t\t\t\tawait buildSessionDigest(paths, activeStore.sessionId);\n\n\t\t\t\tconst hasReference = ctx.sessionManager.getEntries().some((entry) => {\n\t\t\t\t\tif (entry.type !== \"custom\" || entry.customType !== \"evo-recorder-ref\" || !isRecord(entry.data)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn entry.data.sessionId === sessionId;\n\t\t\t\t});\n\t\t\t\tif (!hasReference) {\n\t\t\t\t\tconst reference: RecorderSessionReference = {\n\t\t\t\t\t\tschemaVersion: 1,\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\tlogPath: activeStore.logPath,\n\t\t\t\t\t\tbundleDigest: activeStore.bundleDigest,\n\t\t\t\t\t};\n\t\t\t\t\ttry {\n\t\t\t\t\t\tpi.appendEntry(\"evo-recorder-ref\", reference);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\treportError(error);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\n\t\tpi.on(\"before_agent_start\", (event, ctx) =>\n\t\t\tenqueue(async () => {\n\t\t\t\tif (excludedSessionId === ctx.sessionManager.getSessionId()) return;\n\t\t\t\tconst activeStore = await getStore(ctx.sessionManager.getSessionId());\n\t\t\t\tconst prompt = await activeStore.storePayload(event.prompt);\n\t\t\t\tconst systemPrompt = await activeStore.storePayload(event.systemPrompt);\n\t\t\t\tconst systemPromptOptions = await activeStore.storePayload(event.systemPromptOptions);\n\t\t\t\tconst images = event.images ? await activeStore.storePayload(event.images) : undefined;\n\t\t\t\tawait activeStore.append({\n\t\t\t\t\ttype: \"before_agent_start\",\n\t\t\t\t\tprompt,\n\t\t\t\t\tsystemPrompt,\n\t\t\t\t\tsystemPromptOptions,\n\t\t\t\t\timages,\n\t\t\t\t});\n\t\t\t}),\n\t\t);\n\n\t\tpi.on(\"message_end\", (event, ctx) =>\n\t\t\tenqueue(async () => {\n\t\t\t\tif (excludedSessionId === ctx.sessionManager.getSessionId()) return;\n\t\t\t\tconst activeStore = await getStore(ctx.sessionManager.getSessionId());\n\t\t\t\tconst sourceEntry = [...ctx.sessionManager.getEntries()]\n\t\t\t\t\t.reverse()\n\t\t\t\t\t.find((entry) => entry.type === \"message\" && entry.message === event.message);\n\t\t\t\tawait activeStore.append({\n\t\t\t\t\ttype: \"message\",\n\t\t\t\t\trole: getString(event.message, \"role\") ?? \"unknown\",\n\t\t\t\t\tmessage: await activeStore.storePayload(event.message),\n\t\t\t\t\t...(sourceEntry ? { sourceEntryId: sourceEntry.id } : {}),\n\t\t\t\t});\n\n\t\t\t\tconst usage = getUsage(event.message);\n\t\t\t\tif (usage) {\n\t\t\t\t\tawait activeStore.append({\n\t\t\t\t\t\ttype: \"usage\",\n\t\t\t\t\t\tprovider: getString(event.message, \"provider\") ?? \"unknown\",\n\t\t\t\t\t\tmodel: getString(event.message, \"model\") ?? \"unknown\",\n\t\t\t\t\t\tusage,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\n\t\tpi.on(\"tool_execution_start\", (event, ctx) =>\n\t\t\tenqueue(async () => {\n\t\t\t\tif (excludedSessionId === ctx.sessionManager.getSessionId()) return;\n\t\t\t\tpendingTools.set(event.toolCallId, {\n\t\t\t\t\tinput: event.args,\n\t\t\t\t\tstartedAtMs: now().getTime(),\n\t\t\t\t});\n\t\t\t}),\n\t\t);\n\n\t\tpi.on(\"tool_call\", (event, ctx) =>\n\t\t\tenqueue(async () => {\n\t\t\t\tif (excludedSessionId === ctx.sessionManager.getSessionId()) return;\n\t\t\t\tconst pending = pendingTools.get(event.toolCallId);\n\t\t\t\tif (pending) pending.input = event.input;\n\t\t\t}),\n\t\t);\n\n\t\tpi.on(\"tool_result\", (event, ctx) =>\n\t\t\tenqueue(async () => {\n\t\t\t\tif (excludedSessionId === ctx.sessionManager.getSessionId()) return;\n\t\t\t\tconst pending = pendingTools.get(event.toolCallId);\n\t\t\t\tif (!pending) return;\n\t\t\t\tpending.input = event.input;\n\t\t\t\tpending.result = {\n\t\t\t\t\tcontent: event.content,\n\t\t\t\t\tdetails: event.details,\n\t\t\t\t};\n\t\t\t\tpending.isError = event.isError;\n\t\t\t}),\n\t\t);\n\n\t\tpi.on(\"tool_execution_end\", (event, ctx) =>\n\t\t\tenqueue(async () => {\n\t\t\t\tif (excludedSessionId === ctx.sessionManager.getSessionId()) return;\n\t\t\t\tconst endedAtMs = now().getTime();\n\t\t\t\tconst pending = pendingTools.get(event.toolCallId);\n\t\t\t\tpendingTools.delete(event.toolCallId);\n\t\t\t\tconst startedAtMs = pending?.startedAtMs ?? endedAtMs;\n\t\t\t\tconst input = pending?.input ?? {};\n\t\t\t\tconst result = pending?.result ?? event.result;\n\t\t\t\tconst isError = pending?.isError ?? event.isError;\n\t\t\t\tconst activeStore = await getStore(ctx.sessionManager.getSessionId());\n\t\t\t\tawait activeStore.append({\n\t\t\t\t\ttype: \"tool\",\n\t\t\t\t\ttoolCallId: event.toolCallId,\n\t\t\t\t\ttoolName: event.toolName,\n\t\t\t\t\tstartedAt: new Date(startedAtMs).toISOString(),\n\t\t\t\t\tendedAt: new Date(endedAtMs).toISOString(),\n\t\t\t\t\tdurationMs: Math.max(0, endedAtMs - startedAtMs),\n\t\t\t\t\tinput: await activeStore.storePayload(input),\n\t\t\t\t\tresult: await activeStore.storePayload(result),\n\t\t\t\t\tisError,\n\t\t\t\t\tverification: getVerification(event.toolName, input, result, isError),\n\t\t\t\t});\n\t\t\t}),\n\t\t);\n\n\t\tpi.on(\"session_before_compact\", (_event, ctx) =>\n\t\t\tenqueue(async () => {\n\t\t\t\tif (excludedSessionId === ctx.sessionManager.getSessionId()) return;\n\t\t\t\tcompactionStartedAtMs = now().getTime();\n\t\t\t}),\n\t\t);\n\n\t\tpi.on(\"session_compact\", (event, ctx) =>\n\t\t\tenqueue(async () => {\n\t\t\t\tif (excludedSessionId === ctx.sessionManager.getSessionId()) return;\n\t\t\t\tconst endedAtMs = now().getTime();\n\t\t\t\tconst startedAtMs = compactionStartedAtMs;\n\t\t\t\tcompactionStartedAtMs = undefined;\n\t\t\t\tconst activeStore = await getStore(ctx.sessionManager.getSessionId());\n\t\t\t\tawait activeStore.append({\n\t\t\t\t\ttype: \"compaction\",\n\t\t\t\t\treason: event.reason,\n\t\t\t\t\twillRetry: event.willRetry,\n\t\t\t\t\tfromExtension: event.fromExtension,\n\t\t\t\t\tfirstKeptEntryId: event.compactionEntry.firstKeptEntryId,\n\t\t\t\t\ttokensBefore: event.compactionEntry.tokensBefore,\n\t\t\t\t\tdurationMs: startedAtMs === undefined ? null : Math.max(0, endedAtMs - startedAtMs),\n\t\t\t\t\tsummary: await activeStore.storePayload(event.compactionEntry.summary),\n\t\t\t\t\t...(event.compactionEntry.details === undefined\n\t\t\t\t\t\t? {}\n\t\t\t\t\t\t: { details: await activeStore.storePayload(event.compactionEntry.details) }),\n\t\t\t\t});\n\t\t\t}),\n\t\t);\n\n\t\tpi.on(\"input\", (event, ctx) => {\n\t\t\tif (!isDurablePreferenceInstruction(event.text)) return;\n\t\t\treturn enqueue(async () => {\n\t\t\t\tif (excludedSessionId === ctx.sessionManager.getSessionId()) return;\n\t\t\t\tconst activeStore = await getStore(ctx.sessionManager.getSessionId());\n\t\t\t\tconst inbox = await activeStore.writeInbox(event.text, event.source, \"candidate\");\n\t\t\t\tawait initializeInboxLifecycle(paths, inbox.fileName);\n\t\t\t\tawait activeStore.append({\n\t\t\t\t\ttype: \"explicit_feedback\",\n\t\t\t\t\tsource: event.source,\n\t\t\t\t\ttext: await activeStore.storePayload(event.text),\n\t\t\t\t\tinboxFile: inbox.fileName,\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\n\t\tpi.on(\"session_shutdown\", (event, ctx) =>\n\t\t\tenqueue(async () => {\n\t\t\t\tif (excludedSessionId === ctx.sessionManager.getSessionId()) {\n\t\t\t\t\texcludedSessionId = undefined;\n\t\t\t\t\tpendingTools.clear();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst activeStore = await getStore(ctx.sessionManager.getSessionId());\n\t\t\t\ttry {\n\t\t\t\t\tconst execOptions = {\n\t\t\t\t\t\tcwd: ctx.cwd,\n\t\t\t\t\t\ttimeout: options.gitDiffTimeoutMs ?? 10_000,\n\t\t\t\t\t};\n\t\t\t\t\tlet diff = await pi.exec(\"git\", [\"diff\", \"--no-ext-diff\", \"--binary\", \"HEAD\", \"--\"], execOptions);\n\t\t\t\t\tif (diff.code !== 0 && !diff.killed) {\n\t\t\t\t\t\tdiff = await pi.exec(\"git\", [\"diff\", \"--no-ext-diff\", \"--binary\"], execOptions);\n\t\t\t\t\t}\n\t\t\t\t\tif (diff.code === 0 && !diff.killed) {\n\t\t\t\t\t\tawait activeStore.append({\n\t\t\t\t\t\t\ttype: \"git_diff\",\n\t\t\t\t\t\t\tcwd: ctx.cwd,\n\t\t\t\t\t\t\tclean: diff.stdout.length === 0,\n\t\t\t\t\t\t\tdiff: await activeStore.storePayload(diff.stdout),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\treportError(error);\n\t\t\t\t}\n\n\t\t\t\tawait activeStore.append({\n\t\t\t\t\ttype: \"session_end\",\n\t\t\t\t\treason: event.reason,\n\t\t\t\t\ttargetSessionFile: event.targetSessionFile,\n\t\t\t\t});\n\t\t\t\tawait buildSessionDigest(paths, activeStore.sessionId);\n\t\t\t\tpendingTools.clear();\n\t\t\t\tcompactionStartedAtMs = undefined;\n\t\t\t\tstore = undefined;\n\t\t\t}),\n\t\t);\n\t};\n}\n"]}