@averyyy/pi-coding-agent 0.82.1-piclient.4 → 0.82.1-piclient.6

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/dist/core/agent-session.d.ts +0 -1
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +4 -27
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/model-runtime.d.ts +0 -7
  7. package/dist/core/model-runtime.d.ts.map +1 -1
  8. package/dist/core/model-runtime.js +0 -66
  9. package/dist/core/model-runtime.js.map +1 -1
  10. package/dist/core/pi-server-client.d.ts +1 -3
  11. package/dist/core/pi-server-client.d.ts.map +1 -1
  12. package/dist/core/pi-server-client.js +301 -55
  13. package/dist/core/pi-server-client.js.map +1 -1
  14. package/dist/core/pi-server-compact-state.d.ts +1 -1
  15. package/dist/core/pi-server-compact-state.d.ts.map +1 -1
  16. package/dist/core/pi-server-compact-state.js +2 -1
  17. package/dist/core/pi-server-compact-state.js.map +1 -1
  18. package/dist/core/pi-server-run-state.d.ts +2 -1
  19. package/dist/core/pi-server-run-state.d.ts.map +1 -1
  20. package/dist/core/pi-server-run-state.js +5 -3
  21. package/dist/core/pi-server-run-state.js.map +1 -1
  22. package/dist/core/sdk.d.ts.map +1 -1
  23. package/dist/core/sdk.js +0 -5
  24. package/dist/core/sdk.js.map +1 -1
  25. package/package.json +4 -4
  26. package/dist/core/pi-server-provider-execution.d.ts +0 -19
  27. package/dist/core/pi-server-provider-execution.d.ts.map +0 -1
  28. package/dist/core/pi-server-provider-execution.js +0 -43
  29. package/dist/core/pi-server-provider-execution.js.map +0 -1
  30. package/dist/core/provider-execution-contract.d.ts +0 -36
  31. package/dist/core/provider-execution-contract.d.ts.map +0 -1
  32. package/dist/core/provider-execution-contract.js +0 -30
  33. package/dist/core/provider-execution-contract.js.map +0 -1
@@ -39,7 +39,7 @@ export interface PiServerTerminalCompactState extends PiServerCompactStateBase {
39
39
  resolution: "terminal_failed_observed" | "server_rejected";
40
40
  }
41
41
  export type PiServerCompactObservationState = PiServerAppliedCompactState | PiServerTerminalCompactState;
42
- export type PiServerCompactAcknowledgementResolution = "server_ack" | "server_missing_after_tree_applied" | "server_missing_after_terminal_failure" | "server_rejected_not_started";
42
+ export type PiServerCompactAcknowledgementResolution = "server_ack" | "server_missing_after_tree_applied" | "server_missing_after_terminal_failure" | "server_rejected_not_started" | "server_authoritative_missing";
43
43
  export interface WritePiServerPendingCompactInput {
44
44
  serverHash: string;
45
45
  sessionId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"pi-server-compact-state.d.ts","sourceRoot":"","sources":["../../src/core/pi-server-compact-state.ts"],"names":[],"mappings":"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","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { createHash } from \"node:crypto\";\nimport { closeSync, existsSync, fsyncSync, openSync, readFileSync, truncateSync, writeSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { DatabaseSync } from \"node:sqlite\";\n\nconst COMPACT_STATE_VERSION = 1;\nconst COMPACT_STATE_MAX_BYTES = 8 * 1024 * 1024;\nconst SHA256_PATTERN = /^[a-f0-9]{64}$/u;\nconst IDENTIFIER_MAX_CHARS = 4096;\nconst ERROR_MAX_CHARS = 1024 * 1024;\n\ninterface PiServerCompactStateBase {\n\tversion: typeof COMPACT_STATE_VERSION;\n\tsequence: number;\n\ttimestamp: number;\n}\n\nexport interface PiServerPendingCompactState extends PiServerCompactStateBase {\n\tkind: \"compact\";\n\tserverHash: string;\n\tsessionId: string;\n\toperationId: string;\n\trequestHash: string;\n\tbaseStaticContextHash: string;\n\tbaseTreeHash: string;\n\tbaseEntryCount: number;\n\tbaseLeafId: string | null;\n\tbaseRevision: number;\n\tobservation?: PiServerCompactObservationState;\n}\n\nexport interface PiServerAppliedCompactState extends PiServerCompactStateBase {\n\tkind: \"applied\";\n\toperationId: string;\n\trequestHash: string;\n\tentryId: string;\n\tentryHash: string;\n\tupdatedTreeHash: string;\n\tupdatedLeafId: string;\n\tupdatedRevision: number;\n\tresolution: \"tree_applied\";\n}\n\nexport interface PiServerTerminalCompactState extends PiServerCompactStateBase {\n\tkind: \"terminal\";\n\toperationId: string;\n\trequestHash: string;\n\thttpStatus: number;\n\terror: string;\n\toperationDisposition: \"terminal\" | \"not_started\";\n\tstatus: \"failed\" | \"rejected\";\n\tresolution: \"terminal_failed_observed\" | \"server_rejected\";\n}\n\nexport type PiServerCompactObservationState = PiServerAppliedCompactState | PiServerTerminalCompactState;\n\nexport type PiServerCompactAcknowledgementResolution =\n\t| \"server_ack\"\n\t| \"server_missing_after_tree_applied\"\n\t| \"server_missing_after_terminal_failure\"\n\t| \"server_rejected_not_started\";\n\ninterface PiServerAcknowledgedCompactState extends PiServerCompactStateBase {\n\tkind: \"ack\";\n\toperationId: string;\n\tresolution: PiServerCompactAcknowledgementResolution;\n}\n\ntype PiServerCompactStateRecord =\n\t| PiServerPendingCompactState\n\t| PiServerCompactObservationState\n\t| PiServerAcknowledgedCompactState;\n\ninterface ParsedPiServerCompactState {\n\tnextSequence: number;\n\tpending: PiServerPendingCompactState | undefined;\n}\n\nexport interface WritePiServerPendingCompactInput {\n\tserverHash: string;\n\tsessionId: string;\n\toperationId: string;\n\trequestHash: string;\n\tbaseStaticContextHash: string;\n\tbaseTreeHash: string;\n\tbaseEntryCount: number;\n\tbaseLeafId: string | null;\n\tbaseRevision: number;\n\ttimestamp?: number;\n}\n\nexport interface WritePiServerAppliedCompactInput {\n\toperationId: string;\n\trequestHash: string;\n\tentryId: string;\n\tentryHash: string;\n\tupdatedTreeHash: string;\n\tupdatedLeafId: string;\n\tupdatedRevision: number;\n\ttimestamp?: number;\n}\n\nexport interface WritePiServerTerminalCompactInput {\n\toperationId: string;\n\trequestHash: string;\n\thttpStatus: number;\n\terror: string;\n\toperationDisposition: \"terminal\" | \"not_started\";\n\tstatus: \"failed\" | \"rejected\";\n\ttimestamp?: number;\n}\n\nfunction sha256(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\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 assertIdentifier(value: unknown, path: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.length === 0 || value.length > IDENTIFIER_MAX_CHARS || value.includes(\"\\0\")) {\n\t\tthrow new Error(`${path} must be a non-empty string without NUL bytes`);\n\t}\n}\n\nfunction assertHash(value: unknown, path: string, allowEmpty = false): asserts value is string {\n\tif (\n\t\ttypeof value !== \"string\" ||\n\t\t(!allowEmpty && !SHA256_PATTERN.test(value)) ||\n\t\t(allowEmpty && value !== \"\" && !SHA256_PATTERN.test(value))\n\t) {\n\t\tthrow new Error(`${path} must be ${allowEmpty ? \"empty or \" : \"\"}a lowercase SHA-256 digest`);\n\t}\n}\n\nfunction assertError(value: unknown, path: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.length === 0 || value.length > ERROR_MAX_CHARS || value.includes(\"\\0\")) {\n\t\tthrow new Error(`${path} must be a non-empty string without NUL bytes`);\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 parsePayload(payload: string, lineNumber: number): PiServerCompactStateRecord {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(payload) as unknown;\n\t} catch (error) {\n\t\tthrow new Error(`pi-server compact state payload is invalid JSON at line ${lineNumber}`, { cause: error });\n\t}\n\tassertObject(value, `pi-server compact state line ${lineNumber}`);\n\tif (value.version !== COMPACT_STATE_VERSION) {\n\t\tthrow new Error(`Unsupported pi-server compact state version at line ${lineNumber}`);\n\t}\n\tassertNonNegativeSafeInteger(value.sequence, `pi-server compact state line ${lineNumber}.sequence`);\n\tassertNonNegativeSafeInteger(value.timestamp, `pi-server compact state line ${lineNumber}.timestamp`);\n\tif (value.kind === \"ack\") {\n\t\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\t\tif (\n\t\t\tvalue.resolution !== \"server_ack\" &&\n\t\t\tvalue.resolution !== \"server_missing_after_tree_applied\" &&\n\t\t\tvalue.resolution !== \"server_missing_after_terminal_failure\" &&\n\t\t\tvalue.resolution !== \"server_rejected_not_started\"\n\t\t) {\n\t\t\tthrow new Error(`pi-server compact state acknowledgement resolution is invalid at line ${lineNumber}`);\n\t\t}\n\t\treturn {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"ack\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\toperationId: value.operationId,\n\t\t\tresolution: value.resolution,\n\t\t};\n\t}\n\tif (value.kind === \"applied\") {\n\t\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\t\tassertHash(value.requestHash, `pi-server compact state line ${lineNumber}.requestHash`);\n\t\tassertIdentifier(value.entryId, `pi-server compact state line ${lineNumber}.entryId`);\n\t\tassertHash(value.entryHash, `pi-server compact state line ${lineNumber}.entryHash`);\n\t\tassertHash(value.updatedTreeHash, `pi-server compact state line ${lineNumber}.updatedTreeHash`);\n\t\tassertIdentifier(value.updatedLeafId, `pi-server compact state line ${lineNumber}.updatedLeafId`);\n\t\tassertNonNegativeSafeInteger(value.updatedRevision, `pi-server compact state line ${lineNumber}.updatedRevision`);\n\t\tif (value.resolution !== \"tree_applied\") {\n\t\t\tthrow new Error(`pi-server compact applied resolution is invalid at line ${lineNumber}`);\n\t\t}\n\t\treturn {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"applied\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\toperationId: value.operationId,\n\t\t\trequestHash: value.requestHash,\n\t\t\tentryId: value.entryId,\n\t\t\tentryHash: value.entryHash,\n\t\t\tupdatedTreeHash: value.updatedTreeHash,\n\t\t\tupdatedLeafId: value.updatedLeafId,\n\t\t\tupdatedRevision: value.updatedRevision,\n\t\t\tresolution: value.resolution,\n\t\t};\n\t}\n\tif (value.kind === \"terminal\") {\n\t\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\t\tassertHash(value.requestHash, `pi-server compact state line ${lineNumber}.requestHash`);\n\t\tif (\n\t\t\t!Number.isSafeInteger(value.httpStatus) ||\n\t\t\t(value.httpStatus as number) < 100 ||\n\t\t\t(value.httpStatus as number) > 599\n\t\t) {\n\t\t\tthrow new Error(`pi-server compact terminal HTTP status is invalid at line ${lineNumber}`);\n\t\t}\n\t\tassertError(value.error, `pi-server compact state line ${lineNumber}.error`);\n\t\tif (\n\t\t\t(value.operationDisposition !== \"terminal\" && value.operationDisposition !== \"not_started\") ||\n\t\t\t(value.status !== \"failed\" && value.status !== \"rejected\") ||\n\t\t\t(value.operationDisposition === \"terminal\" && value.status !== \"failed\") ||\n\t\t\t(value.operationDisposition === \"not_started\" && value.status !== \"rejected\")\n\t\t) {\n\t\t\tthrow new Error(`pi-server compact terminal disposition is invalid at line ${lineNumber}`);\n\t\t}\n\t\tconst expectedResolution =\n\t\t\tvalue.operationDisposition === \"terminal\" ? \"terminal_failed_observed\" : \"server_rejected\";\n\t\tif (value.resolution !== expectedResolution) {\n\t\t\tthrow new Error(`pi-server compact terminal resolution is invalid at line ${lineNumber}`);\n\t\t}\n\t\treturn {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"terminal\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\toperationId: value.operationId,\n\t\t\trequestHash: value.requestHash,\n\t\t\thttpStatus: value.httpStatus as number,\n\t\t\terror: value.error,\n\t\t\toperationDisposition: value.operationDisposition,\n\t\t\tstatus: value.status,\n\t\t\tresolution: expectedResolution,\n\t\t};\n\t}\n\tif (value.kind !== \"compact\") {\n\t\tthrow new Error(`Unsupported pi-server compact state record kind at line ${lineNumber}`);\n\t}\n\tassertHash(value.serverHash, `pi-server compact state line ${lineNumber}.serverHash`);\n\tassertIdentifier(value.sessionId, `pi-server compact state line ${lineNumber}.sessionId`);\n\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\tassertHash(value.requestHash, `pi-server compact state line ${lineNumber}.requestHash`);\n\tassertHash(value.baseStaticContextHash, `pi-server compact state line ${lineNumber}.baseStaticContextHash`, true);\n\tassertHash(value.baseTreeHash, `pi-server compact state line ${lineNumber}.baseTreeHash`);\n\tassertNonNegativeSafeInteger(value.baseEntryCount, `pi-server compact state line ${lineNumber}.baseEntryCount`);\n\tif (value.baseLeafId !== null) {\n\t\tassertIdentifier(value.baseLeafId, `pi-server compact state line ${lineNumber}.baseLeafId`);\n\t}\n\tassertNonNegativeSafeInteger(value.baseRevision, `pi-server compact state line ${lineNumber}.baseRevision`);\n\treturn {\n\t\tversion: COMPACT_STATE_VERSION,\n\t\tkind: \"compact\",\n\t\tsequence: value.sequence,\n\t\ttimestamp: value.timestamp,\n\t\tserverHash: value.serverHash,\n\t\tsessionId: value.sessionId,\n\t\toperationId: value.operationId,\n\t\trequestHash: value.requestHash,\n\t\tbaseStaticContextHash: value.baseStaticContextHash,\n\t\tbaseTreeHash: value.baseTreeHash,\n\t\tbaseEntryCount: value.baseEntryCount,\n\t\tbaseLeafId: value.baseLeafId,\n\t\tbaseRevision: value.baseRevision,\n\t};\n}\n\nfunction parseCompactStateFile(path: string): ParsedPiServerCompactState {\n\tif (!existsSync(path)) return { nextSequence: 0, pending: undefined };\n\tconst encoded = readFileSync(path);\n\tif (encoded.byteLength > COMPACT_STATE_MAX_BYTES) {\n\t\tthrow new Error(`pi-server compact state exceeds ${COMPACT_STATE_MAX_BYTES} bytes: ${path}`);\n\t}\n\tconst content = encoded.toString(\"utf-8\");\n\tconst lines = content.split(\"\\n\");\n\tif (!content.endsWith(\"\\n\")) lines.pop();\n\tlet expectedSequence = 0;\n\tlet pending: PiServerPendingCompactState | undefined;\n\tfor (const [index, line] of lines.entries()) {\n\t\tif (!line) continue;\n\t\tlet envelope: unknown;\n\t\ttry {\n\t\t\tenvelope = JSON.parse(line) as unknown;\n\t\t} catch (error) {\n\t\t\tthrow new Error(`pi-server compact state envelope is invalid JSON at line ${index + 1}`, {\n\t\t\t\tcause: error,\n\t\t\t});\n\t\t}\n\t\tassertObject(envelope, `pi-server compact state envelope line ${index + 1}`);\n\t\tif (typeof envelope.payload !== \"string\") {\n\t\t\tthrow new Error(`pi-server compact state envelope payload must be a string at line ${index + 1}`);\n\t\t}\n\t\tassertHash(envelope.sha256, `pi-server compact state envelope line ${index + 1}.sha256`);\n\t\tif (sha256(envelope.payload) !== envelope.sha256) {\n\t\t\tthrow new Error(`pi-server compact state checksum mismatch at line ${index + 1}`);\n\t\t}\n\t\tconst record = parsePayload(envelope.payload, index + 1);\n\t\tif (record.sequence !== expectedSequence) {\n\t\t\tthrow new Error(\n\t\t\t\t`pi-server compact state sequence mismatch at line ${index + 1}: expected ${expectedSequence}, received ${record.sequence}`,\n\t\t\t);\n\t\t}\n\t\texpectedSequence++;\n\t\tif (record.kind === \"compact\") {\n\t\t\tif (pending) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`pi-server compact state contains a new operation before ${pending.operationId} was acknowledged`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tpending = record;\n\t\t} else if (record.kind === \"applied\" || record.kind === \"terminal\") {\n\t\t\tif (\n\t\t\t\t!pending ||\n\t\t\t\tpending.operationId !== record.operationId ||\n\t\t\t\tpending.requestHash !== record.requestHash ||\n\t\t\t\tpending.observation\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`pi-server compact state observation does not match the pending operation at line ${index + 1}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tpending = { ...pending, observation: record };\n\t\t} else {\n\t\t\tif (!pending || pending.operationId !== record.operationId) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`pi-server compact state acknowledgement does not match the pending operation at line ${index + 1}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tpending = undefined;\n\t\t}\n\t}\n\treturn { nextSequence: expectedSequence, pending };\n}\n\nfunction appendRecord(path: string, record: PiServerCompactStateRecord): void {\n\tconst payload = JSON.stringify(record);\n\tconst encoded = Buffer.from(`${JSON.stringify({ payload, sha256: sha256(payload) })}\\n`, \"utf-8\");\n\tconst existing = existsSync(path) ? readFileSync(path) : Buffer.alloc(0);\n\tconst validBytes = existing.lastIndexOf(0x0a) + 1;\n\tif (validBytes !== existing.byteLength) truncateSync(path, validBytes);\n\tif (validBytes + encoded.byteLength > COMPACT_STATE_MAX_BYTES) {\n\t\tthrow new Error(`pi-server compact state would exceed ${COMPACT_STATE_MAX_BYTES} bytes: ${path}`);\n\t}\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) throw new Error(`Failed to make progress writing pi-server compact state: ${path}`);\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 withCompactStateLock<T>(path: string, operation: () => T): T {\n\tconst normalizedPath = resolve(path);\n\tconst database = new DatabaseSync(`${normalizedPath}.lock.sqlite`);\n\tlet transactionStarted = false;\n\ttry {\n\t\tdatabase.exec(\"PRAGMA busy_timeout = 0\");\n\t\tdatabase.exec(\"CREATE TABLE IF NOT EXISTS compact_state_lock (id INTEGER PRIMARY KEY CHECK (id = 1))\");\n\t\tdatabase.exec(\"BEGIN IMMEDIATE\");\n\t\ttransactionStarted = true;\n\t\treturn operation();\n\t} catch (error) {\n\t\tconst details = error instanceof Error ? error.message : String(error);\n\t\tthrow new Error(`Cannot update pi-server compact state ${normalizedPath}: ${details}`, { cause: error });\n\t} finally {\n\t\tif (transactionStarted) {\n\t\t\ttry {\n\t\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t\t} catch {\n\t\t\t\t// Closing the connection is the authoritative lock release.\n\t\t\t}\n\t\t}\n\t\tdatabase.close();\n\t}\n}\n\nexport function getPiServerCompactStatePath(sessionFile: string): string {\n\treturn `${sessionFile}.pi-server-compacts.jsonl`;\n}\n\nexport function readPiServerPendingCompact(path: string): PiServerPendingCompactState | undefined {\n\treturn parseCompactStateFile(path).pending;\n}\n\nexport function writePiServerPendingCompact(\n\tpath: string,\n\tinput: WritePiServerPendingCompactInput,\n): PiServerPendingCompactState {\n\treturn withCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tif (state.pending) {\n\t\t\tthrow new Error(\n\t\t\t\t`pi-server compaction ${state.pending.operationId} must be acknowledged before starting another compaction`,\n\t\t\t);\n\t\t}\n\t\tif (state.nextSequence > 0) {\n\t\t\ttruncateSync(path, 0);\n\t\t\tstate.nextSequence = 0;\n\t\t}\n\t\tconst timestamp = input.timestamp ?? Date.now();\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact state timestamp\");\n\t\tassertHash(input.serverHash, \"pi-server compact state serverHash\");\n\t\tassertIdentifier(input.sessionId, \"pi-server compact state sessionId\");\n\t\tassertIdentifier(input.operationId, \"pi-server compact state operationId\");\n\t\tassertHash(input.requestHash, \"pi-server compact state requestHash\");\n\t\tassertHash(input.baseStaticContextHash, \"pi-server compact state baseStaticContextHash\", true);\n\t\tassertHash(input.baseTreeHash, \"pi-server compact state baseTreeHash\");\n\t\tassertNonNegativeSafeInteger(input.baseEntryCount, \"pi-server compact state baseEntryCount\");\n\t\tif (input.baseLeafId !== null) assertIdentifier(input.baseLeafId, \"pi-server compact state baseLeafId\");\n\t\tassertNonNegativeSafeInteger(input.baseRevision, \"pi-server compact state baseRevision\");\n\t\tconst record: PiServerPendingCompactState = {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"compact\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\tserverHash: input.serverHash,\n\t\t\tsessionId: input.sessionId,\n\t\t\toperationId: input.operationId,\n\t\t\trequestHash: input.requestHash,\n\t\t\tbaseStaticContextHash: input.baseStaticContextHash,\n\t\t\tbaseTreeHash: input.baseTreeHash,\n\t\t\tbaseEntryCount: input.baseEntryCount,\n\t\t\tbaseLeafId: input.baseLeafId,\n\t\t\tbaseRevision: input.baseRevision,\n\t\t};\n\t\tappendRecord(path, record);\n\t\treturn record;\n\t});\n}\n\nexport function writePiServerAppliedCompact(\n\tpath: string,\n\tinput: WritePiServerAppliedCompactInput,\n): PiServerAppliedCompactState {\n\treturn withCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tconst pending = state.pending;\n\t\tif (!pending || pending.operationId !== input.operationId || pending.requestHash !== input.requestHash) {\n\t\t\tthrow new Error(\"Cannot record an applied pi-server compaction without its matching pending operation\");\n\t\t}\n\t\tassertIdentifier(input.entryId, \"pi-server compact applied entryId\");\n\t\tassertHash(input.entryHash, \"pi-server compact applied entryHash\");\n\t\tassertHash(input.updatedTreeHash, \"pi-server compact applied updatedTreeHash\");\n\t\tassertIdentifier(input.updatedLeafId, \"pi-server compact applied updatedLeafId\");\n\t\tassertNonNegativeSafeInteger(input.updatedRevision, \"pi-server compact applied updatedRevision\");\n\t\tif (pending.observation) {\n\t\t\tif (\n\t\t\t\tpending.observation.kind !== \"applied\" ||\n\t\t\t\tpending.observation.entryId !== input.entryId ||\n\t\t\t\tpending.observation.entryHash !== input.entryHash ||\n\t\t\t\tpending.observation.updatedTreeHash !== input.updatedTreeHash ||\n\t\t\t\tpending.observation.updatedLeafId !== input.updatedLeafId ||\n\t\t\t\tpending.observation.updatedRevision !== input.updatedRevision\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Pi-server compaction already has a divergent durable observation\");\n\t\t\t}\n\t\t\treturn pending.observation;\n\t\t}\n\t\tconst timestamp = input.timestamp ?? Date.now();\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact applied timestamp\");\n\t\tconst record: PiServerAppliedCompactState = {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"applied\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\toperationId: input.operationId,\n\t\t\trequestHash: input.requestHash,\n\t\t\tentryId: input.entryId,\n\t\t\tentryHash: input.entryHash,\n\t\t\tupdatedTreeHash: input.updatedTreeHash,\n\t\t\tupdatedLeafId: input.updatedLeafId,\n\t\t\tupdatedRevision: input.updatedRevision,\n\t\t\tresolution: \"tree_applied\",\n\t\t};\n\t\tappendRecord(path, record);\n\t\treturn record;\n\t});\n}\n\nexport function writePiServerTerminalCompact(\n\tpath: string,\n\tinput: WritePiServerTerminalCompactInput,\n): PiServerTerminalCompactState {\n\treturn withCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tconst pending = state.pending;\n\t\tif (!pending || pending.operationId !== input.operationId || pending.requestHash !== input.requestHash) {\n\t\t\tthrow new Error(\"Cannot record a terminal pi-server compaction without its matching pending operation\");\n\t\t}\n\t\tif (!Number.isSafeInteger(input.httpStatus) || input.httpStatus < 100 || input.httpStatus > 599) {\n\t\t\tthrow new Error(\"pi-server compact terminal HTTP status is invalid\");\n\t\t}\n\t\tassertError(input.error, \"pi-server compact terminal error\");\n\t\tif (\n\t\t\t(input.operationDisposition === \"terminal\" && input.status !== \"failed\") ||\n\t\t\t(input.operationDisposition === \"not_started\" && input.status !== \"rejected\")\n\t\t) {\n\t\t\tthrow new Error(\"pi-server compact terminal disposition is invalid\");\n\t\t}\n\t\tif (pending.observation) {\n\t\t\tif (\n\t\t\t\tpending.observation.kind !== \"terminal\" ||\n\t\t\t\tpending.observation.httpStatus !== input.httpStatus ||\n\t\t\t\tpending.observation.error !== input.error ||\n\t\t\t\tpending.observation.operationDisposition !== input.operationDisposition ||\n\t\t\t\tpending.observation.status !== input.status\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Pi-server compaction already has a divergent durable observation\");\n\t\t\t}\n\t\t\treturn pending.observation;\n\t\t}\n\t\tconst timestamp = input.timestamp ?? Date.now();\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact terminal timestamp\");\n\t\tconst record: PiServerTerminalCompactState = {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"terminal\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\toperationId: input.operationId,\n\t\t\trequestHash: input.requestHash,\n\t\t\thttpStatus: input.httpStatus,\n\t\t\terror: input.error,\n\t\t\toperationDisposition: input.operationDisposition,\n\t\t\tstatus: input.status,\n\t\t\tresolution: input.operationDisposition === \"terminal\" ? \"terminal_failed_observed\" : \"server_rejected\",\n\t\t};\n\t\tappendRecord(path, record);\n\t\treturn record;\n\t});\n}\n\nexport function acknowledgePiServerPendingCompact(\n\tpath: string,\n\toperationId: string,\n\tresolution: PiServerCompactAcknowledgementResolution = \"server_ack\",\n\ttimestamp = Date.now(),\n): void {\n\twithCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tif (!state.pending) return;\n\t\tif (state.pending.operationId !== operationId) {\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot acknowledge pi-server compaction ${operationId}; pending operation is ${state.pending.operationId}`,\n\t\t\t);\n\t\t}\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact acknowledgement timestamp\");\n\t\tappendRecord(path, {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"ack\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\toperationId,\n\t\t\tresolution,\n\t\t});\n\t});\n}\n"]}
1
+ {"version":3,"file":"pi-server-compact-state.d.ts","sourceRoot":"","sources":["../../src/core/pi-server-compact-state.ts"],"names":[],"mappings":"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","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { createHash } from \"node:crypto\";\nimport { closeSync, existsSync, fsyncSync, openSync, readFileSync, truncateSync, writeSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { DatabaseSync } from \"node:sqlite\";\n\nconst COMPACT_STATE_VERSION = 1;\nconst COMPACT_STATE_MAX_BYTES = 8 * 1024 * 1024;\nconst SHA256_PATTERN = /^[a-f0-9]{64}$/u;\nconst IDENTIFIER_MAX_CHARS = 4096;\nconst ERROR_MAX_CHARS = 1024 * 1024;\n\ninterface PiServerCompactStateBase {\n\tversion: typeof COMPACT_STATE_VERSION;\n\tsequence: number;\n\ttimestamp: number;\n}\n\nexport interface PiServerPendingCompactState extends PiServerCompactStateBase {\n\tkind: \"compact\";\n\tserverHash: string;\n\tsessionId: string;\n\toperationId: string;\n\trequestHash: string;\n\tbaseStaticContextHash: string;\n\tbaseTreeHash: string;\n\tbaseEntryCount: number;\n\tbaseLeafId: string | null;\n\tbaseRevision: number;\n\tobservation?: PiServerCompactObservationState;\n}\n\nexport interface PiServerAppliedCompactState extends PiServerCompactStateBase {\n\tkind: \"applied\";\n\toperationId: string;\n\trequestHash: string;\n\tentryId: string;\n\tentryHash: string;\n\tupdatedTreeHash: string;\n\tupdatedLeafId: string;\n\tupdatedRevision: number;\n\tresolution: \"tree_applied\";\n}\n\nexport interface PiServerTerminalCompactState extends PiServerCompactStateBase {\n\tkind: \"terminal\";\n\toperationId: string;\n\trequestHash: string;\n\thttpStatus: number;\n\terror: string;\n\toperationDisposition: \"terminal\" | \"not_started\";\n\tstatus: \"failed\" | \"rejected\";\n\tresolution: \"terminal_failed_observed\" | \"server_rejected\";\n}\n\nexport type PiServerCompactObservationState = PiServerAppliedCompactState | PiServerTerminalCompactState;\n\nexport type PiServerCompactAcknowledgementResolution =\n\t| \"server_ack\"\n\t| \"server_missing_after_tree_applied\"\n\t| \"server_missing_after_terminal_failure\"\n\t| \"server_rejected_not_started\"\n\t| \"server_authoritative_missing\";\n\ninterface PiServerAcknowledgedCompactState extends PiServerCompactStateBase {\n\tkind: \"ack\";\n\toperationId: string;\n\tresolution: PiServerCompactAcknowledgementResolution;\n}\n\ntype PiServerCompactStateRecord =\n\t| PiServerPendingCompactState\n\t| PiServerCompactObservationState\n\t| PiServerAcknowledgedCompactState;\n\ninterface ParsedPiServerCompactState {\n\tnextSequence: number;\n\tpending: PiServerPendingCompactState | undefined;\n}\n\nexport interface WritePiServerPendingCompactInput {\n\tserverHash: string;\n\tsessionId: string;\n\toperationId: string;\n\trequestHash: string;\n\tbaseStaticContextHash: string;\n\tbaseTreeHash: string;\n\tbaseEntryCount: number;\n\tbaseLeafId: string | null;\n\tbaseRevision: number;\n\ttimestamp?: number;\n}\n\nexport interface WritePiServerAppliedCompactInput {\n\toperationId: string;\n\trequestHash: string;\n\tentryId: string;\n\tentryHash: string;\n\tupdatedTreeHash: string;\n\tupdatedLeafId: string;\n\tupdatedRevision: number;\n\ttimestamp?: number;\n}\n\nexport interface WritePiServerTerminalCompactInput {\n\toperationId: string;\n\trequestHash: string;\n\thttpStatus: number;\n\terror: string;\n\toperationDisposition: \"terminal\" | \"not_started\";\n\tstatus: \"failed\" | \"rejected\";\n\ttimestamp?: number;\n}\n\nfunction sha256(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\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 assertIdentifier(value: unknown, path: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.length === 0 || value.length > IDENTIFIER_MAX_CHARS || value.includes(\"\\0\")) {\n\t\tthrow new Error(`${path} must be a non-empty string without NUL bytes`);\n\t}\n}\n\nfunction assertHash(value: unknown, path: string, allowEmpty = false): asserts value is string {\n\tif (\n\t\ttypeof value !== \"string\" ||\n\t\t(!allowEmpty && !SHA256_PATTERN.test(value)) ||\n\t\t(allowEmpty && value !== \"\" && !SHA256_PATTERN.test(value))\n\t) {\n\t\tthrow new Error(`${path} must be ${allowEmpty ? \"empty or \" : \"\"}a lowercase SHA-256 digest`);\n\t}\n}\n\nfunction assertError(value: unknown, path: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.length === 0 || value.length > ERROR_MAX_CHARS || value.includes(\"\\0\")) {\n\t\tthrow new Error(`${path} must be a non-empty string without NUL bytes`);\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 parsePayload(payload: string, lineNumber: number): PiServerCompactStateRecord {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(payload) as unknown;\n\t} catch (error) {\n\t\tthrow new Error(`pi-server compact state payload is invalid JSON at line ${lineNumber}`, { cause: error });\n\t}\n\tassertObject(value, `pi-server compact state line ${lineNumber}`);\n\tif (value.version !== COMPACT_STATE_VERSION) {\n\t\tthrow new Error(`Unsupported pi-server compact state version at line ${lineNumber}`);\n\t}\n\tassertNonNegativeSafeInteger(value.sequence, `pi-server compact state line ${lineNumber}.sequence`);\n\tassertNonNegativeSafeInteger(value.timestamp, `pi-server compact state line ${lineNumber}.timestamp`);\n\tif (value.kind === \"ack\") {\n\t\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\t\tif (\n\t\t\tvalue.resolution !== \"server_ack\" &&\n\t\t\tvalue.resolution !== \"server_missing_after_tree_applied\" &&\n\t\t\tvalue.resolution !== \"server_missing_after_terminal_failure\" &&\n\t\t\tvalue.resolution !== \"server_rejected_not_started\" &&\n\t\t\tvalue.resolution !== \"server_authoritative_missing\"\n\t\t) {\n\t\t\tthrow new Error(`pi-server compact state acknowledgement resolution is invalid at line ${lineNumber}`);\n\t\t}\n\t\treturn {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"ack\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\toperationId: value.operationId,\n\t\t\tresolution: value.resolution,\n\t\t};\n\t}\n\tif (value.kind === \"applied\") {\n\t\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\t\tassertHash(value.requestHash, `pi-server compact state line ${lineNumber}.requestHash`);\n\t\tassertIdentifier(value.entryId, `pi-server compact state line ${lineNumber}.entryId`);\n\t\tassertHash(value.entryHash, `pi-server compact state line ${lineNumber}.entryHash`);\n\t\tassertHash(value.updatedTreeHash, `pi-server compact state line ${lineNumber}.updatedTreeHash`);\n\t\tassertIdentifier(value.updatedLeafId, `pi-server compact state line ${lineNumber}.updatedLeafId`);\n\t\tassertNonNegativeSafeInteger(value.updatedRevision, `pi-server compact state line ${lineNumber}.updatedRevision`);\n\t\tif (value.resolution !== \"tree_applied\") {\n\t\t\tthrow new Error(`pi-server compact applied resolution is invalid at line ${lineNumber}`);\n\t\t}\n\t\treturn {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"applied\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\toperationId: value.operationId,\n\t\t\trequestHash: value.requestHash,\n\t\t\tentryId: value.entryId,\n\t\t\tentryHash: value.entryHash,\n\t\t\tupdatedTreeHash: value.updatedTreeHash,\n\t\t\tupdatedLeafId: value.updatedLeafId,\n\t\t\tupdatedRevision: value.updatedRevision,\n\t\t\tresolution: value.resolution,\n\t\t};\n\t}\n\tif (value.kind === \"terminal\") {\n\t\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\t\tassertHash(value.requestHash, `pi-server compact state line ${lineNumber}.requestHash`);\n\t\tif (\n\t\t\t!Number.isSafeInteger(value.httpStatus) ||\n\t\t\t(value.httpStatus as number) < 100 ||\n\t\t\t(value.httpStatus as number) > 599\n\t\t) {\n\t\t\tthrow new Error(`pi-server compact terminal HTTP status is invalid at line ${lineNumber}`);\n\t\t}\n\t\tassertError(value.error, `pi-server compact state line ${lineNumber}.error`);\n\t\tif (\n\t\t\t(value.operationDisposition !== \"terminal\" && value.operationDisposition !== \"not_started\") ||\n\t\t\t(value.status !== \"failed\" && value.status !== \"rejected\") ||\n\t\t\t(value.operationDisposition === \"terminal\" && value.status !== \"failed\") ||\n\t\t\t(value.operationDisposition === \"not_started\" && value.status !== \"rejected\")\n\t\t) {\n\t\t\tthrow new Error(`pi-server compact terminal disposition is invalid at line ${lineNumber}`);\n\t\t}\n\t\tconst expectedResolution =\n\t\t\tvalue.operationDisposition === \"terminal\" ? \"terminal_failed_observed\" : \"server_rejected\";\n\t\tif (value.resolution !== expectedResolution) {\n\t\t\tthrow new Error(`pi-server compact terminal resolution is invalid at line ${lineNumber}`);\n\t\t}\n\t\treturn {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"terminal\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\toperationId: value.operationId,\n\t\t\trequestHash: value.requestHash,\n\t\t\thttpStatus: value.httpStatus as number,\n\t\t\terror: value.error,\n\t\t\toperationDisposition: value.operationDisposition,\n\t\t\tstatus: value.status,\n\t\t\tresolution: expectedResolution,\n\t\t};\n\t}\n\tif (value.kind !== \"compact\") {\n\t\tthrow new Error(`Unsupported pi-server compact state record kind at line ${lineNumber}`);\n\t}\n\tassertHash(value.serverHash, `pi-server compact state line ${lineNumber}.serverHash`);\n\tassertIdentifier(value.sessionId, `pi-server compact state line ${lineNumber}.sessionId`);\n\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\tassertHash(value.requestHash, `pi-server compact state line ${lineNumber}.requestHash`);\n\tassertHash(value.baseStaticContextHash, `pi-server compact state line ${lineNumber}.baseStaticContextHash`, true);\n\tassertHash(value.baseTreeHash, `pi-server compact state line ${lineNumber}.baseTreeHash`);\n\tassertNonNegativeSafeInteger(value.baseEntryCount, `pi-server compact state line ${lineNumber}.baseEntryCount`);\n\tif (value.baseLeafId !== null) {\n\t\tassertIdentifier(value.baseLeafId, `pi-server compact state line ${lineNumber}.baseLeafId`);\n\t}\n\tassertNonNegativeSafeInteger(value.baseRevision, `pi-server compact state line ${lineNumber}.baseRevision`);\n\treturn {\n\t\tversion: COMPACT_STATE_VERSION,\n\t\tkind: \"compact\",\n\t\tsequence: value.sequence,\n\t\ttimestamp: value.timestamp,\n\t\tserverHash: value.serverHash,\n\t\tsessionId: value.sessionId,\n\t\toperationId: value.operationId,\n\t\trequestHash: value.requestHash,\n\t\tbaseStaticContextHash: value.baseStaticContextHash,\n\t\tbaseTreeHash: value.baseTreeHash,\n\t\tbaseEntryCount: value.baseEntryCount,\n\t\tbaseLeafId: value.baseLeafId,\n\t\tbaseRevision: value.baseRevision,\n\t};\n}\n\nfunction parseCompactStateFile(path: string): ParsedPiServerCompactState {\n\tif (!existsSync(path)) return { nextSequence: 0, pending: undefined };\n\tconst encoded = readFileSync(path);\n\tif (encoded.byteLength > COMPACT_STATE_MAX_BYTES) {\n\t\tthrow new Error(`pi-server compact state exceeds ${COMPACT_STATE_MAX_BYTES} bytes: ${path}`);\n\t}\n\tconst content = encoded.toString(\"utf-8\");\n\tconst lines = content.split(\"\\n\");\n\tif (!content.endsWith(\"\\n\")) lines.pop();\n\tlet expectedSequence = 0;\n\tlet pending: PiServerPendingCompactState | undefined;\n\tfor (const [index, line] of lines.entries()) {\n\t\tif (!line) continue;\n\t\tlet envelope: unknown;\n\t\ttry {\n\t\t\tenvelope = JSON.parse(line) as unknown;\n\t\t} catch (error) {\n\t\t\tthrow new Error(`pi-server compact state envelope is invalid JSON at line ${index + 1}`, {\n\t\t\t\tcause: error,\n\t\t\t});\n\t\t}\n\t\tassertObject(envelope, `pi-server compact state envelope line ${index + 1}`);\n\t\tif (typeof envelope.payload !== \"string\") {\n\t\t\tthrow new Error(`pi-server compact state envelope payload must be a string at line ${index + 1}`);\n\t\t}\n\t\tassertHash(envelope.sha256, `pi-server compact state envelope line ${index + 1}.sha256`);\n\t\tif (sha256(envelope.payload) !== envelope.sha256) {\n\t\t\tthrow new Error(`pi-server compact state checksum mismatch at line ${index + 1}`);\n\t\t}\n\t\tconst record = parsePayload(envelope.payload, index + 1);\n\t\tif (record.sequence !== expectedSequence) {\n\t\t\tthrow new Error(\n\t\t\t\t`pi-server compact state sequence mismatch at line ${index + 1}: expected ${expectedSequence}, received ${record.sequence}`,\n\t\t\t);\n\t\t}\n\t\texpectedSequence++;\n\t\tif (record.kind === \"compact\") {\n\t\t\tif (pending) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`pi-server compact state contains a new operation before ${pending.operationId} was acknowledged`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tpending = record;\n\t\t} else if (record.kind === \"applied\" || record.kind === \"terminal\") {\n\t\t\tif (\n\t\t\t\t!pending ||\n\t\t\t\tpending.operationId !== record.operationId ||\n\t\t\t\tpending.requestHash !== record.requestHash ||\n\t\t\t\tpending.observation\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`pi-server compact state observation does not match the pending operation at line ${index + 1}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tpending = { ...pending, observation: record };\n\t\t} else {\n\t\t\tif (!pending || pending.operationId !== record.operationId) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`pi-server compact state acknowledgement does not match the pending operation at line ${index + 1}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tpending = undefined;\n\t\t}\n\t}\n\treturn { nextSequence: expectedSequence, pending };\n}\n\nfunction appendRecord(path: string, record: PiServerCompactStateRecord): void {\n\tconst payload = JSON.stringify(record);\n\tconst encoded = Buffer.from(`${JSON.stringify({ payload, sha256: sha256(payload) })}\\n`, \"utf-8\");\n\tconst existing = existsSync(path) ? readFileSync(path) : Buffer.alloc(0);\n\tconst validBytes = existing.lastIndexOf(0x0a) + 1;\n\tif (validBytes !== existing.byteLength) truncateSync(path, validBytes);\n\tif (validBytes + encoded.byteLength > COMPACT_STATE_MAX_BYTES) {\n\t\tthrow new Error(`pi-server compact state would exceed ${COMPACT_STATE_MAX_BYTES} bytes: ${path}`);\n\t}\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) throw new Error(`Failed to make progress writing pi-server compact state: ${path}`);\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 withCompactStateLock<T>(path: string, operation: () => T): T {\n\tconst normalizedPath = resolve(path);\n\tconst database = new DatabaseSync(`${normalizedPath}.lock.sqlite`);\n\tlet transactionStarted = false;\n\ttry {\n\t\tdatabase.exec(\"PRAGMA busy_timeout = 0\");\n\t\tdatabase.exec(\"CREATE TABLE IF NOT EXISTS compact_state_lock (id INTEGER PRIMARY KEY CHECK (id = 1))\");\n\t\tdatabase.exec(\"BEGIN IMMEDIATE\");\n\t\ttransactionStarted = true;\n\t\treturn operation();\n\t} catch (error) {\n\t\tconst details = error instanceof Error ? error.message : String(error);\n\t\tthrow new Error(`Cannot update pi-server compact state ${normalizedPath}: ${details}`, { cause: error });\n\t} finally {\n\t\tif (transactionStarted) {\n\t\t\ttry {\n\t\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t\t} catch {\n\t\t\t\t// Closing the connection is the authoritative lock release.\n\t\t\t}\n\t\t}\n\t\tdatabase.close();\n\t}\n}\n\nexport function getPiServerCompactStatePath(sessionFile: string): string {\n\treturn `${sessionFile}.pi-server-compacts.jsonl`;\n}\n\nexport function readPiServerPendingCompact(path: string): PiServerPendingCompactState | undefined {\n\treturn parseCompactStateFile(path).pending;\n}\n\nexport function writePiServerPendingCompact(\n\tpath: string,\n\tinput: WritePiServerPendingCompactInput,\n): PiServerPendingCompactState {\n\treturn withCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tif (state.pending) {\n\t\t\tthrow new Error(\n\t\t\t\t`pi-server compaction ${state.pending.operationId} must be acknowledged before starting another compaction`,\n\t\t\t);\n\t\t}\n\t\tif (state.nextSequence > 0) {\n\t\t\ttruncateSync(path, 0);\n\t\t\tstate.nextSequence = 0;\n\t\t}\n\t\tconst timestamp = input.timestamp ?? Date.now();\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact state timestamp\");\n\t\tassertHash(input.serverHash, \"pi-server compact state serverHash\");\n\t\tassertIdentifier(input.sessionId, \"pi-server compact state sessionId\");\n\t\tassertIdentifier(input.operationId, \"pi-server compact state operationId\");\n\t\tassertHash(input.requestHash, \"pi-server compact state requestHash\");\n\t\tassertHash(input.baseStaticContextHash, \"pi-server compact state baseStaticContextHash\", true);\n\t\tassertHash(input.baseTreeHash, \"pi-server compact state baseTreeHash\");\n\t\tassertNonNegativeSafeInteger(input.baseEntryCount, \"pi-server compact state baseEntryCount\");\n\t\tif (input.baseLeafId !== null) assertIdentifier(input.baseLeafId, \"pi-server compact state baseLeafId\");\n\t\tassertNonNegativeSafeInteger(input.baseRevision, \"pi-server compact state baseRevision\");\n\t\tconst record: PiServerPendingCompactState = {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"compact\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\tserverHash: input.serverHash,\n\t\t\tsessionId: input.sessionId,\n\t\t\toperationId: input.operationId,\n\t\t\trequestHash: input.requestHash,\n\t\t\tbaseStaticContextHash: input.baseStaticContextHash,\n\t\t\tbaseTreeHash: input.baseTreeHash,\n\t\t\tbaseEntryCount: input.baseEntryCount,\n\t\t\tbaseLeafId: input.baseLeafId,\n\t\t\tbaseRevision: input.baseRevision,\n\t\t};\n\t\tappendRecord(path, record);\n\t\treturn record;\n\t});\n}\n\nexport function writePiServerAppliedCompact(\n\tpath: string,\n\tinput: WritePiServerAppliedCompactInput,\n): PiServerAppliedCompactState {\n\treturn withCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tconst pending = state.pending;\n\t\tif (!pending || pending.operationId !== input.operationId || pending.requestHash !== input.requestHash) {\n\t\t\tthrow new Error(\"Cannot record an applied pi-server compaction without its matching pending operation\");\n\t\t}\n\t\tassertIdentifier(input.entryId, \"pi-server compact applied entryId\");\n\t\tassertHash(input.entryHash, \"pi-server compact applied entryHash\");\n\t\tassertHash(input.updatedTreeHash, \"pi-server compact applied updatedTreeHash\");\n\t\tassertIdentifier(input.updatedLeafId, \"pi-server compact applied updatedLeafId\");\n\t\tassertNonNegativeSafeInteger(input.updatedRevision, \"pi-server compact applied updatedRevision\");\n\t\tif (pending.observation) {\n\t\t\tif (\n\t\t\t\tpending.observation.kind !== \"applied\" ||\n\t\t\t\tpending.observation.entryId !== input.entryId ||\n\t\t\t\tpending.observation.entryHash !== input.entryHash ||\n\t\t\t\tpending.observation.updatedTreeHash !== input.updatedTreeHash ||\n\t\t\t\tpending.observation.updatedLeafId !== input.updatedLeafId ||\n\t\t\t\tpending.observation.updatedRevision !== input.updatedRevision\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Pi-server compaction already has a divergent durable observation\");\n\t\t\t}\n\t\t\treturn pending.observation;\n\t\t}\n\t\tconst timestamp = input.timestamp ?? Date.now();\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact applied timestamp\");\n\t\tconst record: PiServerAppliedCompactState = {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"applied\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\toperationId: input.operationId,\n\t\t\trequestHash: input.requestHash,\n\t\t\tentryId: input.entryId,\n\t\t\tentryHash: input.entryHash,\n\t\t\tupdatedTreeHash: input.updatedTreeHash,\n\t\t\tupdatedLeafId: input.updatedLeafId,\n\t\t\tupdatedRevision: input.updatedRevision,\n\t\t\tresolution: \"tree_applied\",\n\t\t};\n\t\tappendRecord(path, record);\n\t\treturn record;\n\t});\n}\n\nexport function writePiServerTerminalCompact(\n\tpath: string,\n\tinput: WritePiServerTerminalCompactInput,\n): PiServerTerminalCompactState {\n\treturn withCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tconst pending = state.pending;\n\t\tif (!pending || pending.operationId !== input.operationId || pending.requestHash !== input.requestHash) {\n\t\t\tthrow new Error(\"Cannot record a terminal pi-server compaction without its matching pending operation\");\n\t\t}\n\t\tif (!Number.isSafeInteger(input.httpStatus) || input.httpStatus < 100 || input.httpStatus > 599) {\n\t\t\tthrow new Error(\"pi-server compact terminal HTTP status is invalid\");\n\t\t}\n\t\tassertError(input.error, \"pi-server compact terminal error\");\n\t\tif (\n\t\t\t(input.operationDisposition === \"terminal\" && input.status !== \"failed\") ||\n\t\t\t(input.operationDisposition === \"not_started\" && input.status !== \"rejected\")\n\t\t) {\n\t\t\tthrow new Error(\"pi-server compact terminal disposition is invalid\");\n\t\t}\n\t\tif (pending.observation) {\n\t\t\tif (\n\t\t\t\tpending.observation.kind !== \"terminal\" ||\n\t\t\t\tpending.observation.httpStatus !== input.httpStatus ||\n\t\t\t\tpending.observation.error !== input.error ||\n\t\t\t\tpending.observation.operationDisposition !== input.operationDisposition ||\n\t\t\t\tpending.observation.status !== input.status\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Pi-server compaction already has a divergent durable observation\");\n\t\t\t}\n\t\t\treturn pending.observation;\n\t\t}\n\t\tconst timestamp = input.timestamp ?? Date.now();\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact terminal timestamp\");\n\t\tconst record: PiServerTerminalCompactState = {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"terminal\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\toperationId: input.operationId,\n\t\t\trequestHash: input.requestHash,\n\t\t\thttpStatus: input.httpStatus,\n\t\t\terror: input.error,\n\t\t\toperationDisposition: input.operationDisposition,\n\t\t\tstatus: input.status,\n\t\t\tresolution: input.operationDisposition === \"terminal\" ? \"terminal_failed_observed\" : \"server_rejected\",\n\t\t};\n\t\tappendRecord(path, record);\n\t\treturn record;\n\t});\n}\n\nexport function acknowledgePiServerPendingCompact(\n\tpath: string,\n\toperationId: string,\n\tresolution: PiServerCompactAcknowledgementResolution = \"server_ack\",\n\ttimestamp = Date.now(),\n): void {\n\twithCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tif (!state.pending) return;\n\t\tif (state.pending.operationId !== operationId) {\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot acknowledge pi-server compaction ${operationId}; pending operation is ${state.pending.operationId}`,\n\t\t\t);\n\t\t}\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact acknowledgement timestamp\");\n\t\tappendRecord(path, {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"ack\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\toperationId,\n\t\t\tresolution,\n\t\t});\n\t});\n}\n"]}
@@ -57,7 +57,8 @@ function parsePayload(payload, lineNumber) {
57
57
  if (value.resolution !== "server_ack" &&
58
58
  value.resolution !== "server_missing_after_tree_applied" &&
59
59
  value.resolution !== "server_missing_after_terminal_failure" &&
60
- value.resolution !== "server_rejected_not_started") {
60
+ value.resolution !== "server_rejected_not_started" &&
61
+ value.resolution !== "server_authoritative_missing") {
61
62
  throw new Error(`pi-server compact state acknowledgement resolution is invalid at line ${lineNumber}`);
62
63
  }
63
64
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"pi-server-compact-state.js","sourceRoot":"","sources":["../../src/core/pi-server-compact-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC5G,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAChC,MAAM,uBAAuB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAChD,MAAM,cAAc,GAAG,iBAAiB,CAAC;AACzC,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAClC,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,CAAC;AAuGpC,SAAS,MAAM,CAAC,KAAa,EAAU;IACtC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAAA,CACxD;AAED,SAAS,YAAY,CAAC,KAAc,EAAE,IAAY,EAA4C;IAC7F,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,oBAAoB,CAAC,CAAC;IAC9C,CAAC;AAAA,CACD;AAED,SAAS,gBAAgB,CAAC,KAAc,EAAE,IAAY,EAA2B;IAChF,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,oBAAoB,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACpH,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,+CAA+C,CAAC,CAAC;IACzE,CAAC;AAAA,CACD;AAED,SAAS,UAAU,CAAC,KAAc,EAAE,IAAY,EAAE,UAAU,GAAG,KAAK,EAA2B;IAC9F,IACC,OAAO,KAAK,KAAK,QAAQ;QACzB,CAAC,CAAC,UAAU,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,CAAC,UAAU,IAAI,KAAK,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAC1D,CAAC;QACF,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,YAAY,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,4BAA4B,CAAC,CAAC;IAC/F,CAAC;AAAA,CACD;AAED,SAAS,WAAW,CAAC,KAAc,EAAE,IAAY,EAA2B;IAC3E,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,eAAe,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/G,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,+CAA+C,CAAC,CAAC;IACzE,CAAC;AAAA,CACD;AAED,SAAS,4BAA4B,CAAC,KAAc,EAAE,IAAY,EAA2B;IAC5F,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAK,KAAgB,GAAG,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,sCAAsC,CAAC,CAAC;IAChE,CAAC;AAAA,CACD;AAED,SAAS,YAAY,CAAC,OAAe,EAAE,UAAkB,EAA8B;IACtF,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACJ,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAY,CAAC;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,2DAA2D,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5G,CAAC;IACD,YAAY,CAAC,KAAK,EAAE,gCAAgC,UAAU,EAAE,CAAC,CAAC;IAClE,IAAI,KAAK,CAAC,OAAO,KAAK,qBAAqB,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,uDAAuD,UAAU,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,4BAA4B,CAAC,KAAK,CAAC,QAAQ,EAAE,gCAAgC,UAAU,WAAW,CAAC,CAAC;IACpG,4BAA4B,CAAC,KAAK,CAAC,SAAS,EAAE,gCAAgC,UAAU,YAAY,CAAC,CAAC;IACtG,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC1B,gBAAgB,CAAC,KAAK,CAAC,WAAW,EAAE,gCAAgC,UAAU,cAAc,CAAC,CAAC;QAC9F,IACC,KAAK,CAAC,UAAU,KAAK,YAAY;YACjC,KAAK,CAAC,UAAU,KAAK,mCAAmC;YACxD,KAAK,CAAC,UAAU,KAAK,uCAAuC;YAC5D,KAAK,CAAC,UAAU,KAAK,6BAA6B,EACjD,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,yEAAyE,UAAU,EAAE,CAAC,CAAC;QACxG,CAAC;QACD,OAAO;YACN,OAAO,EAAE,qBAAqB;YAC9B,IAAI,EAAE,KAAK;YACX,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,UAAU,EAAE,KAAK,CAAC,UAAU;SAC5B,CAAC;IACH,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,gBAAgB,CAAC,KAAK,CAAC,WAAW,EAAE,gCAAgC,UAAU,cAAc,CAAC,CAAC;QAC9F,UAAU,CAAC,KAAK,CAAC,WAAW,EAAE,gCAAgC,UAAU,cAAc,CAAC,CAAC;QACxF,gBAAgB,CAAC,KAAK,CAAC,OAAO,EAAE,gCAAgC,UAAU,UAAU,CAAC,CAAC;QACtF,UAAU,CAAC,KAAK,CAAC,SAAS,EAAE,gCAAgC,UAAU,YAAY,CAAC,CAAC;QACpF,UAAU,CAAC,KAAK,CAAC,eAAe,EAAE,gCAAgC,UAAU,kBAAkB,CAAC,CAAC;QAChG,gBAAgB,CAAC,KAAK,CAAC,aAAa,EAAE,gCAAgC,UAAU,gBAAgB,CAAC,CAAC;QAClG,4BAA4B,CAAC,KAAK,CAAC,eAAe,EAAE,gCAAgC,UAAU,kBAAkB,CAAC,CAAC;QAClH,IAAI,KAAK,CAAC,UAAU,KAAK,cAAc,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CAAC,2DAA2D,UAAU,EAAE,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO;YACN,OAAO,EAAE,qBAAqB;YAC9B,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,UAAU,EAAE,KAAK,CAAC,UAAU;SAC5B,CAAC;IACH,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC/B,gBAAgB,CAAC,KAAK,CAAC,WAAW,EAAE,gCAAgC,UAAU,cAAc,CAAC,CAAC;QAC9F,UAAU,CAAC,KAAK,CAAC,WAAW,EAAE,gCAAgC,UAAU,cAAc,CAAC,CAAC;QACxF,IACC,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC;YACtC,KAAK,CAAC,UAAqB,GAAG,GAAG;YACjC,KAAK,CAAC,UAAqB,GAAG,GAAG,EACjC,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,6DAA6D,UAAU,EAAE,CAAC,CAAC;QAC5F,CAAC;QACD,WAAW,CAAC,KAAK,CAAC,KAAK,EAAE,gCAAgC,UAAU,QAAQ,CAAC,CAAC;QAC7E,IACC,CAAC,KAAK,CAAC,oBAAoB,KAAK,UAAU,IAAI,KAAK,CAAC,oBAAoB,KAAK,aAAa,CAAC;YAC3F,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU,CAAC;YAC1D,CAAC,KAAK,CAAC,oBAAoB,KAAK,UAAU,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC;YACxE,CAAC,KAAK,CAAC,oBAAoB,KAAK,aAAa,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU,CAAC,EAC5E,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,6DAA6D,UAAU,EAAE,CAAC,CAAC;QAC5F,CAAC;QACD,MAAM,kBAAkB,GACvB,KAAK,CAAC,oBAAoB,KAAK,UAAU,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,iBAAiB,CAAC;QAC5F,IAAI,KAAK,CAAC,UAAU,KAAK,kBAAkB,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,4DAA4D,UAAU,EAAE,CAAC,CAAC;QAC3F,CAAC;QACD,OAAO;YACN,OAAO,EAAE,qBAAqB;YAC9B,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,UAAU,EAAE,KAAK,CAAC,UAAoB;YACtC,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;YAChD,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,UAAU,EAAE,kBAAkB;SAC9B,CAAC;IACH,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,2DAA2D,UAAU,EAAE,CAAC,CAAC;IAC1F,CAAC;IACD,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,gCAAgC,UAAU,aAAa,CAAC,CAAC;IACtF,gBAAgB,CAAC,KAAK,CAAC,SAAS,EAAE,gCAAgC,UAAU,YAAY,CAAC,CAAC;IAC1F,gBAAgB,CAAC,KAAK,CAAC,WAAW,EAAE,gCAAgC,UAAU,cAAc,CAAC,CAAC;IAC9F,UAAU,CAAC,KAAK,CAAC,WAAW,EAAE,gCAAgC,UAAU,cAAc,CAAC,CAAC;IACxF,UAAU,CAAC,KAAK,CAAC,qBAAqB,EAAE,gCAAgC,UAAU,wBAAwB,EAAE,IAAI,CAAC,CAAC;IAClH,UAAU,CAAC,KAAK,CAAC,YAAY,EAAE,gCAAgC,UAAU,eAAe,CAAC,CAAC;IAC1F,4BAA4B,CAAC,KAAK,CAAC,cAAc,EAAE,gCAAgC,UAAU,iBAAiB,CAAC,CAAC;IAChH,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;QAC/B,gBAAgB,CAAC,KAAK,CAAC,UAAU,EAAE,gCAAgC,UAAU,aAAa,CAAC,CAAC;IAC7F,CAAC;IACD,4BAA4B,CAAC,KAAK,CAAC,YAAY,EAAE,gCAAgC,UAAU,eAAe,CAAC,CAAC;IAC5G,OAAO;QACN,OAAO,EAAE,qBAAqB;QAC9B,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,qBAAqB,EAAE,KAAK,CAAC,qBAAqB;QAClD,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,YAAY,EAAE,KAAK,CAAC,YAAY;KAChC,CAAC;AAAA,CACF;AAED,SAAS,qBAAqB,CAAC,IAAY,EAA8B;IACxE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IACtE,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,OAAO,CAAC,UAAU,GAAG,uBAAuB,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,mCAAmC,uBAAuB,WAAW,IAAI,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,KAAK,CAAC,GAAG,EAAE,CAAC;IACzC,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,IAAI,OAAgD,CAAC;IACrD,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC7C,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,IAAI,QAAiB,CAAC;QACtB,IAAI,CAAC;YACJ,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,4DAA4D,KAAK,GAAG,CAAC,EAAE,EAAE;gBACxF,KAAK,EAAE,KAAK;aACZ,CAAC,CAAC;QACJ,CAAC;QACD,YAAY,CAAC,QAAQ,EAAE,yCAAyC,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7E,IAAI,OAAO,QAAQ,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CAAC,qEAAqE,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC;QACnG,CAAC;QACD,UAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,yCAAyC,KAAK,GAAG,CAAC,SAAS,CAAC,CAAC;QACzF,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CAAC,qDAAqD,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC;QACnF,CAAC;QACD,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QACzD,IAAI,MAAM,CAAC,QAAQ,KAAK,gBAAgB,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CACd,qDAAqD,KAAK,GAAG,CAAC,cAAc,gBAAgB,cAAc,MAAM,CAAC,QAAQ,EAAE,CAC3H,CAAC;QACH,CAAC;QACD,gBAAgB,EAAE,CAAC;QACnB,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CACd,2DAA2D,OAAO,CAAC,WAAW,mBAAmB,CACjG,CAAC;YACH,CAAC;YACD,OAAO,GAAG,MAAM,CAAC;QAClB,CAAC;aAAM,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACpE,IACC,CAAC,OAAO;gBACR,OAAO,CAAC,WAAW,KAAK,MAAM,CAAC,WAAW;gBAC1C,OAAO,CAAC,WAAW,KAAK,MAAM,CAAC,WAAW;gBAC1C,OAAO,CAAC,WAAW,EAClB,CAAC;gBACF,MAAM,IAAI,KAAK,CACd,oFAAoF,KAAK,GAAG,CAAC,EAAE,CAC/F,CAAC;YACH,CAAC;YACD,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;QAC/C,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,WAAW,KAAK,MAAM,CAAC,WAAW,EAAE,CAAC;gBAC5D,MAAM,IAAI,KAAK,CACd,wFAAwF,KAAK,GAAG,CAAC,EAAE,CACnG,CAAC;YACH,CAAC;YACD,OAAO,GAAG,SAAS,CAAC;QACrB,CAAC;IACF,CAAC;IACD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC;AAAA,CACnD;AAED,SAAS,YAAY,CAAC,IAAY,EAAE,MAAkC,EAAQ;IAC7E,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAClG,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACzE,MAAM,UAAU,GAAG,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClD,IAAI,UAAU,KAAK,QAAQ,CAAC,UAAU;QAAE,YAAY,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACvE,IAAI,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,uBAAuB,EAAE,CAAC;QAC/D,MAAM,IAAI,KAAK,CAAC,wCAAwC,uBAAuB,WAAW,IAAI,EAAE,CAAC,CAAC;IACnG,CAAC;IACD,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAC1C,IAAI,CAAC;QACJ,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,OAAO,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;YACrC,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,CAAC;YACvF,IAAI,YAAY,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,4DAA4D,IAAI,EAAE,CAAC,CAAC;YAC5G,OAAO,IAAI,YAAY,CAAC;QACzB,CAAC;QACD,SAAS,CAAC,MAAM,CAAC,CAAC;IACnB,CAAC;YAAS,CAAC;QACV,SAAS,CAAC,MAAM,CAAC,CAAC;IACnB,CAAC;IACD,IAAI,CAAC,WAAW,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAClD,MAAM,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;QACrD,IAAI,CAAC;YACJ,SAAS,CAAC,eAAe,CAAC,CAAC;QAC5B,CAAC;gBAAS,CAAC;YACV,SAAS,CAAC,eAAe,CAAC,CAAC;QAC5B,CAAC;IACF,CAAC;AAAA,CACD;AAED,SAAS,oBAAoB,CAAI,IAAY,EAAE,SAAkB,EAAK;IACrE,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC,GAAG,cAAc,cAAc,CAAC,CAAC;IACnE,IAAI,kBAAkB,GAAG,KAAK,CAAC;IAC/B,IAAI,CAAC;QACJ,QAAQ,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACzC,QAAQ,CAAC,IAAI,CAAC,uFAAuF,CAAC,CAAC;QACvG,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACjC,kBAAkB,GAAG,IAAI,CAAC;QAC1B,OAAO,SAAS,EAAE,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,yCAAyC,cAAc,KAAK,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1G,CAAC;YAAS,CAAC;QACV,IAAI,kBAAkB,EAAE,CAAC;YACxB,IAAI,CAAC;gBACJ,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC3B,CAAC;YAAC,MAAM,CAAC;gBACR,4DAA4D;YAC7D,CAAC;QACF,CAAC;QACD,QAAQ,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;AAAA,CACD;AAED,MAAM,UAAU,2BAA2B,CAAC,WAAmB,EAAU;IACxE,OAAO,GAAG,WAAW,2BAA2B,CAAC;AAAA,CACjD;AAED,MAAM,UAAU,0BAA0B,CAAC,IAAY,EAA2C;IACjG,OAAO,qBAAqB,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;AAAA,CAC3C;AAED,MAAM,UAAU,2BAA2B,CAC1C,IAAY,EACZ,KAAuC,EACT;IAC9B,OAAO,oBAAoB,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CACd,wBAAwB,KAAK,CAAC,OAAO,CAAC,WAAW,0DAA0D,CAC3G,CAAC;QACH,CAAC;QACD,IAAI,KAAK,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;YAC5B,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YACtB,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC;QACxB,CAAC;QACD,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QAChD,4BAA4B,CAAC,SAAS,EAAE,mCAAmC,CAAC,CAAC;QAC7E,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,oCAAoC,CAAC,CAAC;QACnE,gBAAgB,CAAC,KAAK,CAAC,SAAS,EAAE,mCAAmC,CAAC,CAAC;QACvE,gBAAgB,CAAC,KAAK,CAAC,WAAW,EAAE,qCAAqC,CAAC,CAAC;QAC3E,UAAU,CAAC,KAAK,CAAC,WAAW,EAAE,qCAAqC,CAAC,CAAC;QACrE,UAAU,CAAC,KAAK,CAAC,qBAAqB,EAAE,+CAA+C,EAAE,IAAI,CAAC,CAAC;QAC/F,UAAU,CAAC,KAAK,CAAC,YAAY,EAAE,sCAAsC,CAAC,CAAC;QACvE,4BAA4B,CAAC,KAAK,CAAC,cAAc,EAAE,wCAAwC,CAAC,CAAC;QAC7F,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI;YAAE,gBAAgB,CAAC,KAAK,CAAC,UAAU,EAAE,oCAAoC,CAAC,CAAC;QACxG,4BAA4B,CAAC,KAAK,CAAC,YAAY,EAAE,sCAAsC,CAAC,CAAC;QACzF,MAAM,MAAM,GAAgC;YAC3C,OAAO,EAAE,qBAAqB;YAC9B,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE,KAAK,CAAC,YAAY;YAC5B,SAAS;YACT,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,qBAAqB,EAAE,KAAK,CAAC,qBAAqB;YAClD,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,YAAY,EAAE,KAAK,CAAC,YAAY;SAChC,CAAC;QACF,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3B,OAAO,MAAM,CAAC;IAAA,CACd,CAAC,CAAC;AAAA,CACH;AAED,MAAM,UAAU,2BAA2B,CAC1C,IAAY,EACZ,KAAuC,EACT;IAC9B,OAAO,oBAAoB,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC9B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW,EAAE,CAAC;YACxG,MAAM,IAAI,KAAK,CAAC,sFAAsF,CAAC,CAAC;QACzG,CAAC;QACD,gBAAgB,CAAC,KAAK,CAAC,OAAO,EAAE,mCAAmC,CAAC,CAAC;QACrE,UAAU,CAAC,KAAK,CAAC,SAAS,EAAE,qCAAqC,CAAC,CAAC;QACnE,UAAU,CAAC,KAAK,CAAC,eAAe,EAAE,2CAA2C,CAAC,CAAC;QAC/E,gBAAgB,CAAC,KAAK,CAAC,aAAa,EAAE,yCAAyC,CAAC,CAAC;QACjF,4BAA4B,CAAC,KAAK,CAAC,eAAe,EAAE,2CAA2C,CAAC,CAAC;QACjG,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YACzB,IACC,OAAO,CAAC,WAAW,CAAC,IAAI,KAAK,SAAS;gBACtC,OAAO,CAAC,WAAW,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO;gBAC7C,OAAO,CAAC,WAAW,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS;gBACjD,OAAO,CAAC,WAAW,CAAC,eAAe,KAAK,KAAK,CAAC,eAAe;gBAC7D,OAAO,CAAC,WAAW,CAAC,aAAa,KAAK,KAAK,CAAC,aAAa;gBACzD,OAAO,CAAC,WAAW,CAAC,eAAe,KAAK,KAAK,CAAC,eAAe,EAC5D,CAAC;gBACF,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;YACrF,CAAC;YACD,OAAO,OAAO,CAAC,WAAW,CAAC;QAC5B,CAAC;QACD,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QAChD,4BAA4B,CAAC,SAAS,EAAE,qCAAqC,CAAC,CAAC;QAC/E,MAAM,MAAM,GAAgC;YAC3C,OAAO,EAAE,qBAAqB;YAC9B,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE,KAAK,CAAC,YAAY;YAC5B,SAAS;YACT,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,UAAU,EAAE,cAAc;SAC1B,CAAC;QACF,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3B,OAAO,MAAM,CAAC;IAAA,CACd,CAAC,CAAC;AAAA,CACH;AAED,MAAM,UAAU,4BAA4B,CAC3C,IAAY,EACZ,KAAwC,EACT;IAC/B,OAAO,oBAAoB,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC9B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW,EAAE,CAAC;YACxG,MAAM,IAAI,KAAK,CAAC,sFAAsF,CAAC,CAAC;QACzG,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,UAAU,GAAG,GAAG,IAAI,KAAK,CAAC,UAAU,GAAG,GAAG,EAAE,CAAC;YACjG,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACtE,CAAC;QACD,WAAW,CAAC,KAAK,CAAC,KAAK,EAAE,kCAAkC,CAAC,CAAC;QAC7D,IACC,CAAC,KAAK,CAAC,oBAAoB,KAAK,UAAU,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC;YACxE,CAAC,KAAK,CAAC,oBAAoB,KAAK,aAAa,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU,CAAC,EAC5E,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACtE,CAAC;QACD,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YACzB,IACC,OAAO,CAAC,WAAW,CAAC,IAAI,KAAK,UAAU;gBACvC,OAAO,CAAC,WAAW,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU;gBACnD,OAAO,CAAC,WAAW,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK;gBACzC,OAAO,CAAC,WAAW,CAAC,oBAAoB,KAAK,KAAK,CAAC,oBAAoB;gBACvE,OAAO,CAAC,WAAW,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAC1C,CAAC;gBACF,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;YACrF,CAAC;YACD,OAAO,OAAO,CAAC,WAAW,CAAC;QAC5B,CAAC;QACD,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QAChD,4BAA4B,CAAC,SAAS,EAAE,sCAAsC,CAAC,CAAC;QAChF,MAAM,MAAM,GAAiC;YAC5C,OAAO,EAAE,qBAAqB;YAC9B,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,KAAK,CAAC,YAAY;YAC5B,SAAS;YACT,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;YAChD,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,UAAU,EAAE,KAAK,CAAC,oBAAoB,KAAK,UAAU,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,iBAAiB;SACtG,CAAC;QACF,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3B,OAAO,MAAM,CAAC;IAAA,CACd,CAAC,CAAC;AAAA,CACH;AAED,MAAM,UAAU,iCAAiC,CAChD,IAAY,EACZ,WAAmB,EACnB,UAAU,GAA6C,YAAY,EACnE,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EACf;IACP,oBAAoB,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK,CAAC,OAAO;YAAE,OAAO;QAC3B,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,KAAK,WAAW,EAAE,CAAC;YAC/C,MAAM,IAAI,KAAK,CACd,2CAA2C,WAAW,0BAA0B,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAC3G,CAAC;QACH,CAAC;QACD,4BAA4B,CAAC,SAAS,EAAE,6CAA6C,CAAC,CAAC;QACvF,YAAY,CAAC,IAAI,EAAE;YAClB,OAAO,EAAE,qBAAqB;YAC9B,IAAI,EAAE,KAAK;YACX,QAAQ,EAAE,KAAK,CAAC,YAAY;YAC5B,SAAS;YACT,WAAW;YACX,UAAU;SACV,CAAC,CAAC;IAAA,CACH,CAAC,CAAC;AAAA,CACH","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { createHash } from \"node:crypto\";\nimport { closeSync, existsSync, fsyncSync, openSync, readFileSync, truncateSync, writeSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { DatabaseSync } from \"node:sqlite\";\n\nconst COMPACT_STATE_VERSION = 1;\nconst COMPACT_STATE_MAX_BYTES = 8 * 1024 * 1024;\nconst SHA256_PATTERN = /^[a-f0-9]{64}$/u;\nconst IDENTIFIER_MAX_CHARS = 4096;\nconst ERROR_MAX_CHARS = 1024 * 1024;\n\ninterface PiServerCompactStateBase {\n\tversion: typeof COMPACT_STATE_VERSION;\n\tsequence: number;\n\ttimestamp: number;\n}\n\nexport interface PiServerPendingCompactState extends PiServerCompactStateBase {\n\tkind: \"compact\";\n\tserverHash: string;\n\tsessionId: string;\n\toperationId: string;\n\trequestHash: string;\n\tbaseStaticContextHash: string;\n\tbaseTreeHash: string;\n\tbaseEntryCount: number;\n\tbaseLeafId: string | null;\n\tbaseRevision: number;\n\tobservation?: PiServerCompactObservationState;\n}\n\nexport interface PiServerAppliedCompactState extends PiServerCompactStateBase {\n\tkind: \"applied\";\n\toperationId: string;\n\trequestHash: string;\n\tentryId: string;\n\tentryHash: string;\n\tupdatedTreeHash: string;\n\tupdatedLeafId: string;\n\tupdatedRevision: number;\n\tresolution: \"tree_applied\";\n}\n\nexport interface PiServerTerminalCompactState extends PiServerCompactStateBase {\n\tkind: \"terminal\";\n\toperationId: string;\n\trequestHash: string;\n\thttpStatus: number;\n\terror: string;\n\toperationDisposition: \"terminal\" | \"not_started\";\n\tstatus: \"failed\" | \"rejected\";\n\tresolution: \"terminal_failed_observed\" | \"server_rejected\";\n}\n\nexport type PiServerCompactObservationState = PiServerAppliedCompactState | PiServerTerminalCompactState;\n\nexport type PiServerCompactAcknowledgementResolution =\n\t| \"server_ack\"\n\t| \"server_missing_after_tree_applied\"\n\t| \"server_missing_after_terminal_failure\"\n\t| \"server_rejected_not_started\";\n\ninterface PiServerAcknowledgedCompactState extends PiServerCompactStateBase {\n\tkind: \"ack\";\n\toperationId: string;\n\tresolution: PiServerCompactAcknowledgementResolution;\n}\n\ntype PiServerCompactStateRecord =\n\t| PiServerPendingCompactState\n\t| PiServerCompactObservationState\n\t| PiServerAcknowledgedCompactState;\n\ninterface ParsedPiServerCompactState {\n\tnextSequence: number;\n\tpending: PiServerPendingCompactState | undefined;\n}\n\nexport interface WritePiServerPendingCompactInput {\n\tserverHash: string;\n\tsessionId: string;\n\toperationId: string;\n\trequestHash: string;\n\tbaseStaticContextHash: string;\n\tbaseTreeHash: string;\n\tbaseEntryCount: number;\n\tbaseLeafId: string | null;\n\tbaseRevision: number;\n\ttimestamp?: number;\n}\n\nexport interface WritePiServerAppliedCompactInput {\n\toperationId: string;\n\trequestHash: string;\n\tentryId: string;\n\tentryHash: string;\n\tupdatedTreeHash: string;\n\tupdatedLeafId: string;\n\tupdatedRevision: number;\n\ttimestamp?: number;\n}\n\nexport interface WritePiServerTerminalCompactInput {\n\toperationId: string;\n\trequestHash: string;\n\thttpStatus: number;\n\terror: string;\n\toperationDisposition: \"terminal\" | \"not_started\";\n\tstatus: \"failed\" | \"rejected\";\n\ttimestamp?: number;\n}\n\nfunction sha256(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\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 assertIdentifier(value: unknown, path: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.length === 0 || value.length > IDENTIFIER_MAX_CHARS || value.includes(\"\\0\")) {\n\t\tthrow new Error(`${path} must be a non-empty string without NUL bytes`);\n\t}\n}\n\nfunction assertHash(value: unknown, path: string, allowEmpty = false): asserts value is string {\n\tif (\n\t\ttypeof value !== \"string\" ||\n\t\t(!allowEmpty && !SHA256_PATTERN.test(value)) ||\n\t\t(allowEmpty && value !== \"\" && !SHA256_PATTERN.test(value))\n\t) {\n\t\tthrow new Error(`${path} must be ${allowEmpty ? \"empty or \" : \"\"}a lowercase SHA-256 digest`);\n\t}\n}\n\nfunction assertError(value: unknown, path: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.length === 0 || value.length > ERROR_MAX_CHARS || value.includes(\"\\0\")) {\n\t\tthrow new Error(`${path} must be a non-empty string without NUL bytes`);\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 parsePayload(payload: string, lineNumber: number): PiServerCompactStateRecord {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(payload) as unknown;\n\t} catch (error) {\n\t\tthrow new Error(`pi-server compact state payload is invalid JSON at line ${lineNumber}`, { cause: error });\n\t}\n\tassertObject(value, `pi-server compact state line ${lineNumber}`);\n\tif (value.version !== COMPACT_STATE_VERSION) {\n\t\tthrow new Error(`Unsupported pi-server compact state version at line ${lineNumber}`);\n\t}\n\tassertNonNegativeSafeInteger(value.sequence, `pi-server compact state line ${lineNumber}.sequence`);\n\tassertNonNegativeSafeInteger(value.timestamp, `pi-server compact state line ${lineNumber}.timestamp`);\n\tif (value.kind === \"ack\") {\n\t\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\t\tif (\n\t\t\tvalue.resolution !== \"server_ack\" &&\n\t\t\tvalue.resolution !== \"server_missing_after_tree_applied\" &&\n\t\t\tvalue.resolution !== \"server_missing_after_terminal_failure\" &&\n\t\t\tvalue.resolution !== \"server_rejected_not_started\"\n\t\t) {\n\t\t\tthrow new Error(`pi-server compact state acknowledgement resolution is invalid at line ${lineNumber}`);\n\t\t}\n\t\treturn {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"ack\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\toperationId: value.operationId,\n\t\t\tresolution: value.resolution,\n\t\t};\n\t}\n\tif (value.kind === \"applied\") {\n\t\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\t\tassertHash(value.requestHash, `pi-server compact state line ${lineNumber}.requestHash`);\n\t\tassertIdentifier(value.entryId, `pi-server compact state line ${lineNumber}.entryId`);\n\t\tassertHash(value.entryHash, `pi-server compact state line ${lineNumber}.entryHash`);\n\t\tassertHash(value.updatedTreeHash, `pi-server compact state line ${lineNumber}.updatedTreeHash`);\n\t\tassertIdentifier(value.updatedLeafId, `pi-server compact state line ${lineNumber}.updatedLeafId`);\n\t\tassertNonNegativeSafeInteger(value.updatedRevision, `pi-server compact state line ${lineNumber}.updatedRevision`);\n\t\tif (value.resolution !== \"tree_applied\") {\n\t\t\tthrow new Error(`pi-server compact applied resolution is invalid at line ${lineNumber}`);\n\t\t}\n\t\treturn {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"applied\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\toperationId: value.operationId,\n\t\t\trequestHash: value.requestHash,\n\t\t\tentryId: value.entryId,\n\t\t\tentryHash: value.entryHash,\n\t\t\tupdatedTreeHash: value.updatedTreeHash,\n\t\t\tupdatedLeafId: value.updatedLeafId,\n\t\t\tupdatedRevision: value.updatedRevision,\n\t\t\tresolution: value.resolution,\n\t\t};\n\t}\n\tif (value.kind === \"terminal\") {\n\t\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\t\tassertHash(value.requestHash, `pi-server compact state line ${lineNumber}.requestHash`);\n\t\tif (\n\t\t\t!Number.isSafeInteger(value.httpStatus) ||\n\t\t\t(value.httpStatus as number) < 100 ||\n\t\t\t(value.httpStatus as number) > 599\n\t\t) {\n\t\t\tthrow new Error(`pi-server compact terminal HTTP status is invalid at line ${lineNumber}`);\n\t\t}\n\t\tassertError(value.error, `pi-server compact state line ${lineNumber}.error`);\n\t\tif (\n\t\t\t(value.operationDisposition !== \"terminal\" && value.operationDisposition !== \"not_started\") ||\n\t\t\t(value.status !== \"failed\" && value.status !== \"rejected\") ||\n\t\t\t(value.operationDisposition === \"terminal\" && value.status !== \"failed\") ||\n\t\t\t(value.operationDisposition === \"not_started\" && value.status !== \"rejected\")\n\t\t) {\n\t\t\tthrow new Error(`pi-server compact terminal disposition is invalid at line ${lineNumber}`);\n\t\t}\n\t\tconst expectedResolution =\n\t\t\tvalue.operationDisposition === \"terminal\" ? \"terminal_failed_observed\" : \"server_rejected\";\n\t\tif (value.resolution !== expectedResolution) {\n\t\t\tthrow new Error(`pi-server compact terminal resolution is invalid at line ${lineNumber}`);\n\t\t}\n\t\treturn {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"terminal\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\toperationId: value.operationId,\n\t\t\trequestHash: value.requestHash,\n\t\t\thttpStatus: value.httpStatus as number,\n\t\t\terror: value.error,\n\t\t\toperationDisposition: value.operationDisposition,\n\t\t\tstatus: value.status,\n\t\t\tresolution: expectedResolution,\n\t\t};\n\t}\n\tif (value.kind !== \"compact\") {\n\t\tthrow new Error(`Unsupported pi-server compact state record kind at line ${lineNumber}`);\n\t}\n\tassertHash(value.serverHash, `pi-server compact state line ${lineNumber}.serverHash`);\n\tassertIdentifier(value.sessionId, `pi-server compact state line ${lineNumber}.sessionId`);\n\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\tassertHash(value.requestHash, `pi-server compact state line ${lineNumber}.requestHash`);\n\tassertHash(value.baseStaticContextHash, `pi-server compact state line ${lineNumber}.baseStaticContextHash`, true);\n\tassertHash(value.baseTreeHash, `pi-server compact state line ${lineNumber}.baseTreeHash`);\n\tassertNonNegativeSafeInteger(value.baseEntryCount, `pi-server compact state line ${lineNumber}.baseEntryCount`);\n\tif (value.baseLeafId !== null) {\n\t\tassertIdentifier(value.baseLeafId, `pi-server compact state line ${lineNumber}.baseLeafId`);\n\t}\n\tassertNonNegativeSafeInteger(value.baseRevision, `pi-server compact state line ${lineNumber}.baseRevision`);\n\treturn {\n\t\tversion: COMPACT_STATE_VERSION,\n\t\tkind: \"compact\",\n\t\tsequence: value.sequence,\n\t\ttimestamp: value.timestamp,\n\t\tserverHash: value.serverHash,\n\t\tsessionId: value.sessionId,\n\t\toperationId: value.operationId,\n\t\trequestHash: value.requestHash,\n\t\tbaseStaticContextHash: value.baseStaticContextHash,\n\t\tbaseTreeHash: value.baseTreeHash,\n\t\tbaseEntryCount: value.baseEntryCount,\n\t\tbaseLeafId: value.baseLeafId,\n\t\tbaseRevision: value.baseRevision,\n\t};\n}\n\nfunction parseCompactStateFile(path: string): ParsedPiServerCompactState {\n\tif (!existsSync(path)) return { nextSequence: 0, pending: undefined };\n\tconst encoded = readFileSync(path);\n\tif (encoded.byteLength > COMPACT_STATE_MAX_BYTES) {\n\t\tthrow new Error(`pi-server compact state exceeds ${COMPACT_STATE_MAX_BYTES} bytes: ${path}`);\n\t}\n\tconst content = encoded.toString(\"utf-8\");\n\tconst lines = content.split(\"\\n\");\n\tif (!content.endsWith(\"\\n\")) lines.pop();\n\tlet expectedSequence = 0;\n\tlet pending: PiServerPendingCompactState | undefined;\n\tfor (const [index, line] of lines.entries()) {\n\t\tif (!line) continue;\n\t\tlet envelope: unknown;\n\t\ttry {\n\t\t\tenvelope = JSON.parse(line) as unknown;\n\t\t} catch (error) {\n\t\t\tthrow new Error(`pi-server compact state envelope is invalid JSON at line ${index + 1}`, {\n\t\t\t\tcause: error,\n\t\t\t});\n\t\t}\n\t\tassertObject(envelope, `pi-server compact state envelope line ${index + 1}`);\n\t\tif (typeof envelope.payload !== \"string\") {\n\t\t\tthrow new Error(`pi-server compact state envelope payload must be a string at line ${index + 1}`);\n\t\t}\n\t\tassertHash(envelope.sha256, `pi-server compact state envelope line ${index + 1}.sha256`);\n\t\tif (sha256(envelope.payload) !== envelope.sha256) {\n\t\t\tthrow new Error(`pi-server compact state checksum mismatch at line ${index + 1}`);\n\t\t}\n\t\tconst record = parsePayload(envelope.payload, index + 1);\n\t\tif (record.sequence !== expectedSequence) {\n\t\t\tthrow new Error(\n\t\t\t\t`pi-server compact state sequence mismatch at line ${index + 1}: expected ${expectedSequence}, received ${record.sequence}`,\n\t\t\t);\n\t\t}\n\t\texpectedSequence++;\n\t\tif (record.kind === \"compact\") {\n\t\t\tif (pending) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`pi-server compact state contains a new operation before ${pending.operationId} was acknowledged`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tpending = record;\n\t\t} else if (record.kind === \"applied\" || record.kind === \"terminal\") {\n\t\t\tif (\n\t\t\t\t!pending ||\n\t\t\t\tpending.operationId !== record.operationId ||\n\t\t\t\tpending.requestHash !== record.requestHash ||\n\t\t\t\tpending.observation\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`pi-server compact state observation does not match the pending operation at line ${index + 1}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tpending = { ...pending, observation: record };\n\t\t} else {\n\t\t\tif (!pending || pending.operationId !== record.operationId) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`pi-server compact state acknowledgement does not match the pending operation at line ${index + 1}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tpending = undefined;\n\t\t}\n\t}\n\treturn { nextSequence: expectedSequence, pending };\n}\n\nfunction appendRecord(path: string, record: PiServerCompactStateRecord): void {\n\tconst payload = JSON.stringify(record);\n\tconst encoded = Buffer.from(`${JSON.stringify({ payload, sha256: sha256(payload) })}\\n`, \"utf-8\");\n\tconst existing = existsSync(path) ? readFileSync(path) : Buffer.alloc(0);\n\tconst validBytes = existing.lastIndexOf(0x0a) + 1;\n\tif (validBytes !== existing.byteLength) truncateSync(path, validBytes);\n\tif (validBytes + encoded.byteLength > COMPACT_STATE_MAX_BYTES) {\n\t\tthrow new Error(`pi-server compact state would exceed ${COMPACT_STATE_MAX_BYTES} bytes: ${path}`);\n\t}\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) throw new Error(`Failed to make progress writing pi-server compact state: ${path}`);\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 withCompactStateLock<T>(path: string, operation: () => T): T {\n\tconst normalizedPath = resolve(path);\n\tconst database = new DatabaseSync(`${normalizedPath}.lock.sqlite`);\n\tlet transactionStarted = false;\n\ttry {\n\t\tdatabase.exec(\"PRAGMA busy_timeout = 0\");\n\t\tdatabase.exec(\"CREATE TABLE IF NOT EXISTS compact_state_lock (id INTEGER PRIMARY KEY CHECK (id = 1))\");\n\t\tdatabase.exec(\"BEGIN IMMEDIATE\");\n\t\ttransactionStarted = true;\n\t\treturn operation();\n\t} catch (error) {\n\t\tconst details = error instanceof Error ? error.message : String(error);\n\t\tthrow new Error(`Cannot update pi-server compact state ${normalizedPath}: ${details}`, { cause: error });\n\t} finally {\n\t\tif (transactionStarted) {\n\t\t\ttry {\n\t\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t\t} catch {\n\t\t\t\t// Closing the connection is the authoritative lock release.\n\t\t\t}\n\t\t}\n\t\tdatabase.close();\n\t}\n}\n\nexport function getPiServerCompactStatePath(sessionFile: string): string {\n\treturn `${sessionFile}.pi-server-compacts.jsonl`;\n}\n\nexport function readPiServerPendingCompact(path: string): PiServerPendingCompactState | undefined {\n\treturn parseCompactStateFile(path).pending;\n}\n\nexport function writePiServerPendingCompact(\n\tpath: string,\n\tinput: WritePiServerPendingCompactInput,\n): PiServerPendingCompactState {\n\treturn withCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tif (state.pending) {\n\t\t\tthrow new Error(\n\t\t\t\t`pi-server compaction ${state.pending.operationId} must be acknowledged before starting another compaction`,\n\t\t\t);\n\t\t}\n\t\tif (state.nextSequence > 0) {\n\t\t\ttruncateSync(path, 0);\n\t\t\tstate.nextSequence = 0;\n\t\t}\n\t\tconst timestamp = input.timestamp ?? Date.now();\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact state timestamp\");\n\t\tassertHash(input.serverHash, \"pi-server compact state serverHash\");\n\t\tassertIdentifier(input.sessionId, \"pi-server compact state sessionId\");\n\t\tassertIdentifier(input.operationId, \"pi-server compact state operationId\");\n\t\tassertHash(input.requestHash, \"pi-server compact state requestHash\");\n\t\tassertHash(input.baseStaticContextHash, \"pi-server compact state baseStaticContextHash\", true);\n\t\tassertHash(input.baseTreeHash, \"pi-server compact state baseTreeHash\");\n\t\tassertNonNegativeSafeInteger(input.baseEntryCount, \"pi-server compact state baseEntryCount\");\n\t\tif (input.baseLeafId !== null) assertIdentifier(input.baseLeafId, \"pi-server compact state baseLeafId\");\n\t\tassertNonNegativeSafeInteger(input.baseRevision, \"pi-server compact state baseRevision\");\n\t\tconst record: PiServerPendingCompactState = {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"compact\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\tserverHash: input.serverHash,\n\t\t\tsessionId: input.sessionId,\n\t\t\toperationId: input.operationId,\n\t\t\trequestHash: input.requestHash,\n\t\t\tbaseStaticContextHash: input.baseStaticContextHash,\n\t\t\tbaseTreeHash: input.baseTreeHash,\n\t\t\tbaseEntryCount: input.baseEntryCount,\n\t\t\tbaseLeafId: input.baseLeafId,\n\t\t\tbaseRevision: input.baseRevision,\n\t\t};\n\t\tappendRecord(path, record);\n\t\treturn record;\n\t});\n}\n\nexport function writePiServerAppliedCompact(\n\tpath: string,\n\tinput: WritePiServerAppliedCompactInput,\n): PiServerAppliedCompactState {\n\treturn withCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tconst pending = state.pending;\n\t\tif (!pending || pending.operationId !== input.operationId || pending.requestHash !== input.requestHash) {\n\t\t\tthrow new Error(\"Cannot record an applied pi-server compaction without its matching pending operation\");\n\t\t}\n\t\tassertIdentifier(input.entryId, \"pi-server compact applied entryId\");\n\t\tassertHash(input.entryHash, \"pi-server compact applied entryHash\");\n\t\tassertHash(input.updatedTreeHash, \"pi-server compact applied updatedTreeHash\");\n\t\tassertIdentifier(input.updatedLeafId, \"pi-server compact applied updatedLeafId\");\n\t\tassertNonNegativeSafeInteger(input.updatedRevision, \"pi-server compact applied updatedRevision\");\n\t\tif (pending.observation) {\n\t\t\tif (\n\t\t\t\tpending.observation.kind !== \"applied\" ||\n\t\t\t\tpending.observation.entryId !== input.entryId ||\n\t\t\t\tpending.observation.entryHash !== input.entryHash ||\n\t\t\t\tpending.observation.updatedTreeHash !== input.updatedTreeHash ||\n\t\t\t\tpending.observation.updatedLeafId !== input.updatedLeafId ||\n\t\t\t\tpending.observation.updatedRevision !== input.updatedRevision\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Pi-server compaction already has a divergent durable observation\");\n\t\t\t}\n\t\t\treturn pending.observation;\n\t\t}\n\t\tconst timestamp = input.timestamp ?? Date.now();\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact applied timestamp\");\n\t\tconst record: PiServerAppliedCompactState = {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"applied\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\toperationId: input.operationId,\n\t\t\trequestHash: input.requestHash,\n\t\t\tentryId: input.entryId,\n\t\t\tentryHash: input.entryHash,\n\t\t\tupdatedTreeHash: input.updatedTreeHash,\n\t\t\tupdatedLeafId: input.updatedLeafId,\n\t\t\tupdatedRevision: input.updatedRevision,\n\t\t\tresolution: \"tree_applied\",\n\t\t};\n\t\tappendRecord(path, record);\n\t\treturn record;\n\t});\n}\n\nexport function writePiServerTerminalCompact(\n\tpath: string,\n\tinput: WritePiServerTerminalCompactInput,\n): PiServerTerminalCompactState {\n\treturn withCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tconst pending = state.pending;\n\t\tif (!pending || pending.operationId !== input.operationId || pending.requestHash !== input.requestHash) {\n\t\t\tthrow new Error(\"Cannot record a terminal pi-server compaction without its matching pending operation\");\n\t\t}\n\t\tif (!Number.isSafeInteger(input.httpStatus) || input.httpStatus < 100 || input.httpStatus > 599) {\n\t\t\tthrow new Error(\"pi-server compact terminal HTTP status is invalid\");\n\t\t}\n\t\tassertError(input.error, \"pi-server compact terminal error\");\n\t\tif (\n\t\t\t(input.operationDisposition === \"terminal\" && input.status !== \"failed\") ||\n\t\t\t(input.operationDisposition === \"not_started\" && input.status !== \"rejected\")\n\t\t) {\n\t\t\tthrow new Error(\"pi-server compact terminal disposition is invalid\");\n\t\t}\n\t\tif (pending.observation) {\n\t\t\tif (\n\t\t\t\tpending.observation.kind !== \"terminal\" ||\n\t\t\t\tpending.observation.httpStatus !== input.httpStatus ||\n\t\t\t\tpending.observation.error !== input.error ||\n\t\t\t\tpending.observation.operationDisposition !== input.operationDisposition ||\n\t\t\t\tpending.observation.status !== input.status\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Pi-server compaction already has a divergent durable observation\");\n\t\t\t}\n\t\t\treturn pending.observation;\n\t\t}\n\t\tconst timestamp = input.timestamp ?? Date.now();\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact terminal timestamp\");\n\t\tconst record: PiServerTerminalCompactState = {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"terminal\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\toperationId: input.operationId,\n\t\t\trequestHash: input.requestHash,\n\t\t\thttpStatus: input.httpStatus,\n\t\t\terror: input.error,\n\t\t\toperationDisposition: input.operationDisposition,\n\t\t\tstatus: input.status,\n\t\t\tresolution: input.operationDisposition === \"terminal\" ? \"terminal_failed_observed\" : \"server_rejected\",\n\t\t};\n\t\tappendRecord(path, record);\n\t\treturn record;\n\t});\n}\n\nexport function acknowledgePiServerPendingCompact(\n\tpath: string,\n\toperationId: string,\n\tresolution: PiServerCompactAcknowledgementResolution = \"server_ack\",\n\ttimestamp = Date.now(),\n): void {\n\twithCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tif (!state.pending) return;\n\t\tif (state.pending.operationId !== operationId) {\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot acknowledge pi-server compaction ${operationId}; pending operation is ${state.pending.operationId}`,\n\t\t\t);\n\t\t}\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact acknowledgement timestamp\");\n\t\tappendRecord(path, {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"ack\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\toperationId,\n\t\t\tresolution,\n\t\t});\n\t});\n}\n"]}
1
+ {"version":3,"file":"pi-server-compact-state.js","sourceRoot":"","sources":["../../src/core/pi-server-compact-state.ts"],"names":[],"mappings":"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","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { createHash } from \"node:crypto\";\nimport { closeSync, existsSync, fsyncSync, openSync, readFileSync, truncateSync, writeSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { DatabaseSync } from \"node:sqlite\";\n\nconst COMPACT_STATE_VERSION = 1;\nconst COMPACT_STATE_MAX_BYTES = 8 * 1024 * 1024;\nconst SHA256_PATTERN = /^[a-f0-9]{64}$/u;\nconst IDENTIFIER_MAX_CHARS = 4096;\nconst ERROR_MAX_CHARS = 1024 * 1024;\n\ninterface PiServerCompactStateBase {\n\tversion: typeof COMPACT_STATE_VERSION;\n\tsequence: number;\n\ttimestamp: number;\n}\n\nexport interface PiServerPendingCompactState extends PiServerCompactStateBase {\n\tkind: \"compact\";\n\tserverHash: string;\n\tsessionId: string;\n\toperationId: string;\n\trequestHash: string;\n\tbaseStaticContextHash: string;\n\tbaseTreeHash: string;\n\tbaseEntryCount: number;\n\tbaseLeafId: string | null;\n\tbaseRevision: number;\n\tobservation?: PiServerCompactObservationState;\n}\n\nexport interface PiServerAppliedCompactState extends PiServerCompactStateBase {\n\tkind: \"applied\";\n\toperationId: string;\n\trequestHash: string;\n\tentryId: string;\n\tentryHash: string;\n\tupdatedTreeHash: string;\n\tupdatedLeafId: string;\n\tupdatedRevision: number;\n\tresolution: \"tree_applied\";\n}\n\nexport interface PiServerTerminalCompactState extends PiServerCompactStateBase {\n\tkind: \"terminal\";\n\toperationId: string;\n\trequestHash: string;\n\thttpStatus: number;\n\terror: string;\n\toperationDisposition: \"terminal\" | \"not_started\";\n\tstatus: \"failed\" | \"rejected\";\n\tresolution: \"terminal_failed_observed\" | \"server_rejected\";\n}\n\nexport type PiServerCompactObservationState = PiServerAppliedCompactState | PiServerTerminalCompactState;\n\nexport type PiServerCompactAcknowledgementResolution =\n\t| \"server_ack\"\n\t| \"server_missing_after_tree_applied\"\n\t| \"server_missing_after_terminal_failure\"\n\t| \"server_rejected_not_started\"\n\t| \"server_authoritative_missing\";\n\ninterface PiServerAcknowledgedCompactState extends PiServerCompactStateBase {\n\tkind: \"ack\";\n\toperationId: string;\n\tresolution: PiServerCompactAcknowledgementResolution;\n}\n\ntype PiServerCompactStateRecord =\n\t| PiServerPendingCompactState\n\t| PiServerCompactObservationState\n\t| PiServerAcknowledgedCompactState;\n\ninterface ParsedPiServerCompactState {\n\tnextSequence: number;\n\tpending: PiServerPendingCompactState | undefined;\n}\n\nexport interface WritePiServerPendingCompactInput {\n\tserverHash: string;\n\tsessionId: string;\n\toperationId: string;\n\trequestHash: string;\n\tbaseStaticContextHash: string;\n\tbaseTreeHash: string;\n\tbaseEntryCount: number;\n\tbaseLeafId: string | null;\n\tbaseRevision: number;\n\ttimestamp?: number;\n}\n\nexport interface WritePiServerAppliedCompactInput {\n\toperationId: string;\n\trequestHash: string;\n\tentryId: string;\n\tentryHash: string;\n\tupdatedTreeHash: string;\n\tupdatedLeafId: string;\n\tupdatedRevision: number;\n\ttimestamp?: number;\n}\n\nexport interface WritePiServerTerminalCompactInput {\n\toperationId: string;\n\trequestHash: string;\n\thttpStatus: number;\n\terror: string;\n\toperationDisposition: \"terminal\" | \"not_started\";\n\tstatus: \"failed\" | \"rejected\";\n\ttimestamp?: number;\n}\n\nfunction sha256(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\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 assertIdentifier(value: unknown, path: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.length === 0 || value.length > IDENTIFIER_MAX_CHARS || value.includes(\"\\0\")) {\n\t\tthrow new Error(`${path} must be a non-empty string without NUL bytes`);\n\t}\n}\n\nfunction assertHash(value: unknown, path: string, allowEmpty = false): asserts value is string {\n\tif (\n\t\ttypeof value !== \"string\" ||\n\t\t(!allowEmpty && !SHA256_PATTERN.test(value)) ||\n\t\t(allowEmpty && value !== \"\" && !SHA256_PATTERN.test(value))\n\t) {\n\t\tthrow new Error(`${path} must be ${allowEmpty ? \"empty or \" : \"\"}a lowercase SHA-256 digest`);\n\t}\n}\n\nfunction assertError(value: unknown, path: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.length === 0 || value.length > ERROR_MAX_CHARS || value.includes(\"\\0\")) {\n\t\tthrow new Error(`${path} must be a non-empty string without NUL bytes`);\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 parsePayload(payload: string, lineNumber: number): PiServerCompactStateRecord {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(payload) as unknown;\n\t} catch (error) {\n\t\tthrow new Error(`pi-server compact state payload is invalid JSON at line ${lineNumber}`, { cause: error });\n\t}\n\tassertObject(value, `pi-server compact state line ${lineNumber}`);\n\tif (value.version !== COMPACT_STATE_VERSION) {\n\t\tthrow new Error(`Unsupported pi-server compact state version at line ${lineNumber}`);\n\t}\n\tassertNonNegativeSafeInteger(value.sequence, `pi-server compact state line ${lineNumber}.sequence`);\n\tassertNonNegativeSafeInteger(value.timestamp, `pi-server compact state line ${lineNumber}.timestamp`);\n\tif (value.kind === \"ack\") {\n\t\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\t\tif (\n\t\t\tvalue.resolution !== \"server_ack\" &&\n\t\t\tvalue.resolution !== \"server_missing_after_tree_applied\" &&\n\t\t\tvalue.resolution !== \"server_missing_after_terminal_failure\" &&\n\t\t\tvalue.resolution !== \"server_rejected_not_started\" &&\n\t\t\tvalue.resolution !== \"server_authoritative_missing\"\n\t\t) {\n\t\t\tthrow new Error(`pi-server compact state acknowledgement resolution is invalid at line ${lineNumber}`);\n\t\t}\n\t\treturn {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"ack\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\toperationId: value.operationId,\n\t\t\tresolution: value.resolution,\n\t\t};\n\t}\n\tif (value.kind === \"applied\") {\n\t\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\t\tassertHash(value.requestHash, `pi-server compact state line ${lineNumber}.requestHash`);\n\t\tassertIdentifier(value.entryId, `pi-server compact state line ${lineNumber}.entryId`);\n\t\tassertHash(value.entryHash, `pi-server compact state line ${lineNumber}.entryHash`);\n\t\tassertHash(value.updatedTreeHash, `pi-server compact state line ${lineNumber}.updatedTreeHash`);\n\t\tassertIdentifier(value.updatedLeafId, `pi-server compact state line ${lineNumber}.updatedLeafId`);\n\t\tassertNonNegativeSafeInteger(value.updatedRevision, `pi-server compact state line ${lineNumber}.updatedRevision`);\n\t\tif (value.resolution !== \"tree_applied\") {\n\t\t\tthrow new Error(`pi-server compact applied resolution is invalid at line ${lineNumber}`);\n\t\t}\n\t\treturn {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"applied\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\toperationId: value.operationId,\n\t\t\trequestHash: value.requestHash,\n\t\t\tentryId: value.entryId,\n\t\t\tentryHash: value.entryHash,\n\t\t\tupdatedTreeHash: value.updatedTreeHash,\n\t\t\tupdatedLeafId: value.updatedLeafId,\n\t\t\tupdatedRevision: value.updatedRevision,\n\t\t\tresolution: value.resolution,\n\t\t};\n\t}\n\tif (value.kind === \"terminal\") {\n\t\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\t\tassertHash(value.requestHash, `pi-server compact state line ${lineNumber}.requestHash`);\n\t\tif (\n\t\t\t!Number.isSafeInteger(value.httpStatus) ||\n\t\t\t(value.httpStatus as number) < 100 ||\n\t\t\t(value.httpStatus as number) > 599\n\t\t) {\n\t\t\tthrow new Error(`pi-server compact terminal HTTP status is invalid at line ${lineNumber}`);\n\t\t}\n\t\tassertError(value.error, `pi-server compact state line ${lineNumber}.error`);\n\t\tif (\n\t\t\t(value.operationDisposition !== \"terminal\" && value.operationDisposition !== \"not_started\") ||\n\t\t\t(value.status !== \"failed\" && value.status !== \"rejected\") ||\n\t\t\t(value.operationDisposition === \"terminal\" && value.status !== \"failed\") ||\n\t\t\t(value.operationDisposition === \"not_started\" && value.status !== \"rejected\")\n\t\t) {\n\t\t\tthrow new Error(`pi-server compact terminal disposition is invalid at line ${lineNumber}`);\n\t\t}\n\t\tconst expectedResolution =\n\t\t\tvalue.operationDisposition === \"terminal\" ? \"terminal_failed_observed\" : \"server_rejected\";\n\t\tif (value.resolution !== expectedResolution) {\n\t\t\tthrow new Error(`pi-server compact terminal resolution is invalid at line ${lineNumber}`);\n\t\t}\n\t\treturn {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"terminal\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\toperationId: value.operationId,\n\t\t\trequestHash: value.requestHash,\n\t\t\thttpStatus: value.httpStatus as number,\n\t\t\terror: value.error,\n\t\t\toperationDisposition: value.operationDisposition,\n\t\t\tstatus: value.status,\n\t\t\tresolution: expectedResolution,\n\t\t};\n\t}\n\tif (value.kind !== \"compact\") {\n\t\tthrow new Error(`Unsupported pi-server compact state record kind at line ${lineNumber}`);\n\t}\n\tassertHash(value.serverHash, `pi-server compact state line ${lineNumber}.serverHash`);\n\tassertIdentifier(value.sessionId, `pi-server compact state line ${lineNumber}.sessionId`);\n\tassertIdentifier(value.operationId, `pi-server compact state line ${lineNumber}.operationId`);\n\tassertHash(value.requestHash, `pi-server compact state line ${lineNumber}.requestHash`);\n\tassertHash(value.baseStaticContextHash, `pi-server compact state line ${lineNumber}.baseStaticContextHash`, true);\n\tassertHash(value.baseTreeHash, `pi-server compact state line ${lineNumber}.baseTreeHash`);\n\tassertNonNegativeSafeInteger(value.baseEntryCount, `pi-server compact state line ${lineNumber}.baseEntryCount`);\n\tif (value.baseLeafId !== null) {\n\t\tassertIdentifier(value.baseLeafId, `pi-server compact state line ${lineNumber}.baseLeafId`);\n\t}\n\tassertNonNegativeSafeInteger(value.baseRevision, `pi-server compact state line ${lineNumber}.baseRevision`);\n\treturn {\n\t\tversion: COMPACT_STATE_VERSION,\n\t\tkind: \"compact\",\n\t\tsequence: value.sequence,\n\t\ttimestamp: value.timestamp,\n\t\tserverHash: value.serverHash,\n\t\tsessionId: value.sessionId,\n\t\toperationId: value.operationId,\n\t\trequestHash: value.requestHash,\n\t\tbaseStaticContextHash: value.baseStaticContextHash,\n\t\tbaseTreeHash: value.baseTreeHash,\n\t\tbaseEntryCount: value.baseEntryCount,\n\t\tbaseLeafId: value.baseLeafId,\n\t\tbaseRevision: value.baseRevision,\n\t};\n}\n\nfunction parseCompactStateFile(path: string): ParsedPiServerCompactState {\n\tif (!existsSync(path)) return { nextSequence: 0, pending: undefined };\n\tconst encoded = readFileSync(path);\n\tif (encoded.byteLength > COMPACT_STATE_MAX_BYTES) {\n\t\tthrow new Error(`pi-server compact state exceeds ${COMPACT_STATE_MAX_BYTES} bytes: ${path}`);\n\t}\n\tconst content = encoded.toString(\"utf-8\");\n\tconst lines = content.split(\"\\n\");\n\tif (!content.endsWith(\"\\n\")) lines.pop();\n\tlet expectedSequence = 0;\n\tlet pending: PiServerPendingCompactState | undefined;\n\tfor (const [index, line] of lines.entries()) {\n\t\tif (!line) continue;\n\t\tlet envelope: unknown;\n\t\ttry {\n\t\t\tenvelope = JSON.parse(line) as unknown;\n\t\t} catch (error) {\n\t\t\tthrow new Error(`pi-server compact state envelope is invalid JSON at line ${index + 1}`, {\n\t\t\t\tcause: error,\n\t\t\t});\n\t\t}\n\t\tassertObject(envelope, `pi-server compact state envelope line ${index + 1}`);\n\t\tif (typeof envelope.payload !== \"string\") {\n\t\t\tthrow new Error(`pi-server compact state envelope payload must be a string at line ${index + 1}`);\n\t\t}\n\t\tassertHash(envelope.sha256, `pi-server compact state envelope line ${index + 1}.sha256`);\n\t\tif (sha256(envelope.payload) !== envelope.sha256) {\n\t\t\tthrow new Error(`pi-server compact state checksum mismatch at line ${index + 1}`);\n\t\t}\n\t\tconst record = parsePayload(envelope.payload, index + 1);\n\t\tif (record.sequence !== expectedSequence) {\n\t\t\tthrow new Error(\n\t\t\t\t`pi-server compact state sequence mismatch at line ${index + 1}: expected ${expectedSequence}, received ${record.sequence}`,\n\t\t\t);\n\t\t}\n\t\texpectedSequence++;\n\t\tif (record.kind === \"compact\") {\n\t\t\tif (pending) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`pi-server compact state contains a new operation before ${pending.operationId} was acknowledged`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tpending = record;\n\t\t} else if (record.kind === \"applied\" || record.kind === \"terminal\") {\n\t\t\tif (\n\t\t\t\t!pending ||\n\t\t\t\tpending.operationId !== record.operationId ||\n\t\t\t\tpending.requestHash !== record.requestHash ||\n\t\t\t\tpending.observation\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`pi-server compact state observation does not match the pending operation at line ${index + 1}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tpending = { ...pending, observation: record };\n\t\t} else {\n\t\t\tif (!pending || pending.operationId !== record.operationId) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`pi-server compact state acknowledgement does not match the pending operation at line ${index + 1}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tpending = undefined;\n\t\t}\n\t}\n\treturn { nextSequence: expectedSequence, pending };\n}\n\nfunction appendRecord(path: string, record: PiServerCompactStateRecord): void {\n\tconst payload = JSON.stringify(record);\n\tconst encoded = Buffer.from(`${JSON.stringify({ payload, sha256: sha256(payload) })}\\n`, \"utf-8\");\n\tconst existing = existsSync(path) ? readFileSync(path) : Buffer.alloc(0);\n\tconst validBytes = existing.lastIndexOf(0x0a) + 1;\n\tif (validBytes !== existing.byteLength) truncateSync(path, validBytes);\n\tif (validBytes + encoded.byteLength > COMPACT_STATE_MAX_BYTES) {\n\t\tthrow new Error(`pi-server compact state would exceed ${COMPACT_STATE_MAX_BYTES} bytes: ${path}`);\n\t}\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) throw new Error(`Failed to make progress writing pi-server compact state: ${path}`);\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 withCompactStateLock<T>(path: string, operation: () => T): T {\n\tconst normalizedPath = resolve(path);\n\tconst database = new DatabaseSync(`${normalizedPath}.lock.sqlite`);\n\tlet transactionStarted = false;\n\ttry {\n\t\tdatabase.exec(\"PRAGMA busy_timeout = 0\");\n\t\tdatabase.exec(\"CREATE TABLE IF NOT EXISTS compact_state_lock (id INTEGER PRIMARY KEY CHECK (id = 1))\");\n\t\tdatabase.exec(\"BEGIN IMMEDIATE\");\n\t\ttransactionStarted = true;\n\t\treturn operation();\n\t} catch (error) {\n\t\tconst details = error instanceof Error ? error.message : String(error);\n\t\tthrow new Error(`Cannot update pi-server compact state ${normalizedPath}: ${details}`, { cause: error });\n\t} finally {\n\t\tif (transactionStarted) {\n\t\t\ttry {\n\t\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t\t} catch {\n\t\t\t\t// Closing the connection is the authoritative lock release.\n\t\t\t}\n\t\t}\n\t\tdatabase.close();\n\t}\n}\n\nexport function getPiServerCompactStatePath(sessionFile: string): string {\n\treturn `${sessionFile}.pi-server-compacts.jsonl`;\n}\n\nexport function readPiServerPendingCompact(path: string): PiServerPendingCompactState | undefined {\n\treturn parseCompactStateFile(path).pending;\n}\n\nexport function writePiServerPendingCompact(\n\tpath: string,\n\tinput: WritePiServerPendingCompactInput,\n): PiServerPendingCompactState {\n\treturn withCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tif (state.pending) {\n\t\t\tthrow new Error(\n\t\t\t\t`pi-server compaction ${state.pending.operationId} must be acknowledged before starting another compaction`,\n\t\t\t);\n\t\t}\n\t\tif (state.nextSequence > 0) {\n\t\t\ttruncateSync(path, 0);\n\t\t\tstate.nextSequence = 0;\n\t\t}\n\t\tconst timestamp = input.timestamp ?? Date.now();\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact state timestamp\");\n\t\tassertHash(input.serverHash, \"pi-server compact state serverHash\");\n\t\tassertIdentifier(input.sessionId, \"pi-server compact state sessionId\");\n\t\tassertIdentifier(input.operationId, \"pi-server compact state operationId\");\n\t\tassertHash(input.requestHash, \"pi-server compact state requestHash\");\n\t\tassertHash(input.baseStaticContextHash, \"pi-server compact state baseStaticContextHash\", true);\n\t\tassertHash(input.baseTreeHash, \"pi-server compact state baseTreeHash\");\n\t\tassertNonNegativeSafeInteger(input.baseEntryCount, \"pi-server compact state baseEntryCount\");\n\t\tif (input.baseLeafId !== null) assertIdentifier(input.baseLeafId, \"pi-server compact state baseLeafId\");\n\t\tassertNonNegativeSafeInteger(input.baseRevision, \"pi-server compact state baseRevision\");\n\t\tconst record: PiServerPendingCompactState = {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"compact\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\tserverHash: input.serverHash,\n\t\t\tsessionId: input.sessionId,\n\t\t\toperationId: input.operationId,\n\t\t\trequestHash: input.requestHash,\n\t\t\tbaseStaticContextHash: input.baseStaticContextHash,\n\t\t\tbaseTreeHash: input.baseTreeHash,\n\t\t\tbaseEntryCount: input.baseEntryCount,\n\t\t\tbaseLeafId: input.baseLeafId,\n\t\t\tbaseRevision: input.baseRevision,\n\t\t};\n\t\tappendRecord(path, record);\n\t\treturn record;\n\t});\n}\n\nexport function writePiServerAppliedCompact(\n\tpath: string,\n\tinput: WritePiServerAppliedCompactInput,\n): PiServerAppliedCompactState {\n\treturn withCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tconst pending = state.pending;\n\t\tif (!pending || pending.operationId !== input.operationId || pending.requestHash !== input.requestHash) {\n\t\t\tthrow new Error(\"Cannot record an applied pi-server compaction without its matching pending operation\");\n\t\t}\n\t\tassertIdentifier(input.entryId, \"pi-server compact applied entryId\");\n\t\tassertHash(input.entryHash, \"pi-server compact applied entryHash\");\n\t\tassertHash(input.updatedTreeHash, \"pi-server compact applied updatedTreeHash\");\n\t\tassertIdentifier(input.updatedLeafId, \"pi-server compact applied updatedLeafId\");\n\t\tassertNonNegativeSafeInteger(input.updatedRevision, \"pi-server compact applied updatedRevision\");\n\t\tif (pending.observation) {\n\t\t\tif (\n\t\t\t\tpending.observation.kind !== \"applied\" ||\n\t\t\t\tpending.observation.entryId !== input.entryId ||\n\t\t\t\tpending.observation.entryHash !== input.entryHash ||\n\t\t\t\tpending.observation.updatedTreeHash !== input.updatedTreeHash ||\n\t\t\t\tpending.observation.updatedLeafId !== input.updatedLeafId ||\n\t\t\t\tpending.observation.updatedRevision !== input.updatedRevision\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Pi-server compaction already has a divergent durable observation\");\n\t\t\t}\n\t\t\treturn pending.observation;\n\t\t}\n\t\tconst timestamp = input.timestamp ?? Date.now();\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact applied timestamp\");\n\t\tconst record: PiServerAppliedCompactState = {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"applied\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\toperationId: input.operationId,\n\t\t\trequestHash: input.requestHash,\n\t\t\tentryId: input.entryId,\n\t\t\tentryHash: input.entryHash,\n\t\t\tupdatedTreeHash: input.updatedTreeHash,\n\t\t\tupdatedLeafId: input.updatedLeafId,\n\t\t\tupdatedRevision: input.updatedRevision,\n\t\t\tresolution: \"tree_applied\",\n\t\t};\n\t\tappendRecord(path, record);\n\t\treturn record;\n\t});\n}\n\nexport function writePiServerTerminalCompact(\n\tpath: string,\n\tinput: WritePiServerTerminalCompactInput,\n): PiServerTerminalCompactState {\n\treturn withCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tconst pending = state.pending;\n\t\tif (!pending || pending.operationId !== input.operationId || pending.requestHash !== input.requestHash) {\n\t\t\tthrow new Error(\"Cannot record a terminal pi-server compaction without its matching pending operation\");\n\t\t}\n\t\tif (!Number.isSafeInteger(input.httpStatus) || input.httpStatus < 100 || input.httpStatus > 599) {\n\t\t\tthrow new Error(\"pi-server compact terminal HTTP status is invalid\");\n\t\t}\n\t\tassertError(input.error, \"pi-server compact terminal error\");\n\t\tif (\n\t\t\t(input.operationDisposition === \"terminal\" && input.status !== \"failed\") ||\n\t\t\t(input.operationDisposition === \"not_started\" && input.status !== \"rejected\")\n\t\t) {\n\t\t\tthrow new Error(\"pi-server compact terminal disposition is invalid\");\n\t\t}\n\t\tif (pending.observation) {\n\t\t\tif (\n\t\t\t\tpending.observation.kind !== \"terminal\" ||\n\t\t\t\tpending.observation.httpStatus !== input.httpStatus ||\n\t\t\t\tpending.observation.error !== input.error ||\n\t\t\t\tpending.observation.operationDisposition !== input.operationDisposition ||\n\t\t\t\tpending.observation.status !== input.status\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Pi-server compaction already has a divergent durable observation\");\n\t\t\t}\n\t\t\treturn pending.observation;\n\t\t}\n\t\tconst timestamp = input.timestamp ?? Date.now();\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact terminal timestamp\");\n\t\tconst record: PiServerTerminalCompactState = {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"terminal\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\toperationId: input.operationId,\n\t\t\trequestHash: input.requestHash,\n\t\t\thttpStatus: input.httpStatus,\n\t\t\terror: input.error,\n\t\t\toperationDisposition: input.operationDisposition,\n\t\t\tstatus: input.status,\n\t\t\tresolution: input.operationDisposition === \"terminal\" ? \"terminal_failed_observed\" : \"server_rejected\",\n\t\t};\n\t\tappendRecord(path, record);\n\t\treturn record;\n\t});\n}\n\nexport function acknowledgePiServerPendingCompact(\n\tpath: string,\n\toperationId: string,\n\tresolution: PiServerCompactAcknowledgementResolution = \"server_ack\",\n\ttimestamp = Date.now(),\n): void {\n\twithCompactStateLock(path, () => {\n\t\tconst state = parseCompactStateFile(path);\n\t\tif (!state.pending) return;\n\t\tif (state.pending.operationId !== operationId) {\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot acknowledge pi-server compaction ${operationId}; pending operation is ${state.pending.operationId}`,\n\t\t\t);\n\t\t}\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server compact acknowledgement timestamp\");\n\t\tappendRecord(path, {\n\t\t\tversion: COMPACT_STATE_VERSION,\n\t\t\tkind: \"ack\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\toperationId,\n\t\t\tresolution,\n\t\t});\n\t});\n}\n"]}
@@ -15,6 +15,7 @@ export interface PiServerPendingRunState extends PiServerRunStateBase {
15
15
  baseLeafId: string | null;
16
16
  requestHash: string;
17
17
  }
18
+ export type PiServerRunAcknowledgementResolution = "server_ack" | "server_authoritative_missing" | "server_authoritative_rebind";
18
19
  export interface WritePiServerPendingRunInput {
19
20
  serverHash: string;
20
21
  sessionId: string;
@@ -36,6 +37,6 @@ export declare function getPiServerRunStatePath(sessionFile: string): string;
36
37
  export declare function hashPiServerIdentity(serverUrl: string): string;
37
38
  export declare function readPiServerPendingRun(path: string, lease?: PiServerRunStateLease): PiServerPendingRunState | undefined;
38
39
  export declare function writePiServerPendingRun(path: string, input: WritePiServerPendingRunInput, lease?: PiServerRunStateLease): PiServerPendingRunState;
39
- export declare function acknowledgePiServerPendingRun(path: string, runId: string, timestamp?: number, lease?: PiServerRunStateLease): void;
40
+ export declare function acknowledgePiServerPendingRun(path: string, runId: string, timestamp?: number, lease?: PiServerRunStateLease, resolution?: PiServerRunAcknowledgementResolution): void;
40
41
  export {};
41
42
  //# sourceMappingURL=pi-server-run-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pi-server-run-state.d.ts","sourceRoot":"","sources":["../../src/core/pi-server-run-state.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,QAAA,MAAM,iBAAiB,IAAI,CAAC;AAK5B,UAAU,oBAAoB;IAC7B,OAAO,EAAE,OAAO,iBAAiB,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,uBAAwB,SAAQ,oBAAoB;IACpE,IAAI,EAAE,KAAK,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACpB;AAeD,MAAM,WAAW,4BAA4B;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAOD,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,MAAM,EAAE,OAAO,CAAC;CAChB;AAMD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAuBhF;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI,CAa/E;AAmMD,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,sBAAsB,CACrC,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,qBAAqB,GAC3B,uBAAuB,GAAG,SAAS,CAOrC;AAED,wBAAgB,uBAAuB,CACtC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,4BAA4B,EACnC,KAAK,CAAC,EAAE,qBAAqB,GAC3B,uBAAuB,CAuCzB;AAED,wBAAgB,6BAA6B,CAC5C,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,SAAa,EACtB,KAAK,CAAC,EAAE,qBAAqB,GAC3B,IAAI,CAiBN","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { createHash } from \"node:crypto\";\nimport { closeSync, existsSync, fsyncSync, openSync, readFileSync, truncateSync, writeSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { DatabaseSync } from \"node:sqlite\";\n\nconst RUN_STATE_VERSION = 1;\nconst RUN_STATE_MAX_BYTES = 8 * 1024 * 1024;\nconst SHA256_PATTERN = /^[a-f0-9]{64}$/;\nconst IDENTIFIER_MAX_CHARS = 1024;\n\ninterface PiServerRunStateBase {\n\tversion: typeof RUN_STATE_VERSION;\n\tsequence: number;\n\ttimestamp: number;\n}\n\nexport interface PiServerPendingRunState extends PiServerRunStateBase {\n\tkind: \"run\";\n\tserverHash: string;\n\tsessionId: string;\n\trunId: string;\n\tbaseTreeHash: string;\n\tbaseEntryCount: number;\n\tbaseLeafId: string | null;\n\trequestHash: string;\n}\n\ninterface PiServerAcknowledgedRunState extends PiServerRunStateBase {\n\tkind: \"ack\";\n\trunId: string;\n\tresolution: \"server_ack\";\n}\n\ntype PiServerRunStateRecord = PiServerPendingRunState | PiServerAcknowledgedRunState;\n\ninterface PiServerRunStateEnvelope {\n\tpayload: string;\n\tsha256: string;\n}\n\nexport interface WritePiServerPendingRunInput {\n\tserverHash: string;\n\tsessionId: string;\n\trunId: string;\n\tbaseTreeHash: string;\n\tbaseEntryCount: number;\n\tbaseLeafId: string | null;\n\trequestHash: string;\n\ttimestamp?: number;\n}\n\ninterface ParsedPiServerRunState {\n\tnextSequence: number;\n\tpending: PiServerPendingRunState | undefined;\n}\n\nexport interface PiServerRunStateLease {\n\treadonly path: string;\n\treadonly database: DatabaseSync;\n\tactive: boolean;\n}\n\nfunction sha256(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nexport function acquirePiServerRunStateLease(path: string): PiServerRunStateLease {\n\tconst normalizedPath = resolve(path);\n\tconst database = new DatabaseSync(`${normalizedPath}.lock.sqlite`);\n\tlet transactionStarted = false;\n\ttry {\n\t\tdatabase.exec(\"PRAGMA busy_timeout = 0\");\n\t\tdatabase.exec(\"CREATE TABLE IF NOT EXISTS run_state_lock (id INTEGER PRIMARY KEY CHECK (id = 1))\");\n\t\tdatabase.exec(\"BEGIN IMMEDIATE\");\n\t\ttransactionStarted = true;\n\t\treturn { path: normalizedPath, database, active: true };\n\t} catch (error) {\n\t\tif (transactionStarted) {\n\t\t\ttry {\n\t\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t\t} catch {\n\t\t\t\t// Closing the connection below is the authoritative lock release.\n\t\t\t}\n\t\t}\n\t\tdatabase.close();\n\t\tthrow new Error(`Cannot acquire pi-server run lease for ${normalizedPath}; another process owns the session`, {\n\t\t\tcause: error,\n\t\t});\n\t}\n}\n\nexport function releasePiServerRunStateLease(lease: PiServerRunStateLease): void {\n\tif (!lease.active) return;\n\tlease.active = false;\n\ttry {\n\t\tlease.database.exec(\"ROLLBACK\");\n\t} catch {\n\t\t// close() still releases the OS-backed SQLite transaction after connection failure.\n\t}\n\ttry {\n\t\tlease.database.close();\n\t} catch {\n\t\t// The lease is already unusable and close() is idempotent at this abstraction boundary.\n\t}\n}\n\nfunction assertPiServerRunStateLease(path: string, lease: PiServerRunStateLease): void {\n\tif (!lease.active) {\n\t\tthrow new Error(\"Cannot use a released pi-server run lease\");\n\t}\n\tconst normalizedPath = resolve(path);\n\tif (lease.path !== normalizedPath) {\n\t\tthrow new Error(`Pi-server run lease path mismatch: expected ${normalizedPath}, received ${lease.path}`);\n\t}\n}\n\nfunction withRunStateLease<T>(path: string, lease: PiServerRunStateLease | undefined, operation: () => T): T {\n\tif (lease) {\n\t\tassertPiServerRunStateLease(path, lease);\n\t\treturn operation();\n\t}\n\tconst acquiredLease = acquirePiServerRunStateLease(path);\n\ttry {\n\t\treturn operation();\n\t} finally {\n\t\treleasePiServerRunStateLease(acquiredLease);\n\t}\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 assertIdentifier(value: unknown, path: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.length === 0 || value.length > IDENTIFIER_MAX_CHARS) {\n\t\tthrow new Error(`${path} must be a non-empty string up to ${IDENTIFIER_MAX_CHARS} characters`);\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 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 parseRecord(payload: string, lineNumber: number): PiServerRunStateRecord {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(payload) as unknown;\n\t} catch (error) {\n\t\tthrow new Error(`pi-server run state payload is invalid JSON at line ${lineNumber}`, { cause: error });\n\t}\n\tassertObject(value, `pi-server run state line ${lineNumber}`);\n\tif (value.version !== RUN_STATE_VERSION) {\n\t\tthrow new Error(`Unsupported pi-server run state version at line ${lineNumber}`);\n\t}\n\tassertNonNegativeSafeInteger(value.sequence, `pi-server run state line ${lineNumber}.sequence`);\n\tassertNonNegativeSafeInteger(value.timestamp, `pi-server run state line ${lineNumber}.timestamp`);\n\tif (value.kind === \"ack\") {\n\t\tassertIdentifier(value.runId, `pi-server run state line ${lineNumber}.runId`);\n\t\tif (value.resolution !== \"server_ack\") {\n\t\t\tthrow new Error(`pi-server run state acknowledgement resolution is invalid at line ${lineNumber}`);\n\t\t}\n\t\treturn {\n\t\t\tversion: RUN_STATE_VERSION,\n\t\t\tkind: \"ack\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\trunId: value.runId,\n\t\t\tresolution: value.resolution,\n\t\t};\n\t}\n\tif (value.kind !== \"run\") {\n\t\tthrow new Error(`Unsupported pi-server run state record kind at line ${lineNumber}`);\n\t}\n\tassertHash(value.serverHash, `pi-server run state line ${lineNumber}.serverHash`);\n\tassertIdentifier(value.sessionId, `pi-server run state line ${lineNumber}.sessionId`);\n\tassertIdentifier(value.runId, `pi-server run state line ${lineNumber}.runId`);\n\tassertHash(value.baseTreeHash, `pi-server run state line ${lineNumber}.baseTreeHash`);\n\tassertNonNegativeSafeInteger(value.baseEntryCount, `pi-server run state line ${lineNumber}.baseEntryCount`);\n\tif (value.baseLeafId !== null) {\n\t\tassertIdentifier(value.baseLeafId, `pi-server run state line ${lineNumber}.baseLeafId`);\n\t}\n\tassertHash(value.requestHash, `pi-server run state line ${lineNumber}.requestHash`);\n\treturn {\n\t\tversion: RUN_STATE_VERSION,\n\t\tkind: \"run\",\n\t\tsequence: value.sequence,\n\t\ttimestamp: value.timestamp,\n\t\tserverHash: value.serverHash,\n\t\tsessionId: value.sessionId,\n\t\trunId: value.runId,\n\t\tbaseTreeHash: value.baseTreeHash,\n\t\tbaseEntryCount: value.baseEntryCount,\n\t\tbaseLeafId: value.baseLeafId,\n\t\trequestHash: value.requestHash,\n\t};\n}\n\nfunction parseRunStateFile(path: string): ParsedPiServerRunState {\n\tif (!existsSync(path)) {\n\t\treturn { nextSequence: 0, pending: undefined };\n\t}\n\tconst encoded = readFileSync(path);\n\tif (encoded.byteLength > RUN_STATE_MAX_BYTES) {\n\t\tthrow new Error(`pi-server run state exceeds ${RUN_STATE_MAX_BYTES} bytes: ${path}`);\n\t}\n\tconst content = encoded.toString(\"utf-8\");\n\tconst hasCompleteTail = content.endsWith(\"\\n\");\n\tconst lines = content.split(\"\\n\");\n\tif (!hasCompleteTail) {\n\t\tlines.pop();\n\t}\n\tlet expectedSequence = 0;\n\tlet pending: PiServerPendingRunState | undefined;\n\tfor (const [index, line] of lines.entries()) {\n\t\tif (line.length === 0) continue;\n\t\tlet envelopeValue: unknown;\n\t\ttry {\n\t\t\tenvelopeValue = JSON.parse(line) as unknown;\n\t\t} catch (error) {\n\t\t\tthrow new Error(`pi-server run state envelope is invalid JSON at line ${index + 1}`, { cause: error });\n\t\t}\n\t\tassertObject(envelopeValue, `pi-server run state envelope line ${index + 1}`);\n\t\tif (typeof envelopeValue.payload !== \"string\") {\n\t\t\tthrow new Error(`pi-server run state envelope payload must be a string at line ${index + 1}`);\n\t\t}\n\t\tassertHash(envelopeValue.sha256, `pi-server run state envelope line ${index + 1}.sha256`);\n\t\tif (sha256(envelopeValue.payload) !== envelopeValue.sha256) {\n\t\t\tthrow new Error(`pi-server run state checksum mismatch at line ${index + 1}`);\n\t\t}\n\t\tconst record = parseRecord(envelopeValue.payload, index + 1);\n\t\tif (record.sequence !== expectedSequence) {\n\t\t\tthrow new Error(\n\t\t\t\t`pi-server run state sequence mismatch at line ${index + 1}: expected ${expectedSequence}, received ${record.sequence}`,\n\t\t\t);\n\t\t}\n\t\texpectedSequence++;\n\t\tif (record.kind === \"run\") {\n\t\t\tif (pending) {\n\t\t\t\tthrow new Error(`pi-server run state contains a new run before ${pending.runId} was acknowledged`);\n\t\t\t}\n\t\t\tpending = record;\n\t\t} else {\n\t\t\tif (!pending || pending.runId !== record.runId) {\n\t\t\t\tthrow new Error(`pi-server run state acknowledgement does not match the pending run at line ${index + 1}`);\n\t\t\t}\n\t\t\tpending = undefined;\n\t\t}\n\t}\n\treturn { nextSequence: expectedSequence, pending };\n}\n\nfunction appendRecord(path: string, record: PiServerRunStateRecord): void {\n\tconst payload = JSON.stringify(record);\n\tconst envelope: PiServerRunStateEnvelope = { payload, sha256: sha256(payload) };\n\tconst encoded = Buffer.from(`${JSON.stringify(envelope)}\\n`, \"utf-8\");\n\tconst existing = existsSync(path) ? readFileSync(path) : Buffer.alloc(0);\n\tconst validBytes = existing.lastIndexOf(0x0a) + 1;\n\tif (validBytes !== existing.byteLength) {\n\t\ttruncateSync(path, validBytes);\n\t}\n\tconst existingBytes = validBytes;\n\tif (existingBytes + encoded.byteLength > RUN_STATE_MAX_BYTES) {\n\t\tthrow new Error(`pi-server run state would exceed ${RUN_STATE_MAX_BYTES} bytes: ${path}`);\n\t}\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 pi-server run state: ${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\nexport function getPiServerRunStatePath(sessionFile: string): string {\n\treturn `${sessionFile}.pi-server-runs.jsonl`;\n}\n\nexport function hashPiServerIdentity(serverUrl: string): string {\n\treturn sha256(serverUrl);\n}\n\nexport function readPiServerPendingRun(\n\tpath: string,\n\tlease?: PiServerRunStateLease,\n): PiServerPendingRunState | undefined {\n\tif (lease) {\n\t\tassertPiServerRunStateLease(path, lease);\n\t}\n\t// The append-only checksum envelope makes an unlocked observer read safe: a concurrent\n\t// incomplete final record is ignored. Lifecycle decisions pass the owning lease.\n\treturn parseRunStateFile(path).pending;\n}\n\nexport function writePiServerPendingRun(\n\tpath: string,\n\tinput: WritePiServerPendingRunInput,\n\tlease?: PiServerRunStateLease,\n): PiServerPendingRunState {\n\treturn withRunStateLease(path, lease, () => {\n\t\tconst state = parseRunStateFile(path);\n\t\tif (state.pending) {\n\t\t\tthrow new Error(`pi-server run ${state.pending.runId} must be acknowledged before starting another run`);\n\t\t}\n\t\tif (state.nextSequence > 0) {\n\t\t\t// All earlier runs are acknowledged. Truncating before the new durable marker is safe:\n\t\t\t// the caller cannot submit the new provider request until this function returns.\n\t\t\ttruncateSync(path, 0);\n\t\t\tstate.nextSequence = 0;\n\t\t}\n\t\tconst timestamp = input.timestamp ?? Date.now();\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server run state timestamp\");\n\t\tassertHash(input.serverHash, \"pi-server run state serverHash\");\n\t\tassertIdentifier(input.sessionId, \"pi-server run state sessionId\");\n\t\tassertIdentifier(input.runId, \"pi-server run state runId\");\n\t\tassertHash(input.baseTreeHash, \"pi-server run state baseTreeHash\");\n\t\tassertNonNegativeSafeInteger(input.baseEntryCount, \"pi-server run state baseEntryCount\");\n\t\tif (input.baseLeafId !== null) {\n\t\t\tassertIdentifier(input.baseLeafId, \"pi-server run state baseLeafId\");\n\t\t}\n\t\tassertHash(input.requestHash, \"pi-server run state requestHash\");\n\t\tconst record: PiServerPendingRunState = {\n\t\t\tversion: RUN_STATE_VERSION,\n\t\t\tkind: \"run\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\tserverHash: input.serverHash,\n\t\t\tsessionId: input.sessionId,\n\t\t\trunId: input.runId,\n\t\t\tbaseTreeHash: input.baseTreeHash,\n\t\t\tbaseEntryCount: input.baseEntryCount,\n\t\t\tbaseLeafId: input.baseLeafId,\n\t\t\trequestHash: input.requestHash,\n\t\t};\n\t\tappendRecord(path, record);\n\t\treturn record;\n\t});\n}\n\nexport function acknowledgePiServerPendingRun(\n\tpath: string,\n\trunId: string,\n\ttimestamp = Date.now(),\n\tlease?: PiServerRunStateLease,\n): void {\n\twithRunStateLease(path, lease, () => {\n\t\tconst state = parseRunStateFile(path);\n\t\tif (!state.pending) return;\n\t\tif (state.pending.runId !== runId) {\n\t\t\tthrow new Error(`Cannot acknowledge pi-server run ${runId}; pending run is ${state.pending.runId}`);\n\t\t}\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server run acknowledgement timestamp\");\n\t\tappendRecord(path, {\n\t\t\tversion: RUN_STATE_VERSION,\n\t\t\tkind: \"ack\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\trunId,\n\t\t\tresolution: \"server_ack\",\n\t\t});\n\t});\n}\n"]}
1
+ {"version":3,"file":"pi-server-run-state.d.ts","sourceRoot":"","sources":["../../src/core/pi-server-run-state.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,QAAA,MAAM,iBAAiB,IAAI,CAAC;AAK5B,UAAU,oBAAoB;IAC7B,OAAO,EAAE,OAAO,iBAAiB,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,uBAAwB,SAAQ,oBAAoB;IACpE,IAAI,EAAE,KAAK,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACpB;AAUD,MAAM,MAAM,oCAAoC,GAC7C,YAAY,GACZ,8BAA8B,GAC9B,6BAA6B,CAAC;AAOjC,MAAM,WAAW,4BAA4B;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAOD,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,MAAM,EAAE,OAAO,CAAC;CAChB;AAMD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAuBhF;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI,CAa/E;AAuMD,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,sBAAsB,CACrC,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,qBAAqB,GAC3B,uBAAuB,GAAG,SAAS,CAOrC;AAED,wBAAgB,uBAAuB,CACtC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,4BAA4B,EACnC,KAAK,CAAC,EAAE,qBAAqB,GAC3B,uBAAuB,CAuCzB;AAED,wBAAgB,6BAA6B,CAC5C,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,SAAa,EACtB,KAAK,CAAC,EAAE,qBAAqB,EAC7B,UAAU,GAAE,oCAAmD,GAC7D,IAAI,CAiBN","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { createHash } from \"node:crypto\";\nimport { closeSync, existsSync, fsyncSync, openSync, readFileSync, truncateSync, writeSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { DatabaseSync } from \"node:sqlite\";\n\nconst RUN_STATE_VERSION = 1;\nconst RUN_STATE_MAX_BYTES = 8 * 1024 * 1024;\nconst SHA256_PATTERN = /^[a-f0-9]{64}$/;\nconst IDENTIFIER_MAX_CHARS = 1024;\n\ninterface PiServerRunStateBase {\n\tversion: typeof RUN_STATE_VERSION;\n\tsequence: number;\n\ttimestamp: number;\n}\n\nexport interface PiServerPendingRunState extends PiServerRunStateBase {\n\tkind: \"run\";\n\tserverHash: string;\n\tsessionId: string;\n\trunId: string;\n\tbaseTreeHash: string;\n\tbaseEntryCount: number;\n\tbaseLeafId: string | null;\n\trequestHash: string;\n}\n\ninterface PiServerAcknowledgedRunState extends PiServerRunStateBase {\n\tkind: \"ack\";\n\trunId: string;\n\tresolution: PiServerRunAcknowledgementResolution;\n}\n\ntype PiServerRunStateRecord = PiServerPendingRunState | PiServerAcknowledgedRunState;\n\nexport type PiServerRunAcknowledgementResolution =\n\t| \"server_ack\"\n\t| \"server_authoritative_missing\"\n\t| \"server_authoritative_rebind\";\n\ninterface PiServerRunStateEnvelope {\n\tpayload: string;\n\tsha256: string;\n}\n\nexport interface WritePiServerPendingRunInput {\n\tserverHash: string;\n\tsessionId: string;\n\trunId: string;\n\tbaseTreeHash: string;\n\tbaseEntryCount: number;\n\tbaseLeafId: string | null;\n\trequestHash: string;\n\ttimestamp?: number;\n}\n\ninterface ParsedPiServerRunState {\n\tnextSequence: number;\n\tpending: PiServerPendingRunState | undefined;\n}\n\nexport interface PiServerRunStateLease {\n\treadonly path: string;\n\treadonly database: DatabaseSync;\n\tactive: boolean;\n}\n\nfunction sha256(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nexport function acquirePiServerRunStateLease(path: string): PiServerRunStateLease {\n\tconst normalizedPath = resolve(path);\n\tconst database = new DatabaseSync(`${normalizedPath}.lock.sqlite`);\n\tlet transactionStarted = false;\n\ttry {\n\t\tdatabase.exec(\"PRAGMA busy_timeout = 0\");\n\t\tdatabase.exec(\"CREATE TABLE IF NOT EXISTS run_state_lock (id INTEGER PRIMARY KEY CHECK (id = 1))\");\n\t\tdatabase.exec(\"BEGIN IMMEDIATE\");\n\t\ttransactionStarted = true;\n\t\treturn { path: normalizedPath, database, active: true };\n\t} catch (error) {\n\t\tif (transactionStarted) {\n\t\t\ttry {\n\t\t\t\tdatabase.exec(\"ROLLBACK\");\n\t\t\t} catch {\n\t\t\t\t// Closing the connection below is the authoritative lock release.\n\t\t\t}\n\t\t}\n\t\tdatabase.close();\n\t\tthrow new Error(`Cannot acquire pi-server run lease for ${normalizedPath}; another process owns the session`, {\n\t\t\tcause: error,\n\t\t});\n\t}\n}\n\nexport function releasePiServerRunStateLease(lease: PiServerRunStateLease): void {\n\tif (!lease.active) return;\n\tlease.active = false;\n\ttry {\n\t\tlease.database.exec(\"ROLLBACK\");\n\t} catch {\n\t\t// close() still releases the OS-backed SQLite transaction after connection failure.\n\t}\n\ttry {\n\t\tlease.database.close();\n\t} catch {\n\t\t// The lease is already unusable and close() is idempotent at this abstraction boundary.\n\t}\n}\n\nfunction assertPiServerRunStateLease(path: string, lease: PiServerRunStateLease): void {\n\tif (!lease.active) {\n\t\tthrow new Error(\"Cannot use a released pi-server run lease\");\n\t}\n\tconst normalizedPath = resolve(path);\n\tif (lease.path !== normalizedPath) {\n\t\tthrow new Error(`Pi-server run lease path mismatch: expected ${normalizedPath}, received ${lease.path}`);\n\t}\n}\n\nfunction withRunStateLease<T>(path: string, lease: PiServerRunStateLease | undefined, operation: () => T): T {\n\tif (lease) {\n\t\tassertPiServerRunStateLease(path, lease);\n\t\treturn operation();\n\t}\n\tconst acquiredLease = acquirePiServerRunStateLease(path);\n\ttry {\n\t\treturn operation();\n\t} finally {\n\t\treleasePiServerRunStateLease(acquiredLease);\n\t}\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 assertIdentifier(value: unknown, path: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.length === 0 || value.length > IDENTIFIER_MAX_CHARS) {\n\t\tthrow new Error(`${path} must be a non-empty string up to ${IDENTIFIER_MAX_CHARS} characters`);\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 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 parseRecord(payload: string, lineNumber: number): PiServerRunStateRecord {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(payload) as unknown;\n\t} catch (error) {\n\t\tthrow new Error(`pi-server run state payload is invalid JSON at line ${lineNumber}`, { cause: error });\n\t}\n\tassertObject(value, `pi-server run state line ${lineNumber}`);\n\tif (value.version !== RUN_STATE_VERSION) {\n\t\tthrow new Error(`Unsupported pi-server run state version at line ${lineNumber}`);\n\t}\n\tassertNonNegativeSafeInteger(value.sequence, `pi-server run state line ${lineNumber}.sequence`);\n\tassertNonNegativeSafeInteger(value.timestamp, `pi-server run state line ${lineNumber}.timestamp`);\n\tif (value.kind === \"ack\") {\n\t\tassertIdentifier(value.runId, `pi-server run state line ${lineNumber}.runId`);\n\t\tif (\n\t\t\tvalue.resolution !== \"server_ack\" &&\n\t\t\tvalue.resolution !== \"server_authoritative_missing\" &&\n\t\t\tvalue.resolution !== \"server_authoritative_rebind\"\n\t\t) {\n\t\t\tthrow new Error(`pi-server run state acknowledgement resolution is invalid at line ${lineNumber}`);\n\t\t}\n\t\treturn {\n\t\t\tversion: RUN_STATE_VERSION,\n\t\t\tkind: \"ack\",\n\t\t\tsequence: value.sequence,\n\t\t\ttimestamp: value.timestamp,\n\t\t\trunId: value.runId,\n\t\t\tresolution: value.resolution,\n\t\t};\n\t}\n\tif (value.kind !== \"run\") {\n\t\tthrow new Error(`Unsupported pi-server run state record kind at line ${lineNumber}`);\n\t}\n\tassertHash(value.serverHash, `pi-server run state line ${lineNumber}.serverHash`);\n\tassertIdentifier(value.sessionId, `pi-server run state line ${lineNumber}.sessionId`);\n\tassertIdentifier(value.runId, `pi-server run state line ${lineNumber}.runId`);\n\tassertHash(value.baseTreeHash, `pi-server run state line ${lineNumber}.baseTreeHash`);\n\tassertNonNegativeSafeInteger(value.baseEntryCount, `pi-server run state line ${lineNumber}.baseEntryCount`);\n\tif (value.baseLeafId !== null) {\n\t\tassertIdentifier(value.baseLeafId, `pi-server run state line ${lineNumber}.baseLeafId`);\n\t}\n\tassertHash(value.requestHash, `pi-server run state line ${lineNumber}.requestHash`);\n\treturn {\n\t\tversion: RUN_STATE_VERSION,\n\t\tkind: \"run\",\n\t\tsequence: value.sequence,\n\t\ttimestamp: value.timestamp,\n\t\tserverHash: value.serverHash,\n\t\tsessionId: value.sessionId,\n\t\trunId: value.runId,\n\t\tbaseTreeHash: value.baseTreeHash,\n\t\tbaseEntryCount: value.baseEntryCount,\n\t\tbaseLeafId: value.baseLeafId,\n\t\trequestHash: value.requestHash,\n\t};\n}\n\nfunction parseRunStateFile(path: string): ParsedPiServerRunState {\n\tif (!existsSync(path)) {\n\t\treturn { nextSequence: 0, pending: undefined };\n\t}\n\tconst encoded = readFileSync(path);\n\tif (encoded.byteLength > RUN_STATE_MAX_BYTES) {\n\t\tthrow new Error(`pi-server run state exceeds ${RUN_STATE_MAX_BYTES} bytes: ${path}`);\n\t}\n\tconst content = encoded.toString(\"utf-8\");\n\tconst hasCompleteTail = content.endsWith(\"\\n\");\n\tconst lines = content.split(\"\\n\");\n\tif (!hasCompleteTail) {\n\t\tlines.pop();\n\t}\n\tlet expectedSequence = 0;\n\tlet pending: PiServerPendingRunState | undefined;\n\tfor (const [index, line] of lines.entries()) {\n\t\tif (line.length === 0) continue;\n\t\tlet envelopeValue: unknown;\n\t\ttry {\n\t\t\tenvelopeValue = JSON.parse(line) as unknown;\n\t\t} catch (error) {\n\t\t\tthrow new Error(`pi-server run state envelope is invalid JSON at line ${index + 1}`, { cause: error });\n\t\t}\n\t\tassertObject(envelopeValue, `pi-server run state envelope line ${index + 1}`);\n\t\tif (typeof envelopeValue.payload !== \"string\") {\n\t\t\tthrow new Error(`pi-server run state envelope payload must be a string at line ${index + 1}`);\n\t\t}\n\t\tassertHash(envelopeValue.sha256, `pi-server run state envelope line ${index + 1}.sha256`);\n\t\tif (sha256(envelopeValue.payload) !== envelopeValue.sha256) {\n\t\t\tthrow new Error(`pi-server run state checksum mismatch at line ${index + 1}`);\n\t\t}\n\t\tconst record = parseRecord(envelopeValue.payload, index + 1);\n\t\tif (record.sequence !== expectedSequence) {\n\t\t\tthrow new Error(\n\t\t\t\t`pi-server run state sequence mismatch at line ${index + 1}: expected ${expectedSequence}, received ${record.sequence}`,\n\t\t\t);\n\t\t}\n\t\texpectedSequence++;\n\t\tif (record.kind === \"run\") {\n\t\t\tif (pending) {\n\t\t\t\tthrow new Error(`pi-server run state contains a new run before ${pending.runId} was acknowledged`);\n\t\t\t}\n\t\t\tpending = record;\n\t\t} else {\n\t\t\tif (!pending || pending.runId !== record.runId) {\n\t\t\t\tthrow new Error(`pi-server run state acknowledgement does not match the pending run at line ${index + 1}`);\n\t\t\t}\n\t\t\tpending = undefined;\n\t\t}\n\t}\n\treturn { nextSequence: expectedSequence, pending };\n}\n\nfunction appendRecord(path: string, record: PiServerRunStateRecord): void {\n\tconst payload = JSON.stringify(record);\n\tconst envelope: PiServerRunStateEnvelope = { payload, sha256: sha256(payload) };\n\tconst encoded = Buffer.from(`${JSON.stringify(envelope)}\\n`, \"utf-8\");\n\tconst existing = existsSync(path) ? readFileSync(path) : Buffer.alloc(0);\n\tconst validBytes = existing.lastIndexOf(0x0a) + 1;\n\tif (validBytes !== existing.byteLength) {\n\t\ttruncateSync(path, validBytes);\n\t}\n\tconst existingBytes = validBytes;\n\tif (existingBytes + encoded.byteLength > RUN_STATE_MAX_BYTES) {\n\t\tthrow new Error(`pi-server run state would exceed ${RUN_STATE_MAX_BYTES} bytes: ${path}`);\n\t}\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 pi-server run state: ${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\nexport function getPiServerRunStatePath(sessionFile: string): string {\n\treturn `${sessionFile}.pi-server-runs.jsonl`;\n}\n\nexport function hashPiServerIdentity(serverUrl: string): string {\n\treturn sha256(serverUrl);\n}\n\nexport function readPiServerPendingRun(\n\tpath: string,\n\tlease?: PiServerRunStateLease,\n): PiServerPendingRunState | undefined {\n\tif (lease) {\n\t\tassertPiServerRunStateLease(path, lease);\n\t}\n\t// The append-only checksum envelope makes an unlocked observer read safe: a concurrent\n\t// incomplete final record is ignored. Lifecycle decisions pass the owning lease.\n\treturn parseRunStateFile(path).pending;\n}\n\nexport function writePiServerPendingRun(\n\tpath: string,\n\tinput: WritePiServerPendingRunInput,\n\tlease?: PiServerRunStateLease,\n): PiServerPendingRunState {\n\treturn withRunStateLease(path, lease, () => {\n\t\tconst state = parseRunStateFile(path);\n\t\tif (state.pending) {\n\t\t\tthrow new Error(`pi-server run ${state.pending.runId} must be acknowledged before starting another run`);\n\t\t}\n\t\tif (state.nextSequence > 0) {\n\t\t\t// All earlier runs are acknowledged. Truncating before the new durable marker is safe:\n\t\t\t// the caller cannot submit the new provider request until this function returns.\n\t\t\ttruncateSync(path, 0);\n\t\t\tstate.nextSequence = 0;\n\t\t}\n\t\tconst timestamp = input.timestamp ?? Date.now();\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server run state timestamp\");\n\t\tassertHash(input.serverHash, \"pi-server run state serverHash\");\n\t\tassertIdentifier(input.sessionId, \"pi-server run state sessionId\");\n\t\tassertIdentifier(input.runId, \"pi-server run state runId\");\n\t\tassertHash(input.baseTreeHash, \"pi-server run state baseTreeHash\");\n\t\tassertNonNegativeSafeInteger(input.baseEntryCount, \"pi-server run state baseEntryCount\");\n\t\tif (input.baseLeafId !== null) {\n\t\t\tassertIdentifier(input.baseLeafId, \"pi-server run state baseLeafId\");\n\t\t}\n\t\tassertHash(input.requestHash, \"pi-server run state requestHash\");\n\t\tconst record: PiServerPendingRunState = {\n\t\t\tversion: RUN_STATE_VERSION,\n\t\t\tkind: \"run\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\tserverHash: input.serverHash,\n\t\t\tsessionId: input.sessionId,\n\t\t\trunId: input.runId,\n\t\t\tbaseTreeHash: input.baseTreeHash,\n\t\t\tbaseEntryCount: input.baseEntryCount,\n\t\t\tbaseLeafId: input.baseLeafId,\n\t\t\trequestHash: input.requestHash,\n\t\t};\n\t\tappendRecord(path, record);\n\t\treturn record;\n\t});\n}\n\nexport function acknowledgePiServerPendingRun(\n\tpath: string,\n\trunId: string,\n\ttimestamp = Date.now(),\n\tlease?: PiServerRunStateLease,\n\tresolution: PiServerRunAcknowledgementResolution = \"server_ack\",\n): void {\n\twithRunStateLease(path, lease, () => {\n\t\tconst state = parseRunStateFile(path);\n\t\tif (!state.pending) return;\n\t\tif (state.pending.runId !== runId) {\n\t\t\tthrow new Error(`Cannot acknowledge pi-server run ${runId}; pending run is ${state.pending.runId}`);\n\t\t}\n\t\tassertNonNegativeSafeInteger(timestamp, \"pi-server run acknowledgement timestamp\");\n\t\tappendRecord(path, {\n\t\t\tversion: RUN_STATE_VERSION,\n\t\t\tkind: \"ack\",\n\t\t\tsequence: state.nextSequence,\n\t\t\ttimestamp,\n\t\t\trunId,\n\t\t\tresolution,\n\t\t});\n\t});\n}\n"]}
@@ -111,7 +111,9 @@ function parseRecord(payload, lineNumber) {
111
111
  assertNonNegativeSafeInteger(value.timestamp, `pi-server run state line ${lineNumber}.timestamp`);
112
112
  if (value.kind === "ack") {
113
113
  assertIdentifier(value.runId, `pi-server run state line ${lineNumber}.runId`);
114
- if (value.resolution !== "server_ack") {
114
+ if (value.resolution !== "server_ack" &&
115
+ value.resolution !== "server_authoritative_missing" &&
116
+ value.resolution !== "server_authoritative_rebind") {
115
117
  throw new Error(`pi-server run state acknowledgement resolution is invalid at line ${lineNumber}`);
116
118
  }
117
119
  return {
@@ -296,7 +298,7 @@ export function writePiServerPendingRun(path, input, lease) {
296
298
  return record;
297
299
  });
298
300
  }
299
- export function acknowledgePiServerPendingRun(path, runId, timestamp = Date.now(), lease) {
301
+ export function acknowledgePiServerPendingRun(path, runId, timestamp = Date.now(), lease, resolution = "server_ack") {
300
302
  withRunStateLease(path, lease, () => {
301
303
  const state = parseRunStateFile(path);
302
304
  if (!state.pending)
@@ -311,7 +313,7 @@ export function acknowledgePiServerPendingRun(path, runId, timestamp = Date.now(
311
313
  sequence: state.nextSequence,
312
314
  timestamp,
313
315
  runId,
314
- resolution: "server_ack",
316
+ resolution,
315
317
  });
316
318
  });
317
319
  }