@geminixiang/mikan 1.0.0-beta.47 → 1.0.0-beta.49

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 (78) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/adapters/discord/context.d.ts.map +1 -1
  3. package/dist/adapters/discord/context.js +2 -6
  4. package/dist/adapters/discord/context.js.map +1 -1
  5. package/dist/adapters/telegram/context.d.ts.map +1 -1
  6. package/dist/adapters/telegram/context.js +2 -4
  7. package/dist/adapters/telegram/context.js.map +1 -1
  8. package/dist/agent/presenter.d.ts +3 -10
  9. package/dist/agent/presenter.d.ts.map +1 -1
  10. package/dist/agent/presenter.js +35 -32
  11. package/dist/agent/presenter.js.map +1 -1
  12. package/dist/agent/runner.d.ts.map +1 -1
  13. package/dist/agent/runner.js +16 -22
  14. package/dist/agent/runner.js.map +1 -1
  15. package/dist/agent/types.d.ts +4 -6
  16. package/dist/agent/types.d.ts.map +1 -1
  17. package/dist/agent/types.js.map +1 -1
  18. package/dist/cli/boot.js +1 -1
  19. package/dist/cli/boot.js.map +1 -1
  20. package/dist/cli/sessions.d.ts.map +1 -1
  21. package/dist/cli/sessions.js +18 -13
  22. package/dist/cli/sessions.js.map +1 -1
  23. package/dist/config.d.ts.map +1 -1
  24. package/dist/config.js +4 -2
  25. package/dist/config.js.map +1 -1
  26. package/dist/env-manifest.js +2 -2
  27. package/dist/env-manifest.js.map +1 -1
  28. package/dist/harness/models.d.ts.map +1 -1
  29. package/dist/harness/models.js +1 -4
  30. package/dist/harness/models.js.map +1 -1
  31. package/dist/harness/runner.d.ts.map +1 -1
  32. package/dist/harness/runner.js +2 -2
  33. package/dist/harness/runner.js.map +1 -1
  34. package/dist/harness/session-store.d.ts +3 -45
  35. package/dist/harness/session-store.d.ts.map +1 -1
  36. package/dist/harness/session-store.js +293 -198
  37. package/dist/harness/session-store.js.map +1 -1
  38. package/dist/harness/subagent-runner.d.ts.map +1 -1
  39. package/dist/harness/subagent-runner.js +1 -8
  40. package/dist/harness/subagent-runner.js.map +1 -1
  41. package/dist/harness/types.d.ts +11 -11
  42. package/dist/harness/types.d.ts.map +1 -1
  43. package/dist/harness/types.js.map +1 -1
  44. package/dist/main.js +5 -0
  45. package/dist/main.js.map +1 -1
  46. package/dist/mcp/catalog.d.ts.map +1 -1
  47. package/dist/mcp/catalog.js +0 -88
  48. package/dist/mcp/catalog.js.map +1 -1
  49. package/dist/mcp/open-connector.d.ts +1 -1
  50. package/dist/mcp/open-connector.d.ts.map +1 -1
  51. package/dist/mcp/open-connector.js +23 -22
  52. package/dist/mcp/open-connector.js.map +1 -1
  53. package/dist/runtime/conversation-runtime.js +1 -0
  54. package/dist/runtime/conversation-runtime.js.map +1 -1
  55. package/dist/runtime/session-lifecycle.d.ts +0 -1
  56. package/dist/runtime/session-lifecycle.d.ts.map +1 -1
  57. package/dist/runtime/session-lifecycle.js +1 -4
  58. package/dist/runtime/session-lifecycle.js.map +1 -1
  59. package/dist/runtime/types.d.ts +3 -0
  60. package/dist/runtime/types.d.ts.map +1 -1
  61. package/dist/runtime/types.js.map +1 -1
  62. package/dist/sessions/migrate-pi-084.d.ts +8 -0
  63. package/dist/sessions/migrate-pi-084.d.ts.map +1 -0
  64. package/dist/sessions/migrate-pi-084.js +353 -0
  65. package/dist/sessions/migrate-pi-084.js.map +1 -0
  66. package/dist/sessions/migrate-v3.d.ts +0 -5
  67. package/dist/sessions/migrate-v3.d.ts.map +1 -1
  68. package/dist/sessions/migrate-v3.js +129 -51
  69. package/dist/sessions/migrate-v3.js.map +1 -1
  70. package/dist/sessions/types.d.ts +6 -1
  71. package/dist/sessions/types.d.ts.map +1 -1
  72. package/dist/sessions/types.js.map +1 -1
  73. package/dist/types.d.ts +5 -1
  74. package/dist/types.d.ts.map +1 -1
  75. package/dist/types.js.map +1 -1
  76. package/dist/web/session-view/portal.js +0 -16
  77. package/dist/web/session-view/portal.js.map +1 -1
  78. package/package.json +3 -3
@@ -0,0 +1,353 @@
1
+ import { existsSync, linkSync, lstatSync, readFileSync, readdirSync, renameSync, rmSync, statSync, } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { SessionStore } from "../harness/session-store.js";
4
+ import { atomicWritePrivateFile, isRecord } from "../utils/file-guards.js";
5
+ function firstLine(filePath) {
6
+ return readFileSync(filePath, "utf8").split("\n", 1)[0]?.trim() ?? "";
7
+ }
8
+ function parseHeader(value, filePath) {
9
+ if (!isRecord(value) ||
10
+ value.kind !== "header" ||
11
+ value.version !== 4 ||
12
+ typeof value.id !== "string" ||
13
+ typeof value.createdAt !== "number" ||
14
+ typeof value.cwd !== "string") {
15
+ throw new Error(`Invalid Pi 0.84 session header: ${filePath}`);
16
+ }
17
+ return value;
18
+ }
19
+ function isCurrentHeader(value) {
20
+ return (isRecord(value) &&
21
+ value.kind === "header" &&
22
+ value.v === 4 &&
23
+ typeof value.storageVersion === "number");
24
+ }
25
+ export function isPi084SessionFile(filePath) {
26
+ try {
27
+ const value = JSON.parse(firstLine(filePath));
28
+ return isRecord(value) && value.kind === "header" && value.version === 4;
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ }
34
+ function jsonValue(value) {
35
+ return JSON.parse(JSON.stringify(value));
36
+ }
37
+ function convertEntry(record) {
38
+ const { kind: _kind, lane: _lane, ...raw } = record;
39
+ const base = {
40
+ id: String(raw.id),
41
+ parentId: typeof raw.parentId === "string" ? raw.parentId : null,
42
+ seq: Number(raw.seq),
43
+ timestamp: Number(raw.timestamp),
44
+ };
45
+ switch (raw.type) {
46
+ case "message":
47
+ return { ...base, type: "message", message: raw.message };
48
+ case "compaction":
49
+ return {
50
+ ...base,
51
+ type: "compaction",
52
+ summary: String(raw.summary ?? ""),
53
+ retainedTail: jsonValue(raw.retainedTail ?? []),
54
+ tokensBefore: Number(raw.tokensBefore ?? 0),
55
+ fromHook: false,
56
+ ...(raw.details !== undefined ? { details: jsonValue(raw.details) } : {}),
57
+ ...(raw.usage !== undefined ? { usage: raw.usage } : {}),
58
+ };
59
+ case "branch_summary":
60
+ return {
61
+ ...base,
62
+ type: "branch_summary",
63
+ fromId: typeof raw.fromId === "string" ? raw.fromId : null,
64
+ summary: String(raw.summary ?? ""),
65
+ fromHook: false,
66
+ ...(raw.details !== undefined ? { details: jsonValue(raw.details) } : {}),
67
+ ...(raw.usage !== undefined ? { usage: raw.usage } : {}),
68
+ };
69
+ case "custom":
70
+ return {
71
+ ...base,
72
+ type: "custom",
73
+ customType: String(raw.customType ?? "legacy"),
74
+ ...(raw.data !== undefined ? { data: jsonValue(raw.data) } : {}),
75
+ };
76
+ case "model_change":
77
+ case "thinking_level_change":
78
+ case "active_tools_change": {
79
+ const { id: _id, parentId: _parentId, seq: _seq, timestamp: _timestamp, type, ...data } = raw;
80
+ return {
81
+ ...base,
82
+ type: "custom",
83
+ customType: `mikan.pi084.${String(type)}`,
84
+ data: jsonValue(data),
85
+ };
86
+ }
87
+ default:
88
+ throw new Error(`Unsupported Pi 0.84 entry type: ${String(raw.type)}`);
89
+ }
90
+ }
91
+ function parsePi084Session(filePath) {
92
+ const source = readFileSync(filePath, "utf8");
93
+ const lines = source.split("\n");
94
+ if (lines.at(-1) === "")
95
+ lines.pop();
96
+ let header;
97
+ const entries = [];
98
+ const branchTips = new Map([["main", null]]);
99
+ const labels = new Map();
100
+ const records = [];
101
+ let name;
102
+ for (const [index, line] of lines.entries()) {
103
+ let value;
104
+ try {
105
+ value = JSON.parse(line);
106
+ }
107
+ catch (error) {
108
+ if (index === lines.length - 1 && !source.endsWith("\n"))
109
+ break;
110
+ throw new Error(`Invalid JSON on line ${index + 1}: ${filePath}`, { cause: error });
111
+ }
112
+ if (index === 0) {
113
+ header = parseHeader(value, filePath);
114
+ continue;
115
+ }
116
+ if (!isRecord(value))
117
+ throw new Error(`Invalid mutation on line ${index + 1}: ${filePath}`);
118
+ if (value.kind === "entry") {
119
+ const entry = convertEntry(value);
120
+ entries.push(entry);
121
+ if (typeof value.lane === "string")
122
+ branchTips.set(value.lane, entry.id);
123
+ }
124
+ else if (value.kind === "lane" && typeof value.lane === "string") {
125
+ branchTips.set(value.lane, typeof value.leafId === "string" ? value.leafId : null);
126
+ }
127
+ else if (value.kind === "fact" && value.fact === "name") {
128
+ name = typeof value.name === "string" ? value.name : undefined;
129
+ }
130
+ else if (value.kind === "fact" &&
131
+ value.fact === "label" &&
132
+ typeof value.targetId === "string") {
133
+ if (typeof value.label === "string")
134
+ labels.set(value.targetId, value.label);
135
+ else
136
+ labels.delete(value.targetId);
137
+ }
138
+ else if (value.kind === "record") {
139
+ records.push(jsonValue(value));
140
+ }
141
+ else {
142
+ throw new Error(`Unknown Pi 0.84 mutation on line ${index + 1}: ${filePath}`);
143
+ }
144
+ }
145
+ if (!header)
146
+ throw new Error(`Missing Pi 0.84 session header: ${filePath}`);
147
+ let metadata = header.metadata ? structuredClone(header.metadata) : undefined;
148
+ if (header.legacyParentSessionPath && metadata?.parentSessionPath === undefined) {
149
+ (metadata ??= {}).parentSessionPath = header.legacyParentSessionPath;
150
+ }
151
+ return { header, entries, branchTips, name, labels, records, metadata };
152
+ }
153
+ function encodeCurrentSession(source) {
154
+ const header = {
155
+ v: 4,
156
+ kind: "header",
157
+ id: source.header.id,
158
+ storageVersion: 1,
159
+ createdAt: source.header.createdAt,
160
+ cwd: source.header.cwd,
161
+ ...(source.header.parentSessionId !== undefined
162
+ ? { parentSessionId: source.header.parentSessionId }
163
+ : {}),
164
+ ...(source.header.legacyParentSessionPath !== undefined
165
+ ? { legacyParentSessionPath: source.header.legacyParentSessionPath }
166
+ : {}),
167
+ };
168
+ const lines = [JSON.stringify(header)];
169
+ let seq = 0;
170
+ for (const entry of source.entries) {
171
+ lines.push(JSON.stringify({ kind: "entry", ...entry, seq: ++seq }));
172
+ }
173
+ for (const [branch, tip] of source.branchTips) {
174
+ lines.push(JSON.stringify({
175
+ kind: "value",
176
+ op: "set",
177
+ seq: ++seq,
178
+ namespace: "pi.branch.tip",
179
+ key: branch,
180
+ value: tip,
181
+ }));
182
+ }
183
+ if (source.name !== undefined) {
184
+ lines.push(JSON.stringify({
185
+ kind: "value",
186
+ op: "set",
187
+ seq: ++seq,
188
+ namespace: "pi.session.name",
189
+ key: "",
190
+ value: source.name,
191
+ }));
192
+ }
193
+ for (const [targetId, label] of source.labels) {
194
+ lines.push(JSON.stringify({
195
+ kind: "value",
196
+ op: "set",
197
+ seq: ++seq,
198
+ namespace: "pi.entry.label",
199
+ key: targetId,
200
+ value: label,
201
+ }));
202
+ }
203
+ if (source.metadata !== undefined) {
204
+ lines.push(JSON.stringify({
205
+ kind: "value",
206
+ op: "set",
207
+ seq: ++seq,
208
+ namespace: "mikan",
209
+ key: "metadata",
210
+ value: source.metadata,
211
+ }));
212
+ }
213
+ for (const record of source.records) {
214
+ lines.push(JSON.stringify({
215
+ kind: "list",
216
+ op: "append",
217
+ seq: ++seq,
218
+ namespace: "mikan.pi084.records",
219
+ key: "",
220
+ value: record,
221
+ }));
222
+ }
223
+ return `${lines.join("\n")}\n`;
224
+ }
225
+ function currentWrites(path) {
226
+ return readFileSync(path, "utf8")
227
+ .split("\n")
228
+ .slice(1)
229
+ .filter(Boolean)
230
+ .flatMap((line) => {
231
+ const parsed = JSON.parse(line);
232
+ return Array.isArray(parsed) ? parsed : [parsed];
233
+ });
234
+ }
235
+ function valueWritten(writes, namespace, key) {
236
+ return writes.findLast((write) => write.kind === "value" &&
237
+ write.op === "set" &&
238
+ write.namespace === namespace &&
239
+ write.key === key)?.value;
240
+ }
241
+ async function verifyCandidate(path, source) {
242
+ const inspection = await SessionStore.inspect(path);
243
+ const entries = await inspection.getEntries();
244
+ if (entries.length !== source.entries.length)
245
+ throw new Error("entry count changed");
246
+ for (const [index, entry] of entries.entries()) {
247
+ const original = source.entries[index];
248
+ if (!original)
249
+ throw new Error(`entry ${index + 1} changed`);
250
+ const { seq: _entrySeq, ...entryData } = entry;
251
+ const { seq: _originalSeq, ...originalData } = original;
252
+ if (JSON.stringify(entryData) !== JSON.stringify(originalData)) {
253
+ throw new Error(`entry ${index + 1} changed`);
254
+ }
255
+ }
256
+ const expectedTip = source.branchTips.get("main") ?? null;
257
+ const branch = await inspection.getBranch();
258
+ if ((branch.at(-1)?.id ?? null) !== expectedTip)
259
+ throw new Error("main branch tip changed");
260
+ if ((await inspection.getSessionName()) !== source.name)
261
+ throw new Error("session name changed");
262
+ const header = inspection.getHeader();
263
+ if (header.id !== source.header.id ||
264
+ header.timestamp !== new Date(source.header.createdAt).toISOString()) {
265
+ throw new Error("session identity changed");
266
+ }
267
+ const writes = currentWrites(path);
268
+ for (const [branchName, tip] of source.branchTips) {
269
+ if (valueWritten(writes, "pi.branch.tip", branchName) !== tip) {
270
+ throw new Error(`branch ${branchName} tip changed`);
271
+ }
272
+ }
273
+ for (const [targetId, label] of source.labels) {
274
+ if (valueWritten(writes, "pi.entry.label", targetId) !== label) {
275
+ throw new Error(`label ${targetId} changed`);
276
+ }
277
+ }
278
+ if (JSON.stringify(valueWritten(writes, "mikan", "metadata")) !== JSON.stringify(source.metadata)) {
279
+ throw new Error("mikan metadata changed");
280
+ }
281
+ const auditRecords = writes.filter((write) => write.kind === "list" && write.namespace === "mikan.pi084.records");
282
+ if (auditRecords.length !== source.records.length)
283
+ throw new Error("audit records changed");
284
+ }
285
+ function backupAlreadyLinked(sourcePath, backupPath) {
286
+ if (!existsSync(backupPath))
287
+ return false;
288
+ const source = statSync(sourcePath);
289
+ const backup = statSync(backupPath);
290
+ if (source.dev === backup.dev && source.ino === backup.ino)
291
+ return true;
292
+ throw new Error(`Backup already exists: ${backupPath}`);
293
+ }
294
+ export async function migratePi084SessionFile(filePath, options) {
295
+ let headerValue;
296
+ try {
297
+ headerValue = JSON.parse(firstLine(filePath));
298
+ }
299
+ catch {
300
+ return { file: filePath, status: "not-pi-084" };
301
+ }
302
+ if (isCurrentHeader(headerValue))
303
+ return { file: filePath, status: "already-current" };
304
+ if (!isPi084SessionFile(filePath))
305
+ return { file: filePath, status: "not-pi-084" };
306
+ const sourceBytes = readFileSync(filePath);
307
+ const source = parsePi084Session(filePath);
308
+ if (options?.dryRun)
309
+ return { file: filePath, status: "migrated", detail: "dry run" };
310
+ const tempPath = `${filePath}.pi-085.tmp`;
311
+ const backupPath = `${filePath}.pi-084.bak`;
312
+ const backupLinked = backupAlreadyLinked(filePath, backupPath);
313
+ atomicWritePrivateFile(tempPath, encodeCurrentSession(source));
314
+ try {
315
+ await verifyCandidate(tempPath, source);
316
+ if (!readFileSync(filePath).equals(sourceBytes))
317
+ throw new Error("source changed during migration");
318
+ if (!backupLinked)
319
+ linkSync(filePath, backupPath);
320
+ renameSync(tempPath, filePath);
321
+ }
322
+ catch (error) {
323
+ rmSync(tempPath, { force: true });
324
+ throw new Error(`Migration failed for ${filePath}`, { cause: error });
325
+ }
326
+ return { file: filePath, status: "migrated" };
327
+ }
328
+ export function findPi084SessionFiles(root) {
329
+ if (!existsSync(root))
330
+ return [];
331
+ const found = [];
332
+ const walk = (dir) => {
333
+ for (const name of readdirSync(dir)) {
334
+ const path = join(dir, name);
335
+ let stats;
336
+ try {
337
+ stats = lstatSync(path);
338
+ }
339
+ catch {
340
+ continue;
341
+ }
342
+ if (stats.isSymbolicLink())
343
+ continue;
344
+ if (stats.isDirectory())
345
+ walk(path);
346
+ else if (name.endsWith(".jsonl") && isPi084SessionFile(path))
347
+ found.push(path);
348
+ }
349
+ };
350
+ walk(root);
351
+ return found;
352
+ }
353
+ //# sourceMappingURL=migrate-pi-084.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate-pi-084.js","sourceRoot":"","sources":["../../src/sessions/migrate-pi-084.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n existsSync,\n linkSync,\n lstatSync,\n readFileSync,\n readdirSync,\n renameSync,\n rmSync,\n statSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AgentMessage, Entry, JsonValue } from \"@earendil-works/pi-agent-core\";\nimport type { Usage } from \"@earendil-works/pi-ai\";\nimport { SessionStore } from \"../harness/session-store.js\";\nimport { atomicWritePrivateFile, isRecord } from \"../utils/file-guards.js\";\nimport type { Pi084MigrationResult } from \"./types.js\";\nexport type { Pi084MigrationResult } from \"./types.js\";\n\ninterface Pi084Header {\n kind: \"header\";\n version: 4;\n id: string;\n createdAt: number;\n cwd: string;\n parentSessionId?: string;\n legacyParentSessionPath?: string;\n metadata?: Record<string, JsonValue>;\n}\n\ninterface ParsedPi084Session {\n header: Pi084Header;\n entries: Entry[];\n branchTips: Map<string, string | null>;\n name?: string;\n labels: Map<string, string>;\n records: JsonValue[];\n metadata?: Record<string, JsonValue>;\n}\n\nfunction firstLine(filePath: string): string {\n return readFileSync(filePath, \"utf8\").split(\"\\n\", 1)[0]?.trim() ?? \"\";\n}\n\nfunction parseHeader(value: unknown, filePath: string): Pi084Header {\n if (\n !isRecord(value) ||\n value.kind !== \"header\" ||\n value.version !== 4 ||\n typeof value.id !== \"string\" ||\n typeof value.createdAt !== \"number\" ||\n typeof value.cwd !== \"string\"\n ) {\n throw new Error(`Invalid Pi 0.84 session header: ${filePath}`);\n }\n return value as unknown as Pi084Header;\n}\n\nfunction isCurrentHeader(value: unknown): boolean {\n return (\n isRecord(value) &&\n value.kind === \"header\" &&\n value.v === 4 &&\n typeof value.storageVersion === \"number\"\n );\n}\n\nexport function isPi084SessionFile(filePath: string): boolean {\n try {\n const value: unknown = JSON.parse(firstLine(filePath));\n return isRecord(value) && value.kind === \"header\" && value.version === 4;\n } catch {\n return false;\n }\n}\n\nfunction jsonValue(value: unknown): JsonValue {\n return JSON.parse(JSON.stringify(value)) as JsonValue;\n}\n\nfunction convertEntry(record: Record<string, unknown>): Entry {\n const { kind: _kind, lane: _lane, ...raw } = record;\n const base = {\n id: String(raw.id),\n parentId: typeof raw.parentId === \"string\" ? raw.parentId : null,\n seq: Number(raw.seq),\n timestamp: Number(raw.timestamp),\n };\n switch (raw.type) {\n case \"message\":\n return { ...base, type: \"message\", message: raw.message as AgentMessage };\n case \"compaction\":\n return {\n ...base,\n type: \"compaction\",\n summary: String(raw.summary ?? \"\"),\n retainedTail: jsonValue(raw.retainedTail ?? []) as unknown as AgentMessage[],\n tokensBefore: Number(raw.tokensBefore ?? 0),\n fromHook: false,\n ...(raw.details !== undefined ? { details: jsonValue(raw.details) } : {}),\n ...(raw.usage !== undefined ? { usage: raw.usage as Usage } : {}),\n };\n case \"branch_summary\":\n return {\n ...base,\n type: \"branch_summary\",\n fromId: typeof raw.fromId === \"string\" ? raw.fromId : null,\n summary: String(raw.summary ?? \"\"),\n fromHook: false,\n ...(raw.details !== undefined ? { details: jsonValue(raw.details) } : {}),\n ...(raw.usage !== undefined ? { usage: raw.usage as Usage } : {}),\n };\n case \"custom\":\n return {\n ...base,\n type: \"custom\",\n customType: String(raw.customType ?? \"legacy\"),\n ...(raw.data !== undefined ? { data: jsonValue(raw.data) } : {}),\n };\n case \"model_change\":\n case \"thinking_level_change\":\n case \"active_tools_change\": {\n const { id: _id, parentId: _parentId, seq: _seq, timestamp: _timestamp, type, ...data } = raw;\n return {\n ...base,\n type: \"custom\",\n customType: `mikan.pi084.${String(type)}`,\n data: jsonValue(data),\n };\n }\n default:\n throw new Error(`Unsupported Pi 0.84 entry type: ${String(raw.type)}`);\n }\n}\n\nfunction parsePi084Session(filePath: string): ParsedPi084Session {\n const source = readFileSync(filePath, \"utf8\");\n const lines = source.split(\"\\n\");\n if (lines.at(-1) === \"\") lines.pop();\n let header: Pi084Header | undefined;\n const entries: Entry[] = [];\n const branchTips = new Map<string, string | null>([[\"main\", null]]);\n const labels = new Map<string, string>();\n const records: JsonValue[] = [];\n let name: string | undefined;\n\n for (const [index, line] of lines.entries()) {\n let value: unknown;\n try {\n value = JSON.parse(line);\n } catch (error) {\n if (index === lines.length - 1 && !source.endsWith(\"\\n\")) break;\n throw new Error(`Invalid JSON on line ${index + 1}: ${filePath}`, { cause: error });\n }\n if (index === 0) {\n header = parseHeader(value, filePath);\n continue;\n }\n if (!isRecord(value)) throw new Error(`Invalid mutation on line ${index + 1}: ${filePath}`);\n if (value.kind === \"entry\") {\n const entry = convertEntry(value);\n entries.push(entry);\n if (typeof value.lane === \"string\") branchTips.set(value.lane, entry.id);\n } else if (value.kind === \"lane\" && typeof value.lane === \"string\") {\n branchTips.set(value.lane, typeof value.leafId === \"string\" ? value.leafId : null);\n } else if (value.kind === \"fact\" && value.fact === \"name\") {\n name = typeof value.name === \"string\" ? value.name : undefined;\n } else if (\n value.kind === \"fact\" &&\n value.fact === \"label\" &&\n typeof value.targetId === \"string\"\n ) {\n if (typeof value.label === \"string\") labels.set(value.targetId, value.label);\n else labels.delete(value.targetId);\n } else if (value.kind === \"record\") {\n records.push(jsonValue(value));\n } else {\n throw new Error(`Unknown Pi 0.84 mutation on line ${index + 1}: ${filePath}`);\n }\n }\n if (!header) throw new Error(`Missing Pi 0.84 session header: ${filePath}`);\n let metadata = header.metadata ? structuredClone(header.metadata) : undefined;\n if (header.legacyParentSessionPath && metadata?.parentSessionPath === undefined) {\n (metadata ??= {}).parentSessionPath = header.legacyParentSessionPath;\n }\n return { header, entries, branchTips, name, labels, records, metadata };\n}\n\nfunction encodeCurrentSession(source: ParsedPi084Session): string {\n const header = {\n v: 4,\n kind: \"header\",\n id: source.header.id,\n storageVersion: 1,\n createdAt: source.header.createdAt,\n cwd: source.header.cwd,\n ...(source.header.parentSessionId !== undefined\n ? { parentSessionId: source.header.parentSessionId }\n : {}),\n ...(source.header.legacyParentSessionPath !== undefined\n ? { legacyParentSessionPath: source.header.legacyParentSessionPath }\n : {}),\n };\n const lines = [JSON.stringify(header)];\n let seq = 0;\n for (const entry of source.entries) {\n lines.push(JSON.stringify({ kind: \"entry\", ...entry, seq: ++seq }));\n }\n for (const [branch, tip] of source.branchTips) {\n lines.push(\n JSON.stringify({\n kind: \"value\",\n op: \"set\",\n seq: ++seq,\n namespace: \"pi.branch.tip\",\n key: branch,\n value: tip,\n }),\n );\n }\n if (source.name !== undefined) {\n lines.push(\n JSON.stringify({\n kind: \"value\",\n op: \"set\",\n seq: ++seq,\n namespace: \"pi.session.name\",\n key: \"\",\n value: source.name,\n }),\n );\n }\n for (const [targetId, label] of source.labels) {\n lines.push(\n JSON.stringify({\n kind: \"value\",\n op: \"set\",\n seq: ++seq,\n namespace: \"pi.entry.label\",\n key: targetId,\n value: label,\n }),\n );\n }\n if (source.metadata !== undefined) {\n lines.push(\n JSON.stringify({\n kind: \"value\",\n op: \"set\",\n seq: ++seq,\n namespace: \"mikan\",\n key: \"metadata\",\n value: source.metadata,\n }),\n );\n }\n for (const record of source.records) {\n lines.push(\n JSON.stringify({\n kind: \"list\",\n op: \"append\",\n seq: ++seq,\n namespace: \"mikan.pi084.records\",\n key: \"\",\n value: record,\n }),\n );\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction currentWrites(path: string): Array<Record<string, unknown>> {\n return readFileSync(path, \"utf8\")\n .split(\"\\n\")\n .slice(1)\n .filter(Boolean)\n .flatMap((line) => {\n const parsed = JSON.parse(line) as Record<string, unknown> | Array<Record<string, unknown>>;\n return Array.isArray(parsed) ? parsed : [parsed];\n });\n}\n\nfunction valueWritten(\n writes: Array<Record<string, unknown>>,\n namespace: string,\n key: string,\n): unknown {\n return writes.findLast(\n (write) =>\n write.kind === \"value\" &&\n write.op === \"set\" &&\n write.namespace === namespace &&\n write.key === key,\n )?.value;\n}\n\nasync function verifyCandidate(path: string, source: ParsedPi084Session): Promise<void> {\n const inspection = await SessionStore.inspect(path);\n const entries = await inspection.getEntries();\n if (entries.length !== source.entries.length) throw new Error(\"entry count changed\");\n for (const [index, entry] of entries.entries()) {\n const original = source.entries[index];\n if (!original) throw new Error(`entry ${index + 1} changed`);\n const { seq: _entrySeq, ...entryData } = entry;\n const { seq: _originalSeq, ...originalData } = original;\n if (JSON.stringify(entryData) !== JSON.stringify(originalData)) {\n throw new Error(`entry ${index + 1} changed`);\n }\n }\n const expectedTip = source.branchTips.get(\"main\") ?? null;\n const branch = await inspection.getBranch();\n if ((branch.at(-1)?.id ?? null) !== expectedTip) throw new Error(\"main branch tip changed\");\n if ((await inspection.getSessionName()) !== source.name) throw new Error(\"session name changed\");\n const header = inspection.getHeader();\n if (\n header.id !== source.header.id ||\n header.timestamp !== new Date(source.header.createdAt).toISOString()\n ) {\n throw new Error(\"session identity changed\");\n }\n const writes = currentWrites(path);\n for (const [branchName, tip] of source.branchTips) {\n if (valueWritten(writes, \"pi.branch.tip\", branchName) !== tip) {\n throw new Error(`branch ${branchName} tip changed`);\n }\n }\n for (const [targetId, label] of source.labels) {\n if (valueWritten(writes, \"pi.entry.label\", targetId) !== label) {\n throw new Error(`label ${targetId} changed`);\n }\n }\n if (\n JSON.stringify(valueWritten(writes, \"mikan\", \"metadata\")) !== JSON.stringify(source.metadata)\n ) {\n throw new Error(\"mikan metadata changed\");\n }\n const auditRecords = writes.filter(\n (write) => write.kind === \"list\" && write.namespace === \"mikan.pi084.records\",\n );\n if (auditRecords.length !== source.records.length) throw new Error(\"audit records changed\");\n}\n\nfunction backupAlreadyLinked(sourcePath: string, backupPath: string): boolean {\n if (!existsSync(backupPath)) return false;\n const source = statSync(sourcePath);\n const backup = statSync(backupPath);\n if (source.dev === backup.dev && source.ino === backup.ino) return true;\n throw new Error(`Backup already exists: ${backupPath}`);\n}\n\nexport async function migratePi084SessionFile(\n filePath: string,\n options?: { dryRun?: boolean },\n): Promise<Pi084MigrationResult> {\n let headerValue: unknown;\n try {\n headerValue = JSON.parse(firstLine(filePath));\n } catch {\n return { file: filePath, status: \"not-pi-084\" };\n }\n if (isCurrentHeader(headerValue)) return { file: filePath, status: \"already-current\" };\n if (!isPi084SessionFile(filePath)) return { file: filePath, status: \"not-pi-084\" };\n const sourceBytes = readFileSync(filePath);\n const source = parsePi084Session(filePath);\n if (options?.dryRun) return { file: filePath, status: \"migrated\", detail: \"dry run\" };\n\n const tempPath = `${filePath}.pi-085.tmp`;\n const backupPath = `${filePath}.pi-084.bak`;\n const backupLinked = backupAlreadyLinked(filePath, backupPath);\n atomicWritePrivateFile(tempPath, encodeCurrentSession(source));\n try {\n await verifyCandidate(tempPath, source);\n if (!readFileSync(filePath).equals(sourceBytes))\n throw new Error(\"source changed during migration\");\n if (!backupLinked) linkSync(filePath, backupPath);\n renameSync(tempPath, filePath);\n } catch (error) {\n rmSync(tempPath, { force: true });\n throw new Error(`Migration failed for ${filePath}`, { cause: error });\n }\n return { file: filePath, status: \"migrated\" };\n}\n\nexport function findPi084SessionFiles(root: string): string[] {\n if (!existsSync(root)) return [];\n const found: string[] = [];\n const walk = (dir: string): void => {\n for (const name of readdirSync(dir)) {\n const path = join(dir, name);\n let stats;\n try {\n stats = lstatSync(path);\n } catch {\n continue;\n }\n if (stats.isSymbolicLink()) continue;\n if (stats.isDirectory()) walk(path);\n else if (name.endsWith(\".jsonl\") && isPi084SessionFile(path)) found.push(path);\n }\n };\n walk(root);\n return found;\n}\n"]}
@@ -2,11 +2,6 @@ import type { MigrateResult } from "./types.js";
2
2
  export type { MigrateResult } from "./types.js";
3
3
  /** Whether a file's first non-empty line is a v3 session header. */
4
4
  export declare function isV3SessionFile(filePath: string): boolean;
5
- /**
6
- * Migrate one session file in place. The migrated file is verified against
7
- * the v3 reference semantics before it replaces the original; the original
8
- * is preserved as `<file>.v3.bak`.
9
- */
10
5
  export declare function migrateSessionFile(filePath: string, options?: {
11
6
  dryRun?: boolean;
12
7
  }): Promise<MigrateResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"migrate-v3.d.ts","sourceRoot":"","sources":["../../src/sessions/migrate-v3.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA8DhD,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAUzD;AA6UD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7B,OAAO,CAAC,aAAa,CAAC,CAgBxB;AAED,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAqBzD"}
1
+ {"version":3,"file":"migrate-v3.d.ts","sourceRoot":"","sources":["../../src/sessions/migrate-v3.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA8DhD,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAUzD;AA6ZD,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7B,OAAO,CAAC,aAAa,CAAC,CAqBxB;AAED,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAsBzD"}