@averyyy/pi-coding-agent 0.82.1-piclient.6 → 0.82.1-piclient.7
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/CHANGELOG.md +0 -12
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +0 -5
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -12
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +89 -317
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/pi-server-client.d.ts +2 -54
- package/dist/core/pi-server-client.d.ts.map +1 -1
- package/dist/core/pi-server-client.js +272 -2320
- package/dist/core/pi-server-client.js.map +1 -1
- package/dist/core/pi-server-protocol.d.ts +0 -2
- package/dist/core/pi-server-protocol.d.ts.map +1 -1
- package/dist/core/pi-server-protocol.js +1 -24
- package/dist/core/pi-server-protocol.js.map +1 -1
- package/dist/core/pi-server-request.d.ts +0 -28
- package/dist/core/pi-server-request.d.ts.map +1 -1
- package/dist/core/pi-server-request.js +75 -492
- package/dist/core/pi-server-request.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +53 -69
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +2 -86
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +144 -737
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +1 -3
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +26 -46
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +0 -13
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +0 -15
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +29 -98
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/package.json +4 -4
- package/dist/cli/tool-effects.d.ts +0 -6
- package/dist/cli/tool-effects.d.ts.map +0 -1
- package/dist/cli/tool-effects.js +0 -164
- package/dist/cli/tool-effects.js.map +0 -1
- package/dist/core/pi-server-compact-state.d.ts +0 -81
- package/dist/core/pi-server-compact-state.d.ts.map +0 -1
- package/dist/core/pi-server-compact-state.js +0 -439
- package/dist/core/pi-server-compact-state.js.map +0 -1
- package/dist/core/pi-server-run-state.d.ts +0 -42
- package/dist/core/pi-server-run-state.d.ts.map +0 -1
- package/dist/core/pi-server-run-state.js +0 -320
- package/dist/core/pi-server-run-state.js.map +0 -1
- package/dist/core/tool-effect-journal.d.ts +0 -103
- package/dist/core/tool-effect-journal.d.ts.map +0 -1
- package/dist/core/tool-effect-journal.js +0 -1442
- package/dist/core/tool-effect-journal.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-effect-journal.d.ts","sourceRoot":"","sources":["../../src/core/tool-effect-journal.ts"],"names":[],"mappings":"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","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport {\n\tchmodSync,\n\tcloseSync,\n\texistsSync,\n\tfsyncSync,\n\topenSync,\n\treadSync,\n\trenameSync,\n\tstatSync,\n\ttruncateSync,\n\tunlinkSync,\n\twriteSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { StringDecoder } from \"node:string_decoder\";\nimport type { AssistantMessage, ToolResultMessage } from \"@earendil-works/pi-ai\";\nimport type { SessionManager, SessionMessageEntry } from \"./session-manager.ts\";\n\nconst TOOL_EFFECT_JOURNAL_VERSION = 1;\nconst TOOL_EFFECT_CHECKPOINT_VERSION = 1;\nconst JOURNAL_READ_BUFFER_BYTES = 1024 * 1024;\nconst SHA256_PATTERN = /^[a-f0-9]{64}$/;\nconst TOOL_EFFECT_JOURNAL_SUFFIX = \".tool-effects.jsonl\";\nconst DEFAULT_MAX_UNRESOLVED_EFFECTS = 1024;\nconst DEFAULT_MAX_UNRESOLVED_BYTES = 64 * 1024 * 1024;\nconst DEFAULT_CHECKPOINT_RECORDS = 4096;\nconst DEFAULT_CHECKPOINT_BYTES = 16 * 1024 * 1024;\nconst DEFAULT_WINDOWS_RENAME_RETRIES = 5;\nconst MAX_WINDOWS_RENAME_DELAY_MS = 100;\n\nconst TOOL_EFFECT_MAX_UNRESOLVED_EFFECTS_ENV = \"PI_TOOL_EFFECT_MAX_UNRESOLVED_EFFECTS\";\nconst TOOL_EFFECT_MAX_UNRESOLVED_BYTES_ENV = \"PI_TOOL_EFFECT_MAX_UNRESOLVED_BYTES\";\nconst TOOL_EFFECT_CHECKPOINT_RECORDS_ENV = \"PI_TOOL_EFFECT_CHECKPOINT_RECORDS\";\nconst TOOL_EFFECT_CHECKPOINT_BYTES_ENV = \"PI_TOOL_EFFECT_CHECKPOINT_BYTES\";\nconst TOOL_EFFECT_WINDOWS_RENAME_RETRIES_ENV = \"PI_TOOL_EFFECT_WINDOWS_RENAME_RETRIES\";\n\ninterface ToolEffectJournalConfig {\n\tmaxUnresolvedEffects: number;\n\tmaxUnresolvedBytes: number;\n\tcheckpointRecords: number;\n\tcheckpointBytes: number;\n\twindowsRenameRetries: number;\n}\n\nexport type ToolEffectCheckpointStage =\n\t| \"after_temp_open\"\n\t| \"after_temp_write\"\n\t| \"after_temp_fsync\"\n\t| \"before_replace\"\n\t| \"after_replace\"\n\t| \"before_directory_fsync\"\n\t| \"after_directory_fsync\";\n\nexport interface ToolEffectJournalTestHooks {\n\tonCheckpointStage?: (stage: ToolEffectCheckpointStage) => void;\n\tbeforeRenameAttempt?: (attempt: number) => void;\n}\n\nlet toolEffectJournalTestHooks: ToolEffectJournalTestHooks | undefined;\n\nexport function setToolEffectJournalTestHooks(hooks: ToolEffectJournalTestHooks | undefined): void {\n\ttoolEffectJournalTestHooks = hooks;\n}\n\ninterface ToolEffectRecordBase {\n\tversion: typeof TOOL_EFFECT_JOURNAL_VERSION;\n\tsequence: number;\n\ttimestamp: number;\n\teffectId: string;\n}\n\nexport interface ToolEffectIntentRecord extends ToolEffectRecordBase {\n\tkind: \"intent\";\n\tsessionId: string;\n\tassistantEntryId: string;\n\ttoolCallId: string;\n\ttoolName: string;\n\ttoolCallIndex: number;\n\targumentsHash: string;\n}\n\ninterface ToolEffectResultRecord extends ToolEffectRecordBase {\n\tkind: \"result\";\n\tmessage: ToolResultMessage;\n\tfinalizationPending?: true;\n}\n\ninterface ToolEffectFinalizationRecord extends ToolEffectRecordBase {\n\tkind: \"finalization\";\n}\n\ninterface ToolEffectFinalResultRecord extends ToolEffectRecordBase {\n\tkind: \"final\";\n\tmessage: ToolResultMessage;\n}\n\ninterface ToolEffectCommitRecord extends ToolEffectRecordBase {\n\tkind: \"commit\";\n\tsessionEntryId: string;\n}\n\ninterface ToolEffectCheckpointRecord {\n\tversion: typeof TOOL_EFFECT_JOURNAL_VERSION;\n\tcheckpointVersion: typeof TOOL_EFFECT_CHECKPOINT_VERSION;\n\tkind: \"checkpoint\";\n\tsequence: number;\n\ttimestamp: number;\n\tgeneration: number;\n\tpreviousRecordCount: number;\n\tpreviousSha256: string;\n\tsnapshotRecordCount: number;\n\tsnapshotSha256: string;\n}\n\ntype ToolEffectRecord =\n\t| ToolEffectIntentRecord\n\t| ToolEffectResultRecord\n\t| ToolEffectFinalizationRecord\n\t| ToolEffectFinalResultRecord\n\t| ToolEffectCommitRecord;\n\ntype ToolEffectJournalRecord = ToolEffectCheckpointRecord | ToolEffectRecord;\n\ninterface ToolEffectEnvelope {\n\tpayload: string;\n\tsha256: string;\n}\n\nexport interface DurableToolEffect {\n\tintent: ToolEffectIntentRecord;\n\tresult?: ToolResultMessage;\n\tcommittedSessionEntryId?: string;\n}\n\ninterface ParsedDurableToolEffect extends DurableToolEffect {\n\tresultRecordSeen: boolean;\n\texecutionResult?: ToolResultMessage;\n\tresultFinalizationPending: boolean;\n\tfinalizationStarted: boolean;\n\tfinalResultWritten: boolean;\n\tresultTimestamp?: number;\n\tfinalizationTimestamp?: number;\n\tfinalResultTimestamp?: number;\n}\n\ninterface ParsedToolEffectJournal {\n\tnextSequence: number;\n\teffects: Map<string, ParsedDurableToolEffect>;\n\tvalidBytes: number;\n\tfileBytes: number;\n\trecordCount: number;\n\tvalidSha256: string;\n\tcheckpointGeneration: number;\n}\n\nexport interface BeginToolEffectInput {\n\tsessionId: string;\n\tassistantEntryId: string;\n\ttoolCallId: string;\n\ttoolName: string;\n\ttoolCallIndex: number;\n\targuments: unknown;\n\ttimestamp?: number;\n}\n\nexport interface ToolEffectRecoveryResult {\n\trecoveredToolCallIds: string[];\n\tacknowledgedToolCallIds: string[];\n}\n\nexport type ToolEffectResolutionPhase = \"execution_unknown\" | \"finalization_unknown\" | \"result_durable\" | \"committed\";\n\nexport interface ToolEffectResolutionTarget {\n\tsessionId: string;\n\tsessionPath: string;\n\tjournalPath: string;\n\teffectId: string;\n\ttoolCallId: string;\n\ttoolName: string;\n\tphase: ToolEffectResolutionPhase;\n}\n\nexport interface ToolEffectResolutionResult {\n\ttarget: ToolEffectResolutionTarget;\n\tsessionEntryId: string;\n\talreadyResolved: boolean;\n}\n\nexport type UnknownToolEffectResolutionTarget = ToolEffectResolutionTarget & {\n\tphase: \"execution_unknown\" | \"finalization_unknown\";\n};\n\nexport interface SessionToolExecutionStart {\n\ttoolCallId: string;\n\ttoolName: string;\n\targs: unknown;\n}\n\ninterface RecoverableToolEffect {\n\teffect: ParsedDurableToolEffect;\n\tassistantIndex: number;\n\tpersisted?: SessionMessageEntry;\n}\n\nexport class ToolEffectUnknownOutcomeError extends Error {\n\treadonly code = \"unknown_outcome\";\n\treadonly toolCallIds: string[];\n\treadonly effects: UnknownToolEffectResolutionTarget[];\n\n\tconstructor(effects: UnknownToolEffectResolutionTarget[], message?: string) {\n\t\tconst details = effects\n\t\t\t.map(\n\t\t\t\t(effect) =>\n\t\t\t\t\t`sessionPath=${effect.sessionPath}\\njournalPath=${effect.journalPath}\\nsessionId=${effect.sessionId}\\neffectId=${effect.effectId}\\ntoolCallId=${effect.toolCallId}\\ntoolName=${effect.toolName}\\nphase=${effect.phase}`,\n\t\t\t)\n\t\t\t.join(\"\\n\\n\");\n\t\tsuper(\n\t\t\t[\n\t\t\t\tmessage ?? \"Cannot safely continue: tool effects may have occurred but their final outcomes are unknown.\",\n\t\t\t\tdetails,\n\t\t\t\t\"Resolve explicitly with `tool-effects mark-failed` or `tool-effects accept-result`; the tool will not be re-executed automatically.\",\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t\tthis.name = \"ToolEffectUnknownOutcomeError\";\n\t\tthis.toolCallIds = effects.map((effect) => effect.toolCallId);\n\t\tthis.effects = effects.map((effect) => ({ ...effect }));\n\t}\n}\n\nexport class ToolEffectRecoveryRequiredError extends Error {\n\treadonly code = \"recovery_required\";\n\treadonly toolCallId: string;\n\n\tconstructor(toolCallId: string) {\n\t\tsuper(`Cannot execute tool call ${toolCallId}: a durable result is waiting to be recovered into session history`);\n\t\tthis.name = \"ToolEffectRecoveryRequiredError\";\n\t\tthis.toolCallId = toolCallId;\n\t}\n}\n\nexport class ToolEffectJournalCapacityError extends Error {\n\treadonly code = \"tool_effect_journal_capacity\";\n\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ToolEffectJournalCapacityError\";\n\t}\n}\n\nfunction sha256(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nfunction readPositiveIntegerEnvironment(name: string, defaultValue: number): number {\n\tconst raw = process.env[name];\n\tif (raw === undefined) return defaultValue;\n\tif (!/^[1-9][0-9]*$/.test(raw)) {\n\t\tthrow new Error(`${name} must be a positive integer`);\n\t}\n\tconst value = Number(raw);\n\tif (!Number.isSafeInteger(value)) {\n\t\tthrow new Error(`${name} must be a safe integer`);\n\t}\n\treturn value;\n}\n\nfunction readNonNegativeIntegerEnvironment(name: string, defaultValue: number): number {\n\tconst raw = process.env[name];\n\tif (raw === undefined) return defaultValue;\n\tif (!/^(?:0|[1-9][0-9]*)$/.test(raw)) {\n\t\tthrow new Error(`${name} must be a non-negative integer`);\n\t}\n\tconst value = Number(raw);\n\tif (!Number.isSafeInteger(value)) {\n\t\tthrow new Error(`${name} must be a safe integer`);\n\t}\n\treturn value;\n}\n\nfunction getToolEffectJournalConfig(): ToolEffectJournalConfig {\n\treturn {\n\t\tmaxUnresolvedEffects: readPositiveIntegerEnvironment(\n\t\t\tTOOL_EFFECT_MAX_UNRESOLVED_EFFECTS_ENV,\n\t\t\tDEFAULT_MAX_UNRESOLVED_EFFECTS,\n\t\t),\n\t\tmaxUnresolvedBytes: readPositiveIntegerEnvironment(\n\t\t\tTOOL_EFFECT_MAX_UNRESOLVED_BYTES_ENV,\n\t\t\tDEFAULT_MAX_UNRESOLVED_BYTES,\n\t\t),\n\t\tcheckpointRecords: readPositiveIntegerEnvironment(TOOL_EFFECT_CHECKPOINT_RECORDS_ENV, DEFAULT_CHECKPOINT_RECORDS),\n\t\tcheckpointBytes: readPositiveIntegerEnvironment(TOOL_EFFECT_CHECKPOINT_BYTES_ENV, DEFAULT_CHECKPOINT_BYTES),\n\t\twindowsRenameRetries: readNonNegativeIntegerEnvironment(\n\t\t\tTOOL_EFFECT_WINDOWS_RENAME_RETRIES_ENV,\n\t\t\tDEFAULT_WINDOWS_RENAME_RETRIES,\n\t\t),\n\t};\n}\n\nfunction canonicalizeJson(value: unknown, ancestors: Set<object>): unknown {\n\tif (value === null || typeof value === \"string\" || typeof value === \"boolean\") {\n\t\treturn value;\n\t}\n\tif (typeof value === \"number\") {\n\t\tif (!Number.isFinite(value)) {\n\t\t\tthrow new Error(\"Tool effect identity cannot contain non-finite numbers\");\n\t\t}\n\t\treturn value;\n\t}\n\tif (Array.isArray(value)) {\n\t\tif (ancestors.has(value)) {\n\t\t\tthrow new Error(\"Tool effect identity cannot contain circular values\");\n\t\t}\n\t\tancestors.add(value);\n\t\ttry {\n\t\t\treturn value.map((item) => (item === undefined ? null : canonicalizeJson(item, ancestors)));\n\t\t} finally {\n\t\t\tancestors.delete(value);\n\t\t}\n\t}\n\tif (typeof value === \"object\") {\n\t\tif (ancestors.has(value)) {\n\t\t\tthrow new Error(\"Tool effect identity cannot contain circular values\");\n\t\t}\n\t\tancestors.add(value);\n\t\ttry {\n\t\t\tconst canonical: Record<string, unknown> = {};\n\t\t\tfor (const key of Object.keys(value).sort()) {\n\t\t\t\tconst item = (value as Record<string, unknown>)[key];\n\t\t\t\tif (item !== undefined) {\n\t\t\t\t\tcanonical[key] = canonicalizeJson(item, ancestors);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn canonical;\n\t\t} finally {\n\t\t\tancestors.delete(value);\n\t\t}\n\t}\n\tthrow new Error(`Tool effect identity cannot contain ${typeof value} values`);\n}\n\nfunction canonicalJson(value: unknown): string {\n\treturn JSON.stringify(canonicalizeJson(value, new Set()));\n}\n\nfunction canonicalPersistedJson(value: unknown): string {\n\tconst serialized = JSON.stringify(value);\n\tif (serialized === undefined) {\n\t\tthrow new Error(\"Tool effect result is not JSON serializable\");\n\t}\n\treturn canonicalJson(JSON.parse(serialized) as unknown);\n}\n\nfunction assertObject(value: unknown, path: string): asserts value is Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(`${path} must be an object`);\n\t}\n}\n\nfunction assertNonEmptyString(value: unknown, path: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.length === 0) {\n\t\tthrow new Error(`${path} must be a non-empty string`);\n\t}\n}\n\nfunction assertHash(value: unknown, path: string): asserts value is string {\n\tif (typeof value !== \"string\" || !SHA256_PATTERN.test(value)) {\n\t\tthrow new Error(`${path} must be a 64-character lowercase hex digest`);\n\t}\n}\n\nfunction assertNonNegativeSafeInteger(value: unknown, path: string): asserts value is number {\n\tif (!Number.isSafeInteger(value) || (value as number) < 0) {\n\t\tthrow new Error(`${path} must be a non-negative safe integer`);\n\t}\n}\n\nfunction assertToolResultMessage(value: unknown, path: string): asserts value is ToolResultMessage {\n\tassertObject(value, path);\n\tif (value.role !== \"toolResult\") {\n\t\tthrow new Error(`${path}.role must be toolResult`);\n\t}\n\tassertNonEmptyString(value.toolCallId, `${path}.toolCallId`);\n\tassertNonEmptyString(value.toolName, `${path}.toolName`);\n\tif (!Array.isArray(value.content)) {\n\t\tthrow new Error(`${path}.content must be an array`);\n\t}\n\tfor (const [index, content] of value.content.entries()) {\n\t\tassertObject(content, `${path}.content[${index}]`);\n\t\tif (content.type === \"text\") {\n\t\t\tif (typeof content.text !== \"string\") {\n\t\t\t\tthrow new Error(`${path}.content[${index}].text must be a string`);\n\t\t\t}\n\t\t\tif (content.textSignature !== undefined && typeof content.textSignature !== \"string\") {\n\t\t\t\tthrow new Error(`${path}.content[${index}].textSignature must be a string when present`);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (content.type === \"image\") {\n\t\t\tif (typeof content.data !== \"string\") {\n\t\t\t\tthrow new Error(`${path}.content[${index}].data must be a string`);\n\t\t\t}\n\t\t\tif (typeof content.mimeType !== \"string\") {\n\t\t\t\tthrow new Error(`${path}.content[${index}].mimeType must be a string`);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new Error(`${path}.content[${index}].type must be text or image`);\n\t}\n\tif (typeof value.isError !== \"boolean\") {\n\t\tthrow new Error(`${path}.isError must be a boolean`);\n\t}\n\tassertNonNegativeSafeInteger(value.timestamp, `${path}.timestamp`);\n\tif (value.addedToolNames !== undefined) {\n\t\tif (!Array.isArray(value.addedToolNames)) {\n\t\t\tthrow new Error(`${path}.addedToolNames must be an array when present`);\n\t\t}\n\t\tfor (const [index, toolName] of value.addedToolNames.entries()) {\n\t\t\tassertNonEmptyString(toolName, `${path}.addedToolNames[${index}]`);\n\t\t}\n\t}\n\tif (value.usage !== undefined) {\n\t\tassertObject(value.usage, `${path}.usage`);\n\t\tfor (const key of [\"input\", \"output\", \"cacheRead\", \"cacheWrite\", \"totalTokens\"] as const) {\n\t\t\tassertNonNegativeSafeInteger(value.usage[key], `${path}.usage.${key}`);\n\t\t}\n\t\tfor (const key of [\"cacheWrite1h\", \"reasoning\"] as const) {\n\t\t\tif (value.usage[key] !== undefined) {\n\t\t\t\tassertNonNegativeSafeInteger(value.usage[key], `${path}.usage.${key}`);\n\t\t\t}\n\t\t}\n\t\tassertObject(value.usage.cost, `${path}.usage.cost`);\n\t\tfor (const key of [\"input\", \"output\", \"cacheRead\", \"cacheWrite\", \"total\"] as const) {\n\t\t\tconst cost = value.usage.cost[key];\n\t\t\tif (typeof cost !== \"number\" || !Number.isFinite(cost) || cost < 0) {\n\t\t\t\tthrow new Error(`${path}.usage.cost.${key} must be a non-negative finite number`);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction parseRecord(payload: string, lineNumber: number): ToolEffectJournalRecord {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(payload) as unknown;\n\t} catch (error) {\n\t\tthrow new Error(`Tool effect journal payload is invalid JSON at line ${lineNumber}`, { cause: error });\n\t}\n\tassertObject(value, `Tool effect journal line ${lineNumber}`);\n\tif (value.version !== TOOL_EFFECT_JOURNAL_VERSION) {\n\t\tthrow new Error(`Unsupported tool effect journal version at line ${lineNumber}`);\n\t}\n\tassertNonNegativeSafeInteger(value.sequence, `Tool effect journal line ${lineNumber}.sequence`);\n\tassertNonNegativeSafeInteger(value.timestamp, `Tool effect journal line ${lineNumber}.timestamp`);\n\tif (value.kind === \"checkpoint\") {\n\t\tif (value.checkpointVersion !== TOOL_EFFECT_CHECKPOINT_VERSION) {\n\t\t\tthrow new Error(`Unsupported tool effect checkpoint version at line ${lineNumber}`);\n\t\t}\n\t\tassertNonNegativeSafeInteger(value.generation, `Tool effect journal line ${lineNumber}.generation`);\n\t\tif (value.generation === 0) {\n\t\t\tthrow new Error(`Tool effect journal line ${lineNumber}.generation must be positive`);\n\t\t}\n\t\tassertNonNegativeSafeInteger(\n\t\t\tvalue.previousRecordCount,\n\t\t\t`Tool effect journal line ${lineNumber}.previousRecordCount`,\n\t\t);\n\t\tassertHash(value.previousSha256, `Tool effect journal line ${lineNumber}.previousSha256`);\n\t\tassertNonNegativeSafeInteger(\n\t\t\tvalue.snapshotRecordCount,\n\t\t\t`Tool effect journal line ${lineNumber}.snapshotRecordCount`,\n\t\t);\n\t\tassertHash(value.snapshotSha256, `Tool effect journal line ${lineNumber}.snapshotSha256`);\n\t\treturn {\n\t\t\tversion: TOOL_EFFECT_JOURNAL_VERSION,\n\t\t\tcheckpointVersion: TOOL_EFFECT_CHECKPOINT_VERSION,\n\t\t\tkind: \"checkpoint\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\tgeneration: value.generation,\n\t\t\tpreviousRecordCount: value.previousRecordCount,\n\t\t\tpreviousSha256: value.previousSha256,\n\t\t\tsnapshotRecordCount: value.snapshotRecordCount,\n\t\t\tsnapshotSha256: value.snapshotSha256,\n\t\t};\n\t}\n\tassertHash(value.effectId, `Tool effect journal line ${lineNumber}.effectId`);\n\n\tconst base: ToolEffectRecordBase = {\n\t\tversion: TOOL_EFFECT_JOURNAL_VERSION,\n\t\tsequence: value.sequence,\n\t\ttimestamp: value.timestamp,\n\t\teffectId: value.effectId,\n\t};\n\tif (value.kind === \"intent\") {\n\t\tassertNonEmptyString(value.sessionId, `Tool effect journal line ${lineNumber}.sessionId`);\n\t\tassertNonEmptyString(value.assistantEntryId, `Tool effect journal line ${lineNumber}.assistantEntryId`);\n\t\tassertNonEmptyString(value.toolCallId, `Tool effect journal line ${lineNumber}.toolCallId`);\n\t\tassertNonEmptyString(value.toolName, `Tool effect journal line ${lineNumber}.toolName`);\n\t\tassertNonNegativeSafeInteger(value.toolCallIndex, `Tool effect journal line ${lineNumber}.toolCallIndex`);\n\t\tassertHash(value.argumentsHash, `Tool effect journal line ${lineNumber}.argumentsHash`);\n\t\treturn {\n\t\t\t...base,\n\t\t\tkind: \"intent\",\n\t\t\tsessionId: value.sessionId,\n\t\t\tassistantEntryId: value.assistantEntryId,\n\t\t\ttoolCallId: value.toolCallId,\n\t\t\ttoolName: value.toolName,\n\t\t\ttoolCallIndex: value.toolCallIndex,\n\t\t\targumentsHash: value.argumentsHash,\n\t\t};\n\t}\n\tif (value.kind === \"result\") {\n\t\tassertToolResultMessage(value.message, `Tool effect journal line ${lineNumber}.message`);\n\t\tif (value.finalizationPending !== undefined && value.finalizationPending !== true) {\n\t\t\tthrow new Error(`Tool effect journal line ${lineNumber}.finalizationPending must be true when present`);\n\t\t}\n\t\treturn {\n\t\t\t...base,\n\t\t\tkind: \"result\",\n\t\t\tmessage: value.message,\n\t\t\tfinalizationPending: value.finalizationPending,\n\t\t};\n\t}\n\tif (value.kind === \"finalization\") {\n\t\treturn { ...base, kind: \"finalization\" };\n\t}\n\tif (value.kind === \"final\") {\n\t\tassertToolResultMessage(value.message, `Tool effect journal line ${lineNumber}.message`);\n\t\treturn { ...base, kind: \"final\", message: value.message };\n\t}\n\tif (value.kind === \"commit\") {\n\t\tassertNonEmptyString(value.sessionEntryId, `Tool effect journal line ${lineNumber}.sessionEntryId`);\n\t\treturn { ...base, kind: \"commit\", sessionEntryId: value.sessionEntryId };\n\t}\n\tthrow new Error(`Unsupported tool effect journal record kind at line ${lineNumber}`);\n}\n\nfunction applyRecord(\n\teffects: Map<string, ParsedDurableToolEffect>,\n\trecord: ToolEffectRecord,\n\tlineNumber: number,\n): void {\n\tif (record.kind === \"intent\") {\n\t\tif (effects.has(record.effectId)) {\n\t\t\tthrow new Error(`Duplicate tool effect intent at line ${lineNumber}`);\n\t\t}\n\t\tfor (const effect of effects.values()) {\n\t\t\tif (\n\t\t\t\teffect.intent.assistantEntryId === record.assistantEntryId &&\n\t\t\t\teffect.intent.toolCallIndex === record.toolCallIndex\n\t\t\t) {\n\t\t\t\tthrow new Error(`Conflicting tool effect intent slot at line ${lineNumber}`);\n\t\t\t}\n\t\t}\n\t\teffects.set(record.effectId, {\n\t\t\tintent: record,\n\t\t\tresultRecordSeen: false,\n\t\t\tresultFinalizationPending: false,\n\t\t\tfinalizationStarted: false,\n\t\t\tfinalResultWritten: false,\n\t\t});\n\t\treturn;\n\t}\n\tconst effect = effects.get(record.effectId);\n\tif (!effect) {\n\t\tthrow new Error(`Tool effect ${record.kind} has no matching intent at line ${lineNumber}`);\n\t}\n\tif (effect.committedSessionEntryId) {\n\t\tthrow new Error(`Tool effect record follows commit at line ${lineNumber}`);\n\t}\n\tif (record.kind === \"result\") {\n\t\tif (\n\t\t\trecord.message.toolCallId !== effect.intent.toolCallId ||\n\t\t\trecord.message.toolName !== effect.intent.toolName\n\t\t) {\n\t\t\tthrow new Error(`Tool effect result identity mismatch at line ${lineNumber}`);\n\t\t}\n\t\tif (effect.resultRecordSeen) {\n\t\t\tthrow new Error(`Duplicate tool effect result at line ${lineNumber}`);\n\t\t}\n\t\teffect.resultRecordSeen = true;\n\t\teffect.executionResult = record.message;\n\t\teffect.resultFinalizationPending = record.finalizationPending === true;\n\t\teffect.finalizationStarted = record.finalizationPending === true;\n\t\teffect.result = record.message;\n\t\teffect.resultTimestamp = record.timestamp;\n\t\tif (record.finalizationPending === true) {\n\t\t\teffect.finalizationTimestamp = record.timestamp;\n\t\t}\n\t\treturn;\n\t}\n\tif (record.kind === \"finalization\") {\n\t\tif (!effect.resultRecordSeen || !effect.result) {\n\t\t\tthrow new Error(`Tool effect finalization precedes its durable result at line ${lineNumber}`);\n\t\t}\n\t\tif (effect.finalizationStarted) {\n\t\t\tthrow new Error(`Duplicate tool effect finalization at line ${lineNumber}`);\n\t\t}\n\t\teffect.finalizationStarted = true;\n\t\teffect.finalizationTimestamp = record.timestamp;\n\t\treturn;\n\t}\n\tif (record.kind === \"final\") {\n\t\tif (!effect.finalizationStarted) {\n\t\t\tthrow new Error(`Tool effect final result has no matching finalization at line ${lineNumber}`);\n\t\t}\n\t\tif (effect.finalResultWritten) {\n\t\t\tthrow new Error(`Duplicate tool effect final result at line ${lineNumber}`);\n\t\t}\n\t\tif (\n\t\t\trecord.message.toolCallId !== effect.intent.toolCallId ||\n\t\t\trecord.message.toolName !== effect.intent.toolName\n\t\t) {\n\t\t\tthrow new Error(`Tool effect final result identity mismatch at line ${lineNumber}`);\n\t\t}\n\t\teffect.finalResultWritten = true;\n\t\teffect.result = record.message;\n\t\teffect.finalResultTimestamp = record.timestamp;\n\t\treturn;\n\t}\n\tif (!effect.result) {\n\t\tthrow new Error(`Tool effect commit precedes its durable result at line ${lineNumber}`);\n\t}\n\tif (effect.finalizationStarted && !effect.finalResultWritten) {\n\t\tthrow new Error(`Tool effect commit precedes its final result at line ${lineNumber}`);\n\t}\n\teffect.committedSessionEntryId = record.sessionEntryId;\n}\n\nfunction findLastCompleteRecordBytes(path: string, fileBytes: number): number {\n\tif (fileBytes === 0) return 0;\n\tconst handle = openSync(path, \"r\");\n\ttry {\n\t\tconst buffer = Buffer.allocUnsafe(Math.min(JOURNAL_READ_BUFFER_BYTES, fileBytes));\n\t\tlet end = fileBytes;\n\t\twhile (end > 0) {\n\t\t\tconst start = Math.max(0, end - buffer.byteLength);\n\t\t\tconst length = end - start;\n\t\t\tconst bytesRead = readSync(handle, buffer, 0, length, start);\n\t\t\tif (bytesRead !== length) {\n\t\t\t\tthrow new Error(`Could not read tool effect journal tail: ${path}`);\n\t\t\t}\n\t\t\tconst newlineIndex = buffer.lastIndexOf(0x0a, length - 1);\n\t\t\tif (newlineIndex !== -1) {\n\t\t\t\treturn start + newlineIndex + 1;\n\t\t\t}\n\t\t\tend = start;\n\t\t}\n\t\treturn 0;\n\t} finally {\n\t\tcloseSync(handle);\n\t}\n}\n\nfunction createEmptyParsedJournal(fileBytes = 0): ParsedToolEffectJournal {\n\treturn {\n\t\tnextSequence: 0,\n\t\teffects: new Map(),\n\t\tvalidBytes: 0,\n\t\tfileBytes,\n\t\trecordCount: 0,\n\t\tvalidSha256: sha256(\"\"),\n\t\tcheckpointGeneration: 0,\n\t};\n}\n\nfunction parseJournal(path: string): ParsedToolEffectJournal {\n\tif (!existsSync(path)) {\n\t\treturn createEmptyParsedJournal();\n\t}\n\tconst fileBytes = statSync(path).size;\n\tconst validBytes = findLastCompleteRecordBytes(path, fileBytes);\n\tif (validBytes === 0) {\n\t\treturn createEmptyParsedJournal(fileBytes);\n\t}\n\n\tconst effects = new Map<string, ParsedDurableToolEffect>();\n\tconst handle = openSync(path, \"r\");\n\ttry {\n\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\tconst validFileHash = createHash(\"sha256\");\n\t\tconst snapshotHash = createHash(\"sha256\");\n\t\tconst buffer = Buffer.allocUnsafe(Math.min(JOURNAL_READ_BUFFER_BYTES, validBytes));\n\t\tlet pending = \"\";\n\t\tlet position = 0;\n\t\tlet lineNumber = 0;\n\t\tlet expectedSequence = 0;\n\t\tlet checkpoint: ToolEffectCheckpointRecord | undefined;\n\t\tlet snapshotRecordsSeen = 0;\n\n\t\tconst consumeLine = (line: string): void => {\n\t\t\tlineNumber++;\n\t\t\tif (line.length === 0) {\n\t\t\t\tthrow new Error(`Tool effect journal contains an empty record at line ${lineNumber}`);\n\t\t\t}\n\t\t\tlet envelopeValue: unknown;\n\t\t\ttry {\n\t\t\t\tenvelopeValue = JSON.parse(line) as unknown;\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(`Tool effect journal envelope is invalid JSON at line ${lineNumber}`, { cause: error });\n\t\t\t}\n\t\t\tassertObject(envelopeValue, `Tool effect journal envelope line ${lineNumber}`);\n\t\t\tif (typeof envelopeValue.payload !== \"string\") {\n\t\t\t\tthrow new Error(`Tool effect journal envelope payload must be a string at line ${lineNumber}`);\n\t\t\t}\n\t\t\tassertHash(envelopeValue.sha256, `Tool effect journal envelope line ${lineNumber}.sha256`);\n\t\t\tif (sha256(envelopeValue.payload) !== envelopeValue.sha256) {\n\t\t\t\tthrow new Error(`Tool effect journal checksum mismatch at line ${lineNumber}`);\n\t\t\t}\n\t\t\tconst record = parseRecord(envelopeValue.payload, lineNumber);\n\t\t\tif (record.sequence !== expectedSequence) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Tool effect journal sequence mismatch at line ${lineNumber}: expected ${expectedSequence}, received ${record.sequence}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\texpectedSequence++;\n\t\t\tif (record.kind === \"checkpoint\") {\n\t\t\t\tif (lineNumber !== 1 || checkpoint) {\n\t\t\t\t\tthrow new Error(`Tool effect checkpoint must be the first and only checkpoint record`);\n\t\t\t\t}\n\t\t\t\tcheckpoint = record;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (checkpoint && snapshotRecordsSeen < checkpoint.snapshotRecordCount) {\n\t\t\t\tif (record.kind === \"commit\") {\n\t\t\t\t\tthrow new Error(`Tool effect checkpoint snapshot contains a committed effect at line ${lineNumber}`);\n\t\t\t\t}\n\t\t\t\tsnapshotHash.update(envelopeValue.payload);\n\t\t\t\tsnapshotHash.update(\"\\n\");\n\t\t\t\tsnapshotRecordsSeen++;\n\t\t\t}\n\t\t\tapplyRecord(effects, record, lineNumber);\n\t\t};\n\n\t\twhile (position < validBytes) {\n\t\t\tconst length = Math.min(buffer.byteLength, validBytes - position);\n\t\t\tconst bytesRead = readSync(handle, buffer, 0, length, position);\n\t\t\tif (bytesRead === 0) {\n\t\t\t\tthrow new Error(`Tool effect journal ended before its last complete record: ${path}`);\n\t\t\t}\n\t\t\tposition += bytesRead;\n\t\t\tvalidFileHash.update(buffer.subarray(0, bytesRead));\n\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\tlet lineStart = 0;\n\t\t\tlet newlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\tconsumeLine(pending.slice(lineStart, newlineIndex));\n\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\tnewlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t}\n\t\t\tpending = pending.slice(lineStart);\n\t\t}\n\t\tpending += decoder.end();\n\t\tif (pending.length !== 0) {\n\t\t\tthrow new Error(`Tool effect journal complete prefix does not end on a record boundary: ${path}`);\n\t\t}\n\t\tif (checkpoint && snapshotRecordsSeen !== checkpoint.snapshotRecordCount) {\n\t\t\tthrow new Error(\n\t\t\t\t`Tool effect checkpoint snapshot is incomplete: expected ${checkpoint.snapshotRecordCount} records, received ${snapshotRecordsSeen}`,\n\t\t\t);\n\t\t}\n\t\tif (checkpoint && snapshotHash.digest(\"hex\") !== checkpoint.snapshotSha256) {\n\t\t\tthrow new Error(`Tool effect checkpoint snapshot checksum mismatch`);\n\t\t}\n\t\treturn {\n\t\t\tnextSequence: expectedSequence,\n\t\t\teffects,\n\t\t\tvalidBytes,\n\t\t\tfileBytes,\n\t\t\trecordCount: lineNumber,\n\t\t\tvalidSha256: validFileHash.digest(\"hex\"),\n\t\t\tcheckpointGeneration: checkpoint?.generation ?? 0,\n\t\t};\n\t} finally {\n\t\tcloseSync(handle);\n\t}\n}\n\nfunction encodeRecord(record: ToolEffectJournalRecord): { payload: string; encoded: Buffer } {\n\tconst payload = JSON.stringify(record);\n\tconst envelope: ToolEffectEnvelope = { payload, sha256: sha256(payload) };\n\treturn { payload, encoded: Buffer.from(`${JSON.stringify(envelope)}\\n`, \"utf8\") };\n}\n\nfunction appendRecord(path: string, state: ParsedToolEffectJournal, record: ToolEffectRecord): void {\n\tif (state.validBytes !== state.fileBytes) {\n\t\ttruncateSync(path, state.validBytes);\n\t}\n\tconst { encoded } = encodeRecord(record);\n\tconst fileExisted = existsSync(path);\n\tconst handle = openSync(path, \"a\", 0o600);\n\ttry {\n\t\tlet written = 0;\n\t\twhile (written < encoded.byteLength) {\n\t\t\tconst bytesWritten = writeSync(handle, encoded, written, encoded.byteLength - written);\n\t\t\tif (bytesWritten === 0) {\n\t\t\t\tthrow new Error(`Failed to make progress writing tool effect journal: ${path}`);\n\t\t\t}\n\t\t\twritten += bytesWritten;\n\t\t}\n\t\tfsyncSync(handle);\n\t} finally {\n\t\tcloseSync(handle);\n\t}\n\tif (!fileExisted && process.platform !== \"win32\") {\n\t\tconst directoryHandle = openSync(dirname(path), \"r\");\n\t\ttry {\n\t\t\tfsyncSync(directoryHandle);\n\t\t} finally {\n\t\t\tcloseSync(directoryHandle);\n\t\t}\n\t}\n}\n\nfunction requireRecordTimestamp(value: number | undefined, label: string): number {\n\tif (value === undefined) {\n\t\tthrow new Error(`Tool effect journal lost ${label} timestamp`);\n\t}\n\treturn value;\n}\n\nfunction buildUnresolvedSnapshotRecords(\n\teffects: Iterable<ParsedDurableToolEffect>,\n\tstartSequence: number,\n): ToolEffectRecord[] {\n\tconst records: ToolEffectRecord[] = [];\n\tlet sequence = startSequence;\n\tfor (const effect of effects) {\n\t\tif (effect.committedSessionEntryId !== undefined) continue;\n\t\trecords.push({ ...effect.intent, sequence: sequence++ });\n\t\tif (!effect.resultRecordSeen) continue;\n\t\tif (!effect.executionResult) {\n\t\t\tthrow new Error(`Tool effect ${effect.intent.effectId} has a result marker without an execution result`);\n\t\t}\n\t\trecords.push({\n\t\t\tversion: TOOL_EFFECT_JOURNAL_VERSION,\n\t\t\tkind: \"result\",\n\t\t\tsequence: sequence++,\n\t\t\ttimestamp: requireRecordTimestamp(effect.resultTimestamp, \"result\"),\n\t\t\teffectId: effect.intent.effectId,\n\t\t\tmessage: effect.executionResult,\n\t\t\t...(effect.resultFinalizationPending ? { finalizationPending: true as const } : {}),\n\t\t});\n\t\tif (effect.finalizationStarted && !effect.resultFinalizationPending) {\n\t\t\trecords.push({\n\t\t\t\tversion: TOOL_EFFECT_JOURNAL_VERSION,\n\t\t\t\tkind: \"finalization\",\n\t\t\t\tsequence: sequence++,\n\t\t\t\ttimestamp: requireRecordTimestamp(effect.finalizationTimestamp, \"finalization\"),\n\t\t\t\teffectId: effect.intent.effectId,\n\t\t\t});\n\t\t}\n\t\tif (effect.finalResultWritten) {\n\t\t\tif (!effect.result) {\n\t\t\t\tthrow new Error(`Tool effect ${effect.intent.effectId} has a final marker without a final result`);\n\t\t\t}\n\t\t\trecords.push({\n\t\t\t\tversion: TOOL_EFFECT_JOURNAL_VERSION,\n\t\t\t\tkind: \"final\",\n\t\t\t\tsequence: sequence++,\n\t\t\t\ttimestamp: requireRecordTimestamp(effect.finalResultTimestamp, \"final result\"),\n\t\t\t\teffectId: effect.intent.effectId,\n\t\t\t\tmessage: effect.result,\n\t\t\t});\n\t\t}\n\t}\n\treturn records;\n}\n\nfunction hashSnapshotPayloads(payloads: readonly string[]): string {\n\tconst hash = createHash(\"sha256\");\n\tfor (const payload of payloads) {\n\t\thash.update(payload);\n\t\thash.update(\"\\n\");\n\t}\n\treturn hash.digest(\"hex\");\n}\n\nfunction checkpointStateProjection(effects: Iterable<ParsedDurableToolEffect>): string {\n\treturn canonicalPersistedJson(\n\t\t[...effects]\n\t\t\t.filter((effect) => effect.committedSessionEntryId === undefined)\n\t\t\t.map((effect) => ({\n\t\t\t\tintent: { ...effect.intent, sequence: 0 },\n\t\t\t\tresultRecordSeen: effect.resultRecordSeen,\n\t\t\t\texecutionResult: effect.executionResult,\n\t\t\t\tresultFinalizationPending: effect.resultFinalizationPending,\n\t\t\t\tfinalizationStarted: effect.finalizationStarted,\n\t\t\t\tfinalResultWritten: effect.finalResultWritten,\n\t\t\t\tresult: effect.result,\n\t\t\t\tresultTimestamp: effect.resultTimestamp,\n\t\t\t\tfinalizationTimestamp: effect.finalizationTimestamp,\n\t\t\t\tfinalResultTimestamp: effect.finalResultTimestamp,\n\t\t\t})),\n\t);\n}\n\nfunction getErrorCode(error: unknown): string | undefined {\n\tif (typeof error !== \"object\" || error === null || !(\"code\" in error)) return undefined;\n\treturn typeof error.code === \"string\" ? error.code : undefined;\n}\n\nfunction waitForWindowsRenameRetry(attempt: number): void {\n\tconst delay = Math.min(2 ** attempt * 5, MAX_WINDOWS_RENAME_DELAY_MS);\n\tAtomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delay);\n}\n\nfunction replaceCheckpointFile(tempPath: string, path: string, windowsRenameRetries: number): void {\n\tfor (let attempt = 0; ; attempt++) {\n\t\ttry {\n\t\t\ttoolEffectJournalTestHooks?.beforeRenameAttempt?.(attempt);\n\t\t\trenameSync(tempPath, path);\n\t\t\treturn;\n\t\t} catch (error) {\n\t\t\tif (process.platform !== \"win32\" || getErrorCode(error) !== \"EPERM\" || attempt >= windowsRenameRetries) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\twaitForWindowsRenameRetry(attempt);\n\t\t}\n\t}\n}\n\nfunction fsyncCheckpointDirectory(path: string): void {\n\tif (process.platform === \"win32\") {\n\t\t// libuv cannot fsync a Windows directory handle. Re-open and fsync the\n\t\t// replaced file so MoveFileEx/rename metadata and file contents are flushed.\n\t\tconst handle = openSync(path, \"r+\");\n\t\ttry {\n\t\t\tfsyncSync(handle);\n\t\t} finally {\n\t\t\tcloseSync(handle);\n\t\t}\n\t\treturn;\n\t}\n\tconst handle = openSync(dirname(path), \"r\");\n\ttry {\n\t\tfsyncSync(handle);\n\t} finally {\n\t\tcloseSync(handle);\n\t}\n}\n\nfunction writeCheckpointAtomically(\n\tpath: string,\n\tencoded: Buffer,\n\texpectedProjection: string,\n\tconfig: ToolEffectJournalConfig,\n): ParsedToolEffectJournal {\n\tconst tempPath = `${path}.checkpoint-${process.pid}-${randomUUID()}.tmp`;\n\tlet tempHandle: number | undefined;\n\tlet replaced = false;\n\tlet primaryError: unknown;\n\tlet result: ParsedToolEffectJournal | undefined;\n\tconst cleanupErrors: unknown[] = [];\n\ttry {\n\t\ttempHandle = openSync(tempPath, \"wx\", 0o600);\n\t\tchmodSync(tempPath, 0o600);\n\t\ttoolEffectJournalTestHooks?.onCheckpointStage?.(\"after_temp_open\");\n\t\tlet written = 0;\n\t\twhile (written < encoded.byteLength) {\n\t\t\tconst bytesWritten = writeSync(tempHandle, encoded, written, encoded.byteLength - written);\n\t\t\tif (bytesWritten === 0) {\n\t\t\t\tthrow new Error(`Failed to make progress writing tool effect checkpoint: ${tempPath}`);\n\t\t\t}\n\t\t\twritten += bytesWritten;\n\t\t}\n\t\ttoolEffectJournalTestHooks?.onCheckpointStage?.(\"after_temp_write\");\n\t\tfsyncSync(tempHandle);\n\t\ttoolEffectJournalTestHooks?.onCheckpointStage?.(\"after_temp_fsync\");\n\t\tcloseSync(tempHandle);\n\t\ttempHandle = undefined;\n\n\t\tconst checkpointState = parseJournal(tempPath);\n\t\tif (checkpointStateProjection(checkpointState.effects.values()) !== expectedProjection) {\n\t\t\tthrow new Error(`Tool effect checkpoint verification did not preserve unresolved state`);\n\t\t}\n\n\t\ttoolEffectJournalTestHooks?.onCheckpointStage?.(\"before_replace\");\n\t\treplaceCheckpointFile(tempPath, path, config.windowsRenameRetries);\n\t\treplaced = true;\n\t\ttoolEffectJournalTestHooks?.onCheckpointStage?.(\"after_replace\");\n\t\ttoolEffectJournalTestHooks?.onCheckpointStage?.(\"before_directory_fsync\");\n\t\tfsyncCheckpointDirectory(path);\n\t\ttoolEffectJournalTestHooks?.onCheckpointStage?.(\"after_directory_fsync\");\n\t\tresult = checkpointState;\n\t} catch (error) {\n\t\tprimaryError = error;\n\t} finally {\n\t\tif (tempHandle !== undefined) {\n\t\t\ttry {\n\t\t\t\tcloseSync(tempHandle);\n\t\t\t} catch (closeError) {\n\t\t\t\tcleanupErrors.push(closeError);\n\t\t\t}\n\t\t}\n\t\tif (!replaced && existsSync(tempPath)) {\n\t\t\ttry {\n\t\t\t\tunlinkSync(tempPath);\n\t\t\t} catch (cleanupError) {\n\t\t\t\tcleanupErrors.push(cleanupError);\n\t\t\t}\n\t\t}\n\t}\n\tif (primaryError !== undefined && cleanupErrors.length === 0) {\n\t\tthrow primaryError;\n\t}\n\tif (primaryError !== undefined || cleanupErrors.length > 0) {\n\t\tthrow new AggregateError(\n\t\t\tprimaryError === undefined ? cleanupErrors : [primaryError, ...cleanupErrors],\n\t\t\t`Failed to write tool effect checkpoint`,\n\t\t);\n\t}\n\tif (!result) {\n\t\tthrow new Error(`Tool effect checkpoint did not produce a verified state`);\n\t}\n\treturn result;\n}\n\nfunction checkpointJournal(\n\tpath: string,\n\tstate: ParsedToolEffectJournal,\n\tconfig: ToolEffectJournalConfig,\n): ParsedToolEffectJournal {\n\tif (state.recordCount === 0) return state;\n\tconst snapshotRecords = buildUnresolvedSnapshotRecords(state.effects.values(), 1);\n\tconst snapshotEncodings = snapshotRecords.map((record) => encodeRecord(record));\n\tconst checkpoint: ToolEffectCheckpointRecord = {\n\t\tversion: TOOL_EFFECT_JOURNAL_VERSION,\n\t\tcheckpointVersion: TOOL_EFFECT_CHECKPOINT_VERSION,\n\t\tkind: \"checkpoint\",\n\t\tsequence: 0,\n\t\ttimestamp: Date.now(),\n\t\tgeneration: state.checkpointGeneration + 1,\n\t\tpreviousRecordCount: state.recordCount,\n\t\tpreviousSha256: state.validSha256,\n\t\tsnapshotRecordCount: snapshotRecords.length,\n\t\tsnapshotSha256: hashSnapshotPayloads(snapshotEncodings.map(({ payload }) => payload)),\n\t};\n\tconst encoded = Buffer.concat([\n\t\tencodeRecord(checkpoint).encoded,\n\t\t...snapshotEncodings.map(({ encoded }) => encoded),\n\t]);\n\treturn writeCheckpointAtomically(path, encoded, checkpointStateProjection(state.effects.values()), config);\n}\n\nfunction checkpointIfNeeded(\n\tpath: string,\n\tstate: ParsedToolEffectJournal,\n\tconfig: ToolEffectJournalConfig,\n): ParsedToolEffectJournal {\n\tif (state.recordCount < config.checkpointRecords && state.validBytes < config.checkpointBytes) {\n\t\treturn state;\n\t}\n\treturn checkpointJournal(path, state, config);\n}\n\nexport function checkpointToolEffectJournal(path: string): boolean {\n\tconst state = parseJournal(path);\n\tif (state.recordCount === 0) return false;\n\tcheckpointJournal(path, state, getToolEffectJournalConfig());\n\treturn true;\n}\n\nfunction buildEffectIdentity(input: BeginToolEffectInput): {\n\teffectId: string;\n\targumentsHash: string;\n} {\n\tassertNonEmptyString(input.sessionId, \"Tool effect sessionId\");\n\tassertNonEmptyString(input.assistantEntryId, \"Tool effect assistantEntryId\");\n\tassertNonEmptyString(input.toolCallId, \"Tool effect toolCallId\");\n\tassertNonEmptyString(input.toolName, \"Tool effect toolName\");\n\tassertNonNegativeSafeInteger(input.toolCallIndex, \"Tool effect toolCallIndex\");\n\tconst argumentsHash = sha256(canonicalJson(input.arguments));\n\tconst effectId = sha256(\n\t\tcanonicalJson({\n\t\t\tsessionId: input.sessionId,\n\t\t\tassistantEntryId: input.assistantEntryId,\n\t\t\ttoolCallId: input.toolCallId,\n\t\t\ttoolName: input.toolName,\n\t\t\ttoolCallIndex: input.toolCallIndex,\n\t\t\targumentsHash,\n\t\t}),\n\t);\n\treturn { effectId, argumentsHash };\n}\n\nfunction getUncommittedEffects(state: ParsedToolEffectJournal): ParsedDurableToolEffect[] {\n\treturn [...state.effects.values()].filter((effect) => effect.committedSessionEntryId === undefined);\n}\n\nfunction getSnapshotEncodedBytes(effects: Iterable<ParsedDurableToolEffect>): number {\n\treturn buildUnresolvedSnapshotRecords(effects, 1).reduce(\n\t\t(total, record) => total + encodeRecord(record).encoded.byteLength,\n\t\t0,\n\t);\n}\n\nexport function getToolEffectJournalPath(sessionFile: string): string {\n\treturn `${sessionFile}${TOOL_EFFECT_JOURNAL_SUFFIX}`;\n}\n\nfunction getToolEffectResolutionPhase(effect: ParsedDurableToolEffect): ToolEffectResolutionPhase {\n\tif (effect.committedSessionEntryId) return \"committed\";\n\tif (!effect.resultRecordSeen) return \"execution_unknown\";\n\tif (effect.finalizationStarted && !effect.finalResultWritten) return \"finalization_unknown\";\n\treturn \"result_durable\";\n}\n\nfunction getToolEffectResolutionTarget(\n\teffect: ParsedDurableToolEffect,\n\tjournalPath: string,\n): ToolEffectResolutionTarget {\n\tif (!journalPath.endsWith(TOOL_EFFECT_JOURNAL_SUFFIX)) {\n\t\tthrow new Error(`Tool effect journal path must end with ${TOOL_EFFECT_JOURNAL_SUFFIX}`);\n\t}\n\treturn {\n\t\tsessionId: effect.intent.sessionId,\n\t\tsessionPath: journalPath.slice(0, -TOOL_EFFECT_JOURNAL_SUFFIX.length),\n\t\tjournalPath,\n\t\teffectId: effect.intent.effectId,\n\t\ttoolCallId: effect.intent.toolCallId,\n\t\ttoolName: effect.intent.toolName,\n\t\tphase: getToolEffectResolutionPhase(effect),\n\t};\n}\n\nfunction getUnknownToolEffectResolutionTarget(\n\teffect: ParsedDurableToolEffect,\n\tjournalPath: string,\n): UnknownToolEffectResolutionTarget {\n\tconst target = getToolEffectResolutionTarget(effect, journalPath);\n\tif (target.phase !== \"execution_unknown\" && target.phase !== \"finalization_unknown\") {\n\t\tthrow new Error(`Tool effect ${effect.intent.effectId} does not have an unknown outcome`);\n\t}\n\treturn { ...target, phase: target.phase };\n}\n\nexport function beginToolEffect(path: string, input: BeginToolEffectInput): ToolEffectIntentRecord {\n\tconst config = getToolEffectJournalConfig();\n\tlet state = parseJournal(path);\n\tconst { effectId, argumentsHash } = buildEffectIdentity(input);\n\tconst existing = state.effects.get(effectId);\n\tif (existing) {\n\t\tif (existing.result) {\n\t\t\tthrow new ToolEffectRecoveryRequiredError(existing.intent.toolCallId);\n\t\t}\n\t\tthrow new ToolEffectUnknownOutcomeError([getUnknownToolEffectResolutionTarget(existing, path)]);\n\t}\n\tfor (const effect of state.effects.values()) {\n\t\tif (\n\t\t\teffect.intent.assistantEntryId === input.assistantEntryId &&\n\t\t\teffect.intent.toolCallIndex === input.toolCallIndex\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t`Tool effect slot ${input.assistantEntryId}:${input.toolCallIndex} already belongs to ${effect.intent.toolCallId}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tstate = checkpointIfNeeded(path, state, config);\n\tconst timestamp = input.timestamp ?? Date.now();\n\tassertNonNegativeSafeInteger(timestamp, \"Tool effect timestamp\");\n\tconst record: ToolEffectIntentRecord = {\n\t\tversion: TOOL_EFFECT_JOURNAL_VERSION,\n\t\tkind: \"intent\",\n\t\tsequence: state.nextSequence,\n\t\ttimestamp,\n\t\teffectId,\n\t\tsessionId: input.sessionId,\n\t\tassistantEntryId: input.assistantEntryId,\n\t\ttoolCallId: input.toolCallId,\n\t\ttoolName: input.toolName,\n\t\ttoolCallIndex: input.toolCallIndex,\n\t\targumentsHash,\n\t};\n\tconst unresolvedEffects = getUncommittedEffects(state);\n\tif (unresolvedEffects.length + 1 > config.maxUnresolvedEffects) {\n\t\tthrow new ToolEffectJournalCapacityError(\n\t\t\t`Cannot execute tool ${input.toolCallId}: unresolved tool effects would exceed ${config.maxUnresolvedEffects}`,\n\t\t);\n\t}\n\tconst projectedEffect: ParsedDurableToolEffect = {\n\t\tintent: record,\n\t\tresultRecordSeen: false,\n\t\tresultFinalizationPending: false,\n\t\tfinalizationStarted: false,\n\t\tfinalResultWritten: false,\n\t};\n\tconst projectedBytes = getSnapshotEncodedBytes([...unresolvedEffects, projectedEffect]);\n\tif (projectedBytes > config.maxUnresolvedBytes) {\n\t\tthrow new ToolEffectJournalCapacityError(\n\t\t\t`Cannot execute tool ${input.toolCallId}: unresolved tool effect state would exceed ${config.maxUnresolvedBytes} bytes`,\n\t\t);\n\t}\n\tappendRecord(path, state, record);\n\treturn record;\n}\n\nexport function writeToolEffectResult(\n\tpath: string,\n\teffectId: string,\n\tmessage: ToolResultMessage,\n\toptions?: { finalizationPending?: boolean },\n): void {\n\tassertHash(effectId, \"Tool effect id\");\n\tassertToolResultMessage(message, \"Tool effect result\");\n\tconst state = parseJournal(path);\n\tconst effect = state.effects.get(effectId);\n\tif (!effect) {\n\t\tthrow new Error(`Cannot write result for unknown tool effect ${effectId}`);\n\t}\n\tif (effect.committedSessionEntryId) {\n\t\tthrow new Error(`Cannot replace committed tool effect ${effectId}`);\n\t}\n\tif (message.toolCallId !== effect.intent.toolCallId || message.toolName !== effect.intent.toolName) {\n\t\tthrow new Error(`Tool effect result identity does not match intent ${effectId}`);\n\t}\n\tif (effect.resultRecordSeen) {\n\t\tif (\n\t\t\teffect.executionResult &&\n\t\t\tcanonicalPersistedJson(effect.executionResult) === canonicalPersistedJson(message) &&\n\t\t\teffect.resultFinalizationPending === (options?.finalizationPending === true)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tthrow new Error(`Divergent duplicate result for tool effect ${effectId}`);\n\t}\n\tappendRecord(path, state, {\n\t\tversion: TOOL_EFFECT_JOURNAL_VERSION,\n\t\tkind: \"result\",\n\t\tsequence: state.nextSequence,\n\t\ttimestamp: Date.now(),\n\t\teffectId,\n\t\tmessage,\n\t\t...(options?.finalizationPending === true ? { finalizationPending: true as const } : {}),\n\t});\n}\n\nexport function beginToolEffectFinalization(path: string, effectId: string, timestamp = Date.now()): void {\n\tassertHash(effectId, \"Tool effect id\");\n\tassertNonNegativeSafeInteger(timestamp, \"Tool effect finalization timestamp\");\n\tconst state = parseJournal(path);\n\tconst effect = state.effects.get(effectId);\n\tif (!effect) {\n\t\tthrow new Error(`Cannot finalize unknown tool effect ${effectId}`);\n\t}\n\tif (effect.committedSessionEntryId) {\n\t\tthrow new Error(`Cannot finalize committed tool effect ${effectId}`);\n\t}\n\tif (!effect.resultRecordSeen || !effect.result) {\n\t\tthrow new Error(`Cannot finalize tool effect ${effectId} before its durable result`);\n\t}\n\tif (effect.finalizationStarted) {\n\t\tif (effect.finalResultWritten) {\n\t\t\tthrow new ToolEffectRecoveryRequiredError(effect.intent.toolCallId);\n\t\t}\n\t\tthrow new ToolEffectUnknownOutcomeError(\n\t\t\t[getUnknownToolEffectResolutionTarget(effect, path)],\n\t\t\t`Cannot safely continue: final tool result transformation is incomplete (${effect.intent.toolCallId})`,\n\t\t);\n\t}\n\tappendRecord(path, state, {\n\t\tversion: TOOL_EFFECT_JOURNAL_VERSION,\n\t\tkind: \"finalization\",\n\t\tsequence: state.nextSequence,\n\t\ttimestamp,\n\t\teffectId,\n\t});\n}\n\nexport function writeToolEffectFinalResult(\n\tpath: string,\n\teffectId: string,\n\tmessage: ToolResultMessage,\n\ttimestamp = Date.now(),\n): void {\n\tassertHash(effectId, \"Tool effect id\");\n\tassertToolResultMessage(message, \"Tool effect final result\");\n\tassertNonNegativeSafeInteger(timestamp, \"Tool effect final result timestamp\");\n\tconst state = parseJournal(path);\n\tconst effect = state.effects.get(effectId);\n\tif (!effect) {\n\t\tthrow new Error(`Cannot write final result for unknown tool effect ${effectId}`);\n\t}\n\tif (effect.committedSessionEntryId) {\n\t\tthrow new Error(`Cannot replace committed tool effect ${effectId}`);\n\t}\n\tif (!effect.finalizationStarted) {\n\t\tthrow new Error(`Cannot write final result for tool effect ${effectId} before finalization`);\n\t}\n\tif (message.toolCallId !== effect.intent.toolCallId || message.toolName !== effect.intent.toolName) {\n\t\tthrow new Error(`Tool effect final result identity does not match intent ${effectId}`);\n\t}\n\tif (effect.finalResultWritten) {\n\t\tif (effect.result && canonicalPersistedJson(effect.result) === canonicalPersistedJson(message)) {\n\t\t\treturn;\n\t\t}\n\t\tthrow new Error(`Divergent duplicate final result for tool effect ${effectId}`);\n\t}\n\tappendRecord(path, state, {\n\t\tversion: TOOL_EFFECT_JOURNAL_VERSION,\n\t\tkind: \"final\",\n\t\tsequence: state.nextSequence,\n\t\ttimestamp,\n\t\teffectId,\n\t\tmessage,\n\t});\n}\n\nexport function commitToolEffect(path: string, effectId: string, sessionEntryId: string, timestamp = Date.now()): void {\n\tassertHash(effectId, \"Tool effect id\");\n\tassertNonEmptyString(sessionEntryId, \"Tool effect sessionEntryId\");\n\tassertNonNegativeSafeInteger(timestamp, \"Tool effect commit timestamp\");\n\tconst state = parseJournal(path);\n\tconst effect = state.effects.get(effectId);\n\tif (!effect) {\n\t\tthrow new Error(`Cannot commit unknown tool effect ${effectId}`);\n\t}\n\tif (effect.committedSessionEntryId) {\n\t\tif (effect.committedSessionEntryId !== sessionEntryId) {\n\t\t\tthrow new Error(`Tool effect ${effectId} is already committed to ${effect.committedSessionEntryId}`);\n\t\t}\n\t\treturn;\n\t}\n\tif (!effect.result) {\n\t\tthrow new Error(`Cannot commit tool effect ${effectId} before its durable result`);\n\t}\n\tif (effect.finalizationStarted && !effect.finalResultWritten) {\n\t\tthrow new Error(`Cannot commit tool effect ${effectId} before its final result`);\n\t}\n\tappendRecord(path, state, {\n\t\tversion: TOOL_EFFECT_JOURNAL_VERSION,\n\t\tkind: \"commit\",\n\t\tsequence: state.nextSequence,\n\t\ttimestamp,\n\t\teffectId,\n\t\tsessionEntryId,\n\t});\n}\n\nfunction checkpointAfterSessionCommit(path: string): void {\n\tconst config = getToolEffectJournalConfig();\n\tcheckpointIfNeeded(path, parseJournal(path), config);\n}\n\nexport function readDurableToolEffects(path: string): DurableToolEffect[] {\n\treturn [...parseJournal(path).effects.values()].map((effect) => ({\n\t\tintent: { ...effect.intent },\n\t\tresult: effect.result ? structuredClone(effect.result) : undefined,\n\t\tcommittedSessionEntryId: effect.committedSessionEntryId,\n\t}));\n}\n\nfunction getJournalPathForSession(sessionManager: SessionManager): string | undefined {\n\tconst sessionFile = sessionManager.getSessionFile();\n\treturn sessionFile ? getToolEffectJournalPath(sessionFile) : undefined;\n}\n\nfunction findUncommittedEffectForToolCall(\n\tstate: ParsedToolEffectJournal,\n\tsessionId: string,\n\ttoolCallId: string,\n\ttoolName: string,\n): ParsedDurableToolEffect | undefined {\n\tlet matchingEffect: ParsedDurableToolEffect | undefined;\n\tfor (const effect of getUncommittedEffects(state)) {\n\t\tif (\n\t\t\teffect.intent.sessionId !== sessionId ||\n\t\t\teffect.intent.toolCallId !== toolCallId ||\n\t\t\teffect.intent.toolName !== toolName\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (matchingEffect) {\n\t\t\tthrow new Error(`Multiple pending tool effects match tool call ${toolCallId}`);\n\t\t}\n\t\tmatchingEffect = effect;\n\t}\n\treturn matchingEffect;\n}\n\nexport function beginSessionToolEffect(\n\tsessionManager: SessionManager,\n\tevent: SessionToolExecutionStart,\n): ToolEffectIntentRecord | undefined {\n\tif (!sessionManager.isPersisted()) return undefined;\n\tconst path = getJournalPathForSession(sessionManager);\n\tif (!path) {\n\t\tthrow new Error(\"Persistent session has no file for durable tool effect intent\");\n\t}\n\tconst branch = sessionManager.getBranch();\n\tfor (let entryIndex = branch.length - 1; entryIndex >= 0; entryIndex--) {\n\t\tconst entry = branch[entryIndex];\n\t\tif (entry.type !== \"message\" || entry.message.role !== \"assistant\") continue;\n\t\tconst toolCalls = entry.message.content.filter((content) => content.type === \"toolCall\");\n\t\tconst toolCallIndex = toolCalls.findIndex(\n\t\t\t(toolCall) => toolCall.id === event.toolCallId && toolCall.name === event.toolName,\n\t\t);\n\t\tif (toolCallIndex === -1) continue;\n\t\tconst toolCall = toolCalls[toolCallIndex];\n\t\tif (canonicalJson(toolCall.arguments) !== canonicalJson(event.args)) {\n\t\t\tthrow new Error(`Tool execution start arguments do not match assistant tool call ${event.toolCallId}`);\n\t\t}\n\t\tsessionManager.flushSessionFile();\n\t\treturn beginToolEffect(path, {\n\t\t\tsessionId: sessionManager.getSessionId(),\n\t\t\tassistantEntryId: entry.id,\n\t\t\ttoolCallId: event.toolCallId,\n\t\t\ttoolName: event.toolName,\n\t\t\ttoolCallIndex,\n\t\t\targuments: event.args,\n\t\t});\n\t}\n\tthrow new Error(`Cannot find assistant entry for tool execution ${event.toolCallId}`);\n}\n\nexport function writeSessionToolEffectResult(\n\tsessionManager: SessionManager,\n\tmessage: ToolResultMessage,\n\toptions?: { finalizationPending?: boolean },\n): void {\n\tif (!sessionManager.isPersisted()) return;\n\tconst path = getJournalPathForSession(sessionManager);\n\tif (!path || !existsSync(path)) {\n\t\tthrow new Error(`Missing durable tool effect intent for result ${message.toolCallId}`);\n\t}\n\tconst effect = findUncommittedEffectForToolCall(\n\t\tparseJournal(path),\n\t\tsessionManager.getSessionId(),\n\t\tmessage.toolCallId,\n\t\tmessage.toolName,\n\t);\n\tif (!effect) {\n\t\tthrow new Error(`Missing pending tool effect intent for result ${message.toolCallId}`);\n\t}\n\twriteToolEffectResult(path, effect.intent.effectId, message, options);\n}\n\nexport function beginSessionToolEffectFinalization(sessionManager: SessionManager, message: ToolResultMessage): void {\n\tif (!sessionManager.isPersisted()) return;\n\tconst path = getJournalPathForSession(sessionManager);\n\tif (!path || !existsSync(path)) {\n\t\tthrow new Error(`Missing durable tool effect result for finalization ${message.toolCallId}`);\n\t}\n\tconst effect = findUncommittedEffectForToolCall(\n\t\tparseJournal(path),\n\t\tsessionManager.getSessionId(),\n\t\tmessage.toolCallId,\n\t\tmessage.toolName,\n\t);\n\tif (!effect) {\n\t\tthrow new Error(`Missing pending tool effect result for finalization ${message.toolCallId}`);\n\t}\n\tbeginToolEffectFinalization(path, effect.intent.effectId);\n}\n\nexport function writeSessionToolEffectFinalResult(sessionManager: SessionManager, message: ToolResultMessage): void {\n\tif (!sessionManager.isPersisted()) return;\n\tconst path = getJournalPathForSession(sessionManager);\n\tif (!path || !existsSync(path)) {\n\t\tthrow new Error(`Missing durable tool effect finalization for result ${message.toolCallId}`);\n\t}\n\tconst effect = findUncommittedEffectForToolCall(\n\t\tparseJournal(path),\n\t\tsessionManager.getSessionId(),\n\t\tmessage.toolCallId,\n\t\tmessage.toolName,\n\t);\n\tif (!effect) {\n\t\tthrow new Error(`Missing pending tool effect finalization for result ${message.toolCallId}`);\n\t}\n\twriteToolEffectFinalResult(path, effect.intent.effectId, message);\n}\n\nexport function commitSessionToolEffect(\n\tsessionManager: SessionManager,\n\tmessage: ToolResultMessage,\n\tsessionEntryId: string,\n): void {\n\tif (!sessionManager.isPersisted()) return;\n\tconst path = getJournalPathForSession(sessionManager);\n\tif (!path || !existsSync(path)) {\n\t\tthrow new Error(`Missing durable tool effect result for session entry ${sessionEntryId}`);\n\t}\n\tconst state = parseJournal(path);\n\tconst effect = findUncommittedEffectForToolCall(\n\t\tstate,\n\t\tsessionManager.getSessionId(),\n\t\tmessage.toolCallId,\n\t\tmessage.toolName,\n\t);\n\tif (effect) {\n\t\tif (!effect.result || canonicalPersistedJson(effect.result) !== canonicalPersistedJson(message)) {\n\t\t\tthrow new Error(`Session tool result does not match durable effect ${effect.intent.effectId}`);\n\t\t}\n\t\tcommitToolEffect(path, effect.intent.effectId, sessionEntryId);\n\t\tcheckpointAfterSessionCommit(path);\n\t\treturn;\n\t}\n\tconst committed = [...state.effects.values()].find(\n\t\t(candidate) =>\n\t\t\tcandidate.intent.sessionId === sessionManager.getSessionId() &&\n\t\t\tcandidate.intent.toolCallId === message.toolCallId &&\n\t\t\tcandidate.intent.toolName === message.toolName &&\n\t\t\tcandidate.committedSessionEntryId === sessionEntryId,\n\t);\n\tif (!committed) {\n\t\tthrow new Error(`Missing pending tool effect result for session entry ${sessionEntryId}`);\n\t}\n\tif (!committed.result || canonicalPersistedJson(committed.result) !== canonicalPersistedJson(message)) {\n\t\tthrow new Error(`Session tool result does not match committed durable effect ${committed.intent.effectId}`);\n\t}\n\tcheckpointIfNeeded(path, state, getToolEffectJournalConfig());\n}\n\nfunction getAssistantToolCalls(entry: SessionMessageEntry): AssistantMessage[\"content\"] {\n\tif (entry.message.role !== \"assistant\") {\n\t\tthrow new Error(`Tool effect assistant entry ${entry.id} is not an assistant message`);\n\t}\n\treturn entry.message.content;\n}\n\nfunction validateEffectAgainstAssistant(effect: DurableToolEffect, entry: SessionMessageEntry): void {\n\tconst toolCalls = getAssistantToolCalls(entry).filter((content) => content.type === \"toolCall\");\n\tconst toolCall = toolCalls[effect.intent.toolCallIndex];\n\tif (\n\t\t!toolCall ||\n\t\ttoolCall.id !== effect.intent.toolCallId ||\n\t\ttoolCall.name !== effect.intent.toolName ||\n\t\tsha256(canonicalJson(toolCall.arguments)) !== effect.intent.argumentsHash\n\t) {\n\t\tthrow new Error(\n\t\t\t`Tool effect ${effect.intent.effectId} does not match assistant entry ${effect.intent.assistantEntryId}`,\n\t\t);\n\t}\n}\n\nfunction findPersistedToolResult(\n\tbranch: ReturnType<SessionManager[\"getBranch\"]>,\n\tassistantIndex: number,\n\teffect: DurableToolEffect,\n): SessionMessageEntry | undefined {\n\tfor (let index = assistantIndex + 1; index < branch.length; index++) {\n\t\tconst entry = branch[index];\n\t\tif (\n\t\t\tentry.type === \"message\" &&\n\t\t\tentry.message.role === \"toolResult\" &&\n\t\t\tentry.message.toolCallId === effect.intent.toolCallId\n\t\t) {\n\t\t\tif (entry.message.toolName !== effect.intent.toolName) {\n\t\t\t\tthrow new Error(`Persisted tool result name does not match effect ${effect.intent.effectId}`);\n\t\t\t}\n\t\t\treturn entry;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction assertSafeRecoveryTail(\n\tbranch: ReturnType<SessionManager[\"getBranch\"]>,\n\teffects: RecoverableToolEffect[],\n): void {\n\tif (effects.length === 0) return;\n\tconst firstAssistantIndex = Math.min(...effects.map((candidate) => candidate.assistantIndex));\n\tconst firstAssistantEntry = branch[firstAssistantIndex];\n\tif (firstAssistantEntry.type !== \"message\" || firstAssistantEntry.message.role !== \"assistant\") {\n\t\tthrow new Error(`Tool effect recovery entry ${firstAssistantEntry.id} is not an assistant message`);\n\t}\n\tconst allowedToolCallIds = new Set(\n\t\tfirstAssistantEntry.message.content\n\t\t\t.filter((content) => content.type === \"toolCall\")\n\t\t\t.map((toolCall) => toolCall.id),\n\t);\n\tfor (let index = firstAssistantIndex + 1; index < branch.length; index++) {\n\t\tconst entry = branch[index];\n\t\tif (entry.type === \"message\") {\n\t\t\tif (entry.message.role === \"toolResult\" && allowedToolCallIds.has(entry.message.toolCallId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (entry.message.role === \"bashExecution\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow new Error(`Cannot recover tool effects after later model-visible history at session entry ${entry.id}`);\n\t\t}\n\t\tif (entry.type === \"custom_message\" || entry.type === \"compaction\" || entry.type === \"branch_summary\") {\n\t\t\tthrow new Error(`Cannot recover tool effects after later model-visible history at session entry ${entry.id}`);\n\t\t}\n\t}\n}\n\nfunction loadToolEffectResolutionTarget(\n\tsessionManager: SessionManager,\n\teffectId: string,\n): {\n\tpath: string;\n\tstate: ParsedToolEffectJournal;\n\teffect: ParsedDurableToolEffect;\n\ttarget: ToolEffectResolutionTarget;\n} {\n\tassertHash(effectId, \"Tool effect id\");\n\tif (!sessionManager.isPersisted()) {\n\t\tthrow new Error(\"Tool effect resolution requires a persisted session\");\n\t}\n\tconst sessionFile = sessionManager.getSessionFile();\n\tif (!sessionFile) {\n\t\tthrow new Error(\"Persistent session has no session file\");\n\t}\n\tconst path = getToolEffectJournalPath(sessionFile);\n\tif (!existsSync(path)) {\n\t\tthrow new Error(`Tool effect journal does not exist: ${path}`);\n\t}\n\tconst state = parseJournal(path);\n\tconst effect = state.effects.get(effectId);\n\tif (!effect) {\n\t\tthrow new Error(`Tool effect ${effectId} does not exist in ${path}`);\n\t}\n\tif (effect.intent.sessionId !== sessionManager.getSessionId()) {\n\t\tthrow new Error(`Tool effect ${effectId} does not belong to session ${sessionManager.getSessionId()}`);\n\t}\n\tconst assistantEntry = sessionManager.getEntry(effect.intent.assistantEntryId);\n\tif (!assistantEntry || assistantEntry.type !== \"message\") {\n\t\tthrow new Error(`Tool effect ${effectId} references a missing assistant entry`);\n\t}\n\tvalidateEffectAgainstAssistant(effect, assistantEntry);\n\treturn {\n\t\tpath,\n\t\tstate,\n\t\teffect,\n\t\ttarget: getToolEffectResolutionTarget(effect, path),\n\t};\n}\n\nexport function inspectToolEffect(sessionManager: SessionManager, effectId: string): ToolEffectResolutionTarget {\n\treturn loadToolEffectResolutionTarget(sessionManager, effectId).target;\n}\n\nfunction resolveToolEffectWithResult(\n\tsessionManager: SessionManager,\n\teffectId: string,\n\tmessage: ToolResultMessage,\n): ToolEffectResolutionResult {\n\tassertToolResultMessage(message, \"Accepted tool result\");\n\tconst serializedMessage = JSON.stringify(message);\n\tif (serializedMessage === undefined) {\n\t\tthrow new Error(\"Accepted tool result is not JSON serializable\");\n\t}\n\tconst durableMessage: unknown = JSON.parse(serializedMessage);\n\tassertToolResultMessage(durableMessage, \"Accepted tool result\");\n\n\tconst { path, state, effect, target } = loadToolEffectResolutionTarget(sessionManager, effectId);\n\tif (durableMessage.toolCallId !== effect.intent.toolCallId || durableMessage.toolName !== effect.intent.toolName) {\n\t\tthrow new Error(`Accepted tool result identity does not match tool effect ${effectId}`);\n\t}\n\n\tif (effect.committedSessionEntryId) {\n\t\tconst committedEntry = sessionManager.getEntry(effect.committedSessionEntryId);\n\t\tif (\n\t\t\t!committedEntry ||\n\t\t\tcommittedEntry.type !== \"message\" ||\n\t\t\tcommittedEntry.message.role !== \"toolResult\" ||\n\t\t\tcanonicalPersistedJson(committedEntry.message) !== canonicalPersistedJson(durableMessage) ||\n\t\t\t!effect.result ||\n\t\t\tcanonicalPersistedJson(effect.result) !== canonicalPersistedJson(durableMessage)\n\t\t) {\n\t\t\tthrow new Error(`Committed tool effect ${effectId} does not match the accepted result`);\n\t\t}\n\t\tcheckpointIfNeeded(path, state, getToolEffectJournalConfig());\n\t\treturn {\n\t\t\ttarget,\n\t\t\tsessionEntryId: effect.committedSessionEntryId,\n\t\t\talreadyResolved: true,\n\t\t};\n\t}\n\n\tconst branch = sessionManager.getBranch();\n\tconst assistantIndex = branch.findIndex((entry) => entry.id === effect.intent.assistantEntryId);\n\tif (assistantIndex === -1) {\n\t\tthrow new Error(`Tool effect ${effectId} is not on the active session branch`);\n\t}\n\tconst persisted = findPersistedToolResult(branch, assistantIndex, effect);\n\tif (persisted && canonicalPersistedJson(persisted.message) !== canonicalPersistedJson(durableMessage)) {\n\t\tthrow new Error(`Persisted tool result ${persisted.id} does not match the accepted result`);\n\t}\n\n\tif (target.phase === \"result_durable\") {\n\t\tif (!effect.result || canonicalPersistedJson(effect.result) !== canonicalPersistedJson(durableMessage)) {\n\t\t\tthrow new Error(`Durable tool effect ${effectId} does not match the accepted result`);\n\t\t}\n\t} else if (target.phase !== \"execution_unknown\" && target.phase !== \"finalization_unknown\") {\n\t\tthrow new Error(`Tool effect ${effectId} cannot be resolved from phase ${target.phase}`);\n\t}\n\n\tlet sessionEntryId = persisted?.id;\n\tif (!sessionEntryId) {\n\t\tfor (const candidate of getUncommittedEffects(state)) {\n\t\t\tif (\n\t\t\t\tcandidate.intent.assistantEntryId !== effect.intent.assistantEntryId ||\n\t\t\t\tcandidate.intent.toolCallIndex >= effect.intent.toolCallIndex\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!findPersistedToolResult(branch, assistantIndex, candidate)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Resolve earlier tool effect ${candidate.intent.effectId} before ${effect.intent.effectId}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tassertSafeRecoveryTail(branch, [{ effect, assistantIndex }]);\n\t\tsessionEntryId = sessionManager.appendMessage(durableMessage);\n\t\tsessionManager.flushSessionFile();\n\t} else {\n\t\tsessionManager.flushSessionFile();\n\t}\n\n\tif (!effect.resultRecordSeen) {\n\t\twriteToolEffectResult(path, effectId, durableMessage, { finalizationPending: true });\n\t}\n\tconst refreshed = parseJournal(path).effects.get(effectId);\n\tif (!refreshed) {\n\t\tthrow new Error(`Tool effect ${effectId} disappeared during resolution`);\n\t}\n\tif (refreshed.finalizationStarted && !refreshed.finalResultWritten) {\n\t\twriteToolEffectFinalResult(path, effectId, durableMessage);\n\t}\n\tcommitToolEffect(path, effectId, sessionEntryId);\n\tcheckpointAfterSessionCommit(path);\n\treturn { target, sessionEntryId, alreadyResolved: persisted !== undefined };\n}\n\nexport function markToolEffectFailed(sessionManager: SessionManager, effectId: string): ToolEffectResolutionResult {\n\tconst { effect } = loadToolEffectResolutionTarget(sessionManager, effectId);\n\treturn resolveToolEffectWithResult(sessionManager, effectId, {\n\t\trole: \"toolResult\",\n\t\ttoolCallId: effect.intent.toolCallId,\n\t\ttoolName: effect.intent.toolName,\n\t\tcontent: [\n\t\t\t{\n\t\t\t\ttype: \"text\",\n\t\t\t\ttext: \"Tool execution outcome was explicitly marked failed during recovery. The interrupted tool was not re-executed; external side effects may already have occurred.\",\n\t\t\t},\n\t\t],\n\t\tisError: true,\n\t\ttimestamp: effect.intent.timestamp,\n\t});\n}\n\nexport function acceptToolEffectResult(\n\tsessionManager: SessionManager,\n\teffectId: string,\n\tmessage: unknown,\n): ToolEffectResolutionResult {\n\tassertToolResultMessage(message, \"Accepted tool result\");\n\treturn resolveToolEffectWithResult(sessionManager, effectId, message);\n}\n\nexport function recoverToolEffects(sessionManager: SessionManager, journalPath?: string): ToolEffectRecoveryResult {\n\tconst sessionFile = sessionManager.getSessionFile();\n\tconst path = journalPath ?? (sessionFile ? getToolEffectJournalPath(sessionFile) : undefined);\n\tif (!path || !existsSync(path)) {\n\t\treturn { recoveredToolCallIds: [], acknowledgedToolCallIds: [] };\n\t}\n\tconst state = parseJournal(path);\n\tconst branch = sessionManager.getBranch();\n\tconst branchIndexes = new Map(branch.map((entry, index) => [entry.id, index]));\n\tconst activeEffects: RecoverableToolEffect[] = [];\n\n\tfor (const effect of state.effects.values()) {\n\t\tif (!effect.committedSessionEntryId) continue;\n\t\tconst result = effect.result;\n\t\tif (!result) {\n\t\t\tthrow new Error(`Committed tool effect ${effect.intent.effectId} has no durable result`);\n\t\t}\n\t\tconst sessionEntry = sessionManager.getEntry(effect.committedSessionEntryId);\n\t\tif (\n\t\t\t!sessionEntry ||\n\t\t\tsessionEntry.type !== \"message\" ||\n\t\t\tsessionEntry.message.role !== \"toolResult\" ||\n\t\t\tcanonicalPersistedJson(sessionEntry.message) !== canonicalPersistedJson(result)\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t`Committed tool effect ${effect.intent.effectId} does not match session entry ${effect.committedSessionEntryId}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tfor (const effect of getUncommittedEffects(state)) {\n\t\tif (effect.intent.sessionId !== sessionManager.getSessionId()) {\n\t\t\tthrow new Error(`Tool effect journal session id does not match ${sessionManager.getSessionId()}`);\n\t\t}\n\t\tconst assistantIndex = branchIndexes.get(effect.intent.assistantEntryId);\n\t\tif (assistantIndex === undefined) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst assistantEntry = branch[assistantIndex];\n\t\tif (assistantEntry.type !== \"message\") {\n\t\t\tthrow new Error(`Tool effect assistant entry ${assistantEntry.id} is not a message`);\n\t\t}\n\t\tvalidateEffectAgainstAssistant(effect, assistantEntry);\n\t\tconst persisted = findPersistedToolResult(branch, assistantIndex, effect);\n\t\tif (persisted) {\n\t\t\tconst result = effect.result;\n\t\t\tif (\n\t\t\t\tresult &&\n\t\t\t\t(!effect.finalizationStarted || effect.finalResultWritten) &&\n\t\t\t\tcanonicalPersistedJson(persisted.message) !== canonicalPersistedJson(result)\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Persisted tool result ${persisted.id} does not match durable effect ${effect.intent.effectId}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tactiveEffects.push({ effect, assistantIndex, persisted });\n\t}\n\n\tconst unknownEffects = activeEffects\n\t\t.filter(\n\t\t\t(candidate) =>\n\t\t\t\tcandidate.effect.result === undefined ||\n\t\t\t\t(candidate.effect.finalizationStarted && !candidate.effect.finalResultWritten),\n\t\t)\n\t\t.map((candidate) => getUnknownToolEffectResolutionTarget(candidate.effect, path));\n\tif (unknownEffects.length > 0) {\n\t\tthrow new ToolEffectUnknownOutcomeError(\n\t\t\tunknownEffects,\n\t\t\t\"Cannot safely continue: tool effects may have occurred but their final outcomes are unknown.\",\n\t\t);\n\t}\n\n\tconst acknowledgedToolCallIds: string[] = [];\n\tfor (const candidate of activeEffects) {\n\t\tif (!candidate.persisted) continue;\n\t\tsessionManager.flushSessionFile();\n\t\tcommitToolEffect(path, candidate.effect.intent.effectId, candidate.persisted.id);\n\t\tacknowledgedToolCallIds.push(candidate.effect.intent.toolCallId);\n\t}\n\n\tconst recoverable = activeEffects.filter((candidate) => candidate.persisted === undefined);\n\tassertSafeRecoveryTail(branch, recoverable);\n\trecoverable.sort(\n\t\t(left, right) =>\n\t\t\tleft.assistantIndex - right.assistantIndex ||\n\t\t\tleft.effect.intent.toolCallIndex - right.effect.intent.toolCallIndex,\n\t);\n\tconst recoveredToolCallIds: string[] = [];\n\tfor (const candidate of recoverable) {\n\t\tconst result = candidate.effect.result;\n\t\tif (!result) {\n\t\t\tthrow new Error(`Tool effect ${candidate.effect.intent.effectId} lost its durable result during recovery`);\n\t\t}\n\t\tconst sessionEntryId = sessionManager.appendMessage(structuredClone(result));\n\t\tsessionManager.flushSessionFile();\n\t\tcommitToolEffect(path, candidate.effect.intent.effectId, sessionEntryId);\n\t\trecoveredToolCallIds.push(candidate.effect.intent.toolCallId);\n\t}\n\tcheckpointAfterSessionCommit(path);\n\treturn { recoveredToolCallIds, acknowledgedToolCallIds };\n}\n"]}
|