@averyyy/pi-coding-agent 0.82.1-piclient.6 → 0.82.1-piclient.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +0 -12
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +0 -5
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -12
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +89 -317
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/pi-server-client.d.ts +2 -54
- package/dist/core/pi-server-client.d.ts.map +1 -1
- package/dist/core/pi-server-client.js +272 -2320
- package/dist/core/pi-server-client.js.map +1 -1
- package/dist/core/pi-server-protocol.d.ts +0 -2
- package/dist/core/pi-server-protocol.d.ts.map +1 -1
- package/dist/core/pi-server-protocol.js +1 -24
- package/dist/core/pi-server-protocol.js.map +1 -1
- package/dist/core/pi-server-request.d.ts +0 -28
- package/dist/core/pi-server-request.d.ts.map +1 -1
- package/dist/core/pi-server-request.js +75 -492
- package/dist/core/pi-server-request.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +53 -69
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +2 -86
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +144 -737
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +1 -3
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +26 -46
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +0 -13
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +0 -15
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +29 -98
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/package.json +4 -4
- package/dist/cli/tool-effects.d.ts +0 -6
- package/dist/cli/tool-effects.d.ts.map +0 -1
- package/dist/cli/tool-effects.js +0 -164
- package/dist/cli/tool-effects.js.map +0 -1
- package/dist/core/pi-server-compact-state.d.ts +0 -81
- package/dist/core/pi-server-compact-state.d.ts.map +0 -1
- package/dist/core/pi-server-compact-state.js +0 -439
- package/dist/core/pi-server-compact-state.js.map +0 -1
- package/dist/core/pi-server-run-state.d.ts +0 -42
- package/dist/core/pi-server-run-state.d.ts.map +0 -1
- package/dist/core/pi-server-run-state.js +0 -320
- package/dist/core/pi-server-run-state.js.map +0 -1
- package/dist/core/tool-effect-journal.d.ts +0 -103
- package/dist/core/tool-effect-journal.d.ts.map +0 -1
- package/dist/core/tool-effect-journal.js +0 -1442
- package/dist/core/tool-effect-journal.js.map +0 -1
package/dist/cli/tool-effects.js
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
import { APP_NAME } from "../config.js";
|
|
4
|
-
import { assertValidSessionId, SessionManager } from "../core/session-manager.js";
|
|
5
|
-
import { acceptToolEffectResult, inspectToolEffect, markToolEffectFailed, } from "../core/tool-effect-journal.js";
|
|
6
|
-
import { normalizePath, resolvePath } from "../utils/paths.js";
|
|
7
|
-
function printToolEffectsHelp() {
|
|
8
|
-
console.log(`${chalk.bold(`${APP_NAME} tool-effects`)} - explicitly resolve an interrupted tool effect
|
|
9
|
-
|
|
10
|
-
${chalk.bold("Usage:")}
|
|
11
|
-
${APP_NAME} tool-effects inspect --session <path|exact-id> --effect-id <sha256>
|
|
12
|
-
${APP_NAME} tool-effects mark-failed --session <path|exact-id> --effect-id <sha256>
|
|
13
|
-
${APP_NAME} tool-effects accept-result --session <path|exact-id> --effect-id <sha256> <json-file>
|
|
14
|
-
|
|
15
|
-
${chalk.bold("Options:")}
|
|
16
|
-
--session <path|exact-id> Exact session path or complete session ID
|
|
17
|
-
--effect-id <sha256> Exact effect ID reported by the recovery error
|
|
18
|
-
--session-dir <dir> Override the session lookup directory
|
|
19
|
-
--help, -h Show this help
|
|
20
|
-
|
|
21
|
-
mark-failed records an error result without re-executing the tool. External side effects may already have occurred.
|
|
22
|
-
accept-result requires a complete ToolResultMessage JSON object whose toolCallId and toolName match the effect.
|
|
23
|
-
Neither command displays stored tool arguments or accepted result content.`);
|
|
24
|
-
}
|
|
25
|
-
function requireOptionValue(args, index, option) {
|
|
26
|
-
const value = args[index + 1];
|
|
27
|
-
if (value === undefined || value.startsWith("-")) {
|
|
28
|
-
throw new Error(`${option} requires a value`);
|
|
29
|
-
}
|
|
30
|
-
return value;
|
|
31
|
-
}
|
|
32
|
-
function setOnce(current, value, option) {
|
|
33
|
-
if (current !== undefined) {
|
|
34
|
-
throw new Error(`${option} may only be specified once`);
|
|
35
|
-
}
|
|
36
|
-
return value;
|
|
37
|
-
}
|
|
38
|
-
function parseToolEffectsCommand(args) {
|
|
39
|
-
if (args.includes("--help") || args.includes("-h"))
|
|
40
|
-
return "help";
|
|
41
|
-
const action = args[1];
|
|
42
|
-
if (action !== "inspect" && action !== "mark-failed" && action !== "accept-result") {
|
|
43
|
-
throw new Error("tool-effects requires one action: inspect, mark-failed, or accept-result");
|
|
44
|
-
}
|
|
45
|
-
let session;
|
|
46
|
-
let effectId;
|
|
47
|
-
let sessionDir;
|
|
48
|
-
const positional = [];
|
|
49
|
-
for (let index = 2; index < args.length; index++) {
|
|
50
|
-
const argument = args[index];
|
|
51
|
-
if (argument === "--session") {
|
|
52
|
-
session = setOnce(session, requireOptionValue(args, index, argument), argument);
|
|
53
|
-
index++;
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
if (argument === "--effect-id") {
|
|
57
|
-
effectId = setOnce(effectId, requireOptionValue(args, index, argument), argument);
|
|
58
|
-
index++;
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
if (argument === "--session-dir") {
|
|
62
|
-
sessionDir = setOnce(sessionDir, requireOptionValue(args, index, argument), argument);
|
|
63
|
-
index++;
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
if (argument.startsWith("-")) {
|
|
67
|
-
throw new Error(`Unknown tool-effects option: ${argument}`);
|
|
68
|
-
}
|
|
69
|
-
positional.push(argument);
|
|
70
|
-
}
|
|
71
|
-
if (!session)
|
|
72
|
-
throw new Error("tool-effects requires --session <path|exact-id>");
|
|
73
|
-
if (!effectId)
|
|
74
|
-
throw new Error("tool-effects requires --effect-id <sha256>");
|
|
75
|
-
if (action === "accept-result") {
|
|
76
|
-
if (positional.length !== 1) {
|
|
77
|
-
throw new Error("tool-effects accept-result requires exactly one JSON file");
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
else if (positional.length > 0) {
|
|
81
|
-
throw new Error(`tool-effects ${action} does not accept positional arguments`);
|
|
82
|
-
}
|
|
83
|
-
return {
|
|
84
|
-
action,
|
|
85
|
-
session,
|
|
86
|
-
effectId,
|
|
87
|
-
sessionDir,
|
|
88
|
-
resultFile: positional[0],
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
async function openExactSession(sessionArgument, cwd, sessionDir) {
|
|
92
|
-
if (sessionArgument.includes("/") || sessionArgument.includes("\\") || sessionArgument.endsWith(".jsonl")) {
|
|
93
|
-
return SessionManager.open(resolvePath(sessionArgument, cwd), sessionDir);
|
|
94
|
-
}
|
|
95
|
-
assertValidSessionId(sessionArgument);
|
|
96
|
-
const matches = [
|
|
97
|
-
...(await SessionManager.list(cwd, sessionDir)),
|
|
98
|
-
...(await SessionManager.listAll(sessionDir)),
|
|
99
|
-
].filter((session) => session.id === sessionArgument);
|
|
100
|
-
const uniquePaths = [...new Set(matches.map((session) => normalizePath(session.path)))];
|
|
101
|
-
if (uniquePaths.length === 0) {
|
|
102
|
-
throw new Error(`No session found with exact id '${sessionArgument}'`);
|
|
103
|
-
}
|
|
104
|
-
if (uniquePaths.length > 1) {
|
|
105
|
-
throw new Error(`Multiple sessions have exact id '${sessionArgument}'; use an exact session path`);
|
|
106
|
-
}
|
|
107
|
-
return SessionManager.open(uniquePaths[0], sessionDir);
|
|
108
|
-
}
|
|
109
|
-
function printTarget(target) {
|
|
110
|
-
console.log(`sessionPath: ${target.sessionPath}`);
|
|
111
|
-
console.log(`sessionId: ${target.sessionId}`);
|
|
112
|
-
console.log(`journalPath: ${target.journalPath}`);
|
|
113
|
-
console.log(`effectId: ${target.effectId}`);
|
|
114
|
-
console.log(`toolCallId: ${target.toolCallId}`);
|
|
115
|
-
console.log(`toolName: ${target.toolName}`);
|
|
116
|
-
console.log(`phase: ${target.phase}`);
|
|
117
|
-
}
|
|
118
|
-
function printResolution(result) {
|
|
119
|
-
printTarget(result.target);
|
|
120
|
-
console.log(`sessionEntryId: ${result.sessionEntryId}`);
|
|
121
|
-
console.log(`resolution: ${result.alreadyResolved ? "already-recorded" : "recorded"}`);
|
|
122
|
-
}
|
|
123
|
-
export async function handleToolEffectsCommand(args, options) {
|
|
124
|
-
if (args[0] !== "tool-effects")
|
|
125
|
-
return false;
|
|
126
|
-
try {
|
|
127
|
-
const command = parseToolEffectsCommand(args);
|
|
128
|
-
if (command === "help") {
|
|
129
|
-
printToolEffectsHelp();
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
const sessionDir = command.sessionDir ? resolvePath(command.sessionDir, options.cwd) : options.defaultSessionDir;
|
|
133
|
-
const session = await openExactSession(command.session, options.cwd, sessionDir);
|
|
134
|
-
if (command.action === "inspect") {
|
|
135
|
-
printTarget(inspectToolEffect(session, command.effectId));
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
if (command.action === "mark-failed") {
|
|
139
|
-
printResolution(markToolEffectFailed(session, command.effectId));
|
|
140
|
-
console.log("The tool was not re-executed; external side effects may already have occurred.");
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
if (!command.resultFile) {
|
|
144
|
-
throw new Error("tool-effects accept-result requires a JSON file");
|
|
145
|
-
}
|
|
146
|
-
const resultPath = resolvePath(command.resultFile, options.cwd);
|
|
147
|
-
let result;
|
|
148
|
-
try {
|
|
149
|
-
result = JSON.parse(readFileSync(resultPath, "utf8"));
|
|
150
|
-
}
|
|
151
|
-
catch (error) {
|
|
152
|
-
throw new Error(`Could not read accepted result JSON from ${resultPath}`, { cause: error });
|
|
153
|
-
}
|
|
154
|
-
printResolution(acceptToolEffectResult(session, command.effectId, result));
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
157
|
-
catch (error) {
|
|
158
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
159
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
160
|
-
process.exitCode = 1;
|
|
161
|
-
return true;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
//# sourceMappingURL=tool-effects.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-effects.js","sourceRoot":"","sources":["../../src/cli/tool-effects.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport chalk from \"chalk\";\nimport { APP_NAME } from \"../config.ts\";\nimport { assertValidSessionId, SessionManager } from \"../core/session-manager.ts\";\nimport {\n\tacceptToolEffectResult,\n\tinspectToolEffect,\n\tmarkToolEffectFailed,\n\ttype ToolEffectResolutionResult,\n\ttype ToolEffectResolutionTarget,\n} from \"../core/tool-effect-journal.ts\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\n\ntype ToolEffectsAction = \"inspect\" | \"mark-failed\" | \"accept-result\";\n\ninterface ToolEffectsCommand {\n\taction: ToolEffectsAction;\n\tsession: string;\n\teffectId: string;\n\tsessionDir?: string;\n\tresultFile?: string;\n}\n\nexport interface ToolEffectsCommandOptions {\n\tcwd: string;\n\tdefaultSessionDir?: string;\n}\n\nfunction printToolEffectsHelp(): void {\n\tconsole.log(`${chalk.bold(`${APP_NAME} tool-effects`)} - explicitly resolve an interrupted tool effect\n\n${chalk.bold(\"Usage:\")}\n ${APP_NAME} tool-effects inspect --session <path|exact-id> --effect-id <sha256>\n ${APP_NAME} tool-effects mark-failed --session <path|exact-id> --effect-id <sha256>\n ${APP_NAME} tool-effects accept-result --session <path|exact-id> --effect-id <sha256> <json-file>\n\n${chalk.bold(\"Options:\")}\n --session <path|exact-id> Exact session path or complete session ID\n --effect-id <sha256> Exact effect ID reported by the recovery error\n --session-dir <dir> Override the session lookup directory\n --help, -h Show this help\n\nmark-failed records an error result without re-executing the tool. External side effects may already have occurred.\naccept-result requires a complete ToolResultMessage JSON object whose toolCallId and toolName match the effect.\nNeither command displays stored tool arguments or accepted result content.`);\n}\n\nfunction requireOptionValue(args: string[], index: number, option: string): string {\n\tconst value = args[index + 1];\n\tif (value === undefined || value.startsWith(\"-\")) {\n\t\tthrow new Error(`${option} requires a value`);\n\t}\n\treturn value;\n}\n\nfunction setOnce(current: string | undefined, value: string, option: string): string {\n\tif (current !== undefined) {\n\t\tthrow new Error(`${option} may only be specified once`);\n\t}\n\treturn value;\n}\n\nfunction parseToolEffectsCommand(args: string[]): ToolEffectsCommand | \"help\" {\n\tif (args.includes(\"--help\") || args.includes(\"-h\")) return \"help\";\n\tconst action = args[1];\n\tif (action !== \"inspect\" && action !== \"mark-failed\" && action !== \"accept-result\") {\n\t\tthrow new Error(\"tool-effects requires one action: inspect, mark-failed, or accept-result\");\n\t}\n\n\tlet session: string | undefined;\n\tlet effectId: string | undefined;\n\tlet sessionDir: string | undefined;\n\tconst positional: string[] = [];\n\tfor (let index = 2; index < args.length; index++) {\n\t\tconst argument = args[index];\n\t\tif (argument === \"--session\") {\n\t\t\tsession = setOnce(session, requireOptionValue(args, index, argument), argument);\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--effect-id\") {\n\t\t\teffectId = setOnce(effectId, requireOptionValue(args, index, argument), argument);\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--session-dir\") {\n\t\t\tsessionDir = setOnce(sessionDir, requireOptionValue(args, index, argument), argument);\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument.startsWith(\"-\")) {\n\t\t\tthrow new Error(`Unknown tool-effects option: ${argument}`);\n\t\t}\n\t\tpositional.push(argument);\n\t}\n\n\tif (!session) throw new Error(\"tool-effects requires --session <path|exact-id>\");\n\tif (!effectId) throw new Error(\"tool-effects requires --effect-id <sha256>\");\n\tif (action === \"accept-result\") {\n\t\tif (positional.length !== 1) {\n\t\t\tthrow new Error(\"tool-effects accept-result requires exactly one JSON file\");\n\t\t}\n\t} else if (positional.length > 0) {\n\t\tthrow new Error(`tool-effects ${action} does not accept positional arguments`);\n\t}\n\treturn {\n\t\taction,\n\t\tsession,\n\t\teffectId,\n\t\tsessionDir,\n\t\tresultFile: positional[0],\n\t};\n}\n\nasync function openExactSession(\n\tsessionArgument: string,\n\tcwd: string,\n\tsessionDir: string | undefined,\n): Promise<SessionManager> {\n\tif (sessionArgument.includes(\"/\") || sessionArgument.includes(\"\\\\\") || sessionArgument.endsWith(\".jsonl\")) {\n\t\treturn SessionManager.open(resolvePath(sessionArgument, cwd), sessionDir);\n\t}\n\n\tassertValidSessionId(sessionArgument);\n\tconst matches = [\n\t\t...(await SessionManager.list(cwd, sessionDir)),\n\t\t...(await SessionManager.listAll(sessionDir)),\n\t].filter((session) => session.id === sessionArgument);\n\tconst uniquePaths = [...new Set(matches.map((session) => normalizePath(session.path)))];\n\tif (uniquePaths.length === 0) {\n\t\tthrow new Error(`No session found with exact id '${sessionArgument}'`);\n\t}\n\tif (uniquePaths.length > 1) {\n\t\tthrow new Error(`Multiple sessions have exact id '${sessionArgument}'; use an exact session path`);\n\t}\n\treturn SessionManager.open(uniquePaths[0], sessionDir);\n}\n\nfunction printTarget(target: ToolEffectResolutionTarget): void {\n\tconsole.log(`sessionPath: ${target.sessionPath}`);\n\tconsole.log(`sessionId: ${target.sessionId}`);\n\tconsole.log(`journalPath: ${target.journalPath}`);\n\tconsole.log(`effectId: ${target.effectId}`);\n\tconsole.log(`toolCallId: ${target.toolCallId}`);\n\tconsole.log(`toolName: ${target.toolName}`);\n\tconsole.log(`phase: ${target.phase}`);\n}\n\nfunction printResolution(result: ToolEffectResolutionResult): void {\n\tprintTarget(result.target);\n\tconsole.log(`sessionEntryId: ${result.sessionEntryId}`);\n\tconsole.log(`resolution: ${result.alreadyResolved ? \"already-recorded\" : \"recorded\"}`);\n}\n\nexport async function handleToolEffectsCommand(args: string[], options: ToolEffectsCommandOptions): Promise<boolean> {\n\tif (args[0] !== \"tool-effects\") return false;\n\ttry {\n\t\tconst command = parseToolEffectsCommand(args);\n\t\tif (command === \"help\") {\n\t\t\tprintToolEffectsHelp();\n\t\t\treturn true;\n\t\t}\n\t\tconst sessionDir = command.sessionDir ? resolvePath(command.sessionDir, options.cwd) : options.defaultSessionDir;\n\t\tconst session = await openExactSession(command.session, options.cwd, sessionDir);\n\t\tif (command.action === \"inspect\") {\n\t\t\tprintTarget(inspectToolEffect(session, command.effectId));\n\t\t\treturn true;\n\t\t}\n\t\tif (command.action === \"mark-failed\") {\n\t\t\tprintResolution(markToolEffectFailed(session, command.effectId));\n\t\t\tconsole.log(\"The tool was not re-executed; external side effects may already have occurred.\");\n\t\t\treturn true;\n\t\t}\n\t\tif (!command.resultFile) {\n\t\t\tthrow new Error(\"tool-effects accept-result requires a JSON file\");\n\t\t}\n\t\tconst resultPath = resolvePath(command.resultFile, options.cwd);\n\t\tlet result: unknown;\n\t\ttry {\n\t\t\tresult = JSON.parse(readFileSync(resultPath, \"utf8\")) as unknown;\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Could not read accepted result JSON from ${resultPath}`, { cause: error });\n\t\t}\n\t\tprintResolution(acceptToolEffectResult(session, command.effectId, result));\n\t\treturn true;\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\tprocess.exitCode = 1;\n\t\treturn true;\n\t}\n}\n"]}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
declare const COMPACT_STATE_VERSION = 1;
|
|
2
|
-
interface PiServerCompactStateBase {
|
|
3
|
-
version: typeof COMPACT_STATE_VERSION;
|
|
4
|
-
sequence: number;
|
|
5
|
-
timestamp: number;
|
|
6
|
-
}
|
|
7
|
-
export interface PiServerPendingCompactState extends PiServerCompactStateBase {
|
|
8
|
-
kind: "compact";
|
|
9
|
-
serverHash: string;
|
|
10
|
-
sessionId: string;
|
|
11
|
-
operationId: string;
|
|
12
|
-
requestHash: string;
|
|
13
|
-
baseStaticContextHash: string;
|
|
14
|
-
baseTreeHash: string;
|
|
15
|
-
baseEntryCount: number;
|
|
16
|
-
baseLeafId: string | null;
|
|
17
|
-
baseRevision: number;
|
|
18
|
-
observation?: PiServerCompactObservationState;
|
|
19
|
-
}
|
|
20
|
-
export interface PiServerAppliedCompactState extends PiServerCompactStateBase {
|
|
21
|
-
kind: "applied";
|
|
22
|
-
operationId: string;
|
|
23
|
-
requestHash: string;
|
|
24
|
-
entryId: string;
|
|
25
|
-
entryHash: string;
|
|
26
|
-
updatedTreeHash: string;
|
|
27
|
-
updatedLeafId: string;
|
|
28
|
-
updatedRevision: number;
|
|
29
|
-
resolution: "tree_applied";
|
|
30
|
-
}
|
|
31
|
-
export interface PiServerTerminalCompactState extends PiServerCompactStateBase {
|
|
32
|
-
kind: "terminal";
|
|
33
|
-
operationId: string;
|
|
34
|
-
requestHash: string;
|
|
35
|
-
httpStatus: number;
|
|
36
|
-
error: string;
|
|
37
|
-
operationDisposition: "terminal" | "not_started";
|
|
38
|
-
status: "failed" | "rejected";
|
|
39
|
-
resolution: "terminal_failed_observed" | "server_rejected";
|
|
40
|
-
}
|
|
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" | "server_authoritative_missing";
|
|
43
|
-
export interface WritePiServerPendingCompactInput {
|
|
44
|
-
serverHash: string;
|
|
45
|
-
sessionId: string;
|
|
46
|
-
operationId: string;
|
|
47
|
-
requestHash: string;
|
|
48
|
-
baseStaticContextHash: string;
|
|
49
|
-
baseTreeHash: string;
|
|
50
|
-
baseEntryCount: number;
|
|
51
|
-
baseLeafId: string | null;
|
|
52
|
-
baseRevision: number;
|
|
53
|
-
timestamp?: number;
|
|
54
|
-
}
|
|
55
|
-
export interface WritePiServerAppliedCompactInput {
|
|
56
|
-
operationId: string;
|
|
57
|
-
requestHash: string;
|
|
58
|
-
entryId: string;
|
|
59
|
-
entryHash: string;
|
|
60
|
-
updatedTreeHash: string;
|
|
61
|
-
updatedLeafId: string;
|
|
62
|
-
updatedRevision: number;
|
|
63
|
-
timestamp?: number;
|
|
64
|
-
}
|
|
65
|
-
export interface WritePiServerTerminalCompactInput {
|
|
66
|
-
operationId: string;
|
|
67
|
-
requestHash: string;
|
|
68
|
-
httpStatus: number;
|
|
69
|
-
error: string;
|
|
70
|
-
operationDisposition: "terminal" | "not_started";
|
|
71
|
-
status: "failed" | "rejected";
|
|
72
|
-
timestamp?: number;
|
|
73
|
-
}
|
|
74
|
-
export declare function getPiServerCompactStatePath(sessionFile: string): string;
|
|
75
|
-
export declare function readPiServerPendingCompact(path: string): PiServerPendingCompactState | undefined;
|
|
76
|
-
export declare function writePiServerPendingCompact(path: string, input: WritePiServerPendingCompactInput): PiServerPendingCompactState;
|
|
77
|
-
export declare function writePiServerAppliedCompact(path: string, input: WritePiServerAppliedCompactInput): PiServerAppliedCompactState;
|
|
78
|
-
export declare function writePiServerTerminalCompact(path: string, input: WritePiServerTerminalCompactInput): PiServerTerminalCompactState;
|
|
79
|
-
export declare function acknowledgePiServerPendingCompact(path: string, operationId: string, resolution?: PiServerCompactAcknowledgementResolution, timestamp?: number): void;
|
|
80
|
-
export {};
|
|
81
|
-
//# sourceMappingURL=pi-server-compact-state.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
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"]}
|