{"version":3,"file":"legacy-v3.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/legacy-v3.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA6B,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAG9E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,KAAK,EAA+C,cAAc,EAAE,MAAM,cAAc,CAAC;AAGhG,OAAO,EAAE,KAAK,qBAAqB,EAA2B,MAAM,YAAY,CAAC;AACjF,OAAO,EAGN,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,MAAM,YAAY,CAAC;AAmGpB,MAAM,WAAW,yBAAyB;IACzC,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,aAAa,EAAE,KAAK,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,wBAAwB,GAAG,IAAI,CAAC,oBAAoB,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC;AAalF,wBAAsB,0BAA0B,CAC/C,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,wBAAwB,CAAC,CAanC;AAED,wBAAsB,uBAAuB,CAC5C,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,kBAAkB,CAAC,CAM7B;AAkWD,uFAAuF;AACvF,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,yBAAyB,CAoBlG","sourcesContent":["import type { ImageContent, TextContent, Usage } from \"@earendil-works/pi-ai\";\nimport { uuidv7 } from \"@earendil-works/pi-ai/utils/uuid\";\nimport type { AgentMessage, ThinkingLevel } from \"../../../types.ts\";\nimport type { Context } from \"../../context.ts\";\nimport { createBranchSummaryMessage, createCompactionSummaryMessage } from \"../../messages.ts\";\nimport type { FileSystem } from \"../../types.ts\";\nimport { addUsage, emptyUsage } from \"../../utils/usage.ts\";\nimport type { CommittedEntryWrite, CommittedValueSetWrite, CommittedWrite } from \"../commit.ts\";\nimport type { JsonValue, LaneConfiguration } from \"../types.ts\";\nimport { branchTip, entryLabel, laneConfig, laneState, sessionName } from \"../values.ts\";\nimport { type LegacyV3SessionHeader, parseJsonlSessionHeader } from \"./codec.ts\";\nimport {\n\tJSONL_FORMAT_VERSION,\n\tJSONL_STORAGE_VERSION,\n\ttype JsonlSessionMetadata,\n\ttype JsonlStorageHeader,\n} from \"./types.ts\";\n\ninterface LegacyV3EntryBase {\n\ttype: string;\n\tid: string;\n\tparentId: string | null;\n\ttimestamp: string;\n}\n\ninterface LegacyV3MessageEntry extends LegacyV3EntryBase {\n\ttype: \"message\";\n\tmessage: AgentMessage;\n}\n\ninterface LegacyV3CustomEntry extends LegacyV3EntryBase {\n\ttype: \"custom\";\n\tcustomType: string;\n\tdata?: JsonValue;\n}\n\ninterface LegacyV3CustomMessageEntry extends LegacyV3EntryBase {\n\ttype: \"custom_message\";\n\tcustomType: string;\n\tcontent: string | (TextContent | ImageContent)[];\n\tdetails?: unknown;\n\tdisplay: boolean;\n}\n\ninterface LegacyV3BranchSummaryEntry extends LegacyV3EntryBase {\n\ttype: \"branch_summary\";\n\tfromId: string;\n\tsummary: string;\n\tdetails?: JsonValue;\n\tusage?: Usage;\n\tfromHook?: boolean;\n}\n\ninterface LegacyV3CompactionEntry extends LegacyV3EntryBase {\n\ttype: \"compaction\";\n\tsummary: string;\n\tfirstKeptEntryId: string;\n\ttokensBefore: number;\n\tdetails?: JsonValue;\n\tusage?: Usage;\n\tfromHook?: boolean;\n}\n\ninterface LegacyV3ModelChangeEntry extends LegacyV3EntryBase {\n\ttype: \"model_change\";\n\tprovider: string;\n\tmodelId: string;\n}\n\ninterface LegacyV3ThinkingLevelChangeEntry extends LegacyV3EntryBase {\n\ttype: \"thinking_level_change\";\n\tthinkingLevel: string;\n}\n\ninterface LegacyV3ActiveToolsChangeEntry extends LegacyV3EntryBase {\n\ttype: \"active_tools_change\";\n\tactiveToolNames: string[];\n}\n\ninterface LegacyV3SessionInfoEntry extends LegacyV3EntryBase {\n\ttype: \"session_info\";\n\tname?: string;\n}\n\ninterface LegacyV3LabelEntry extends LegacyV3EntryBase {\n\ttype: \"label\";\n\ttargetId: string;\n\tlabel?: string;\n}\n\ninterface ImportedCustomMessage {\n\trole: \"custom\";\n\tcustomType: string;\n\tcontent: string | (TextContent | ImageContent)[];\n\tdetails?: unknown;\n\tdisplay: boolean;\n\ttimestamp: number;\n}\n\ntype RetainedLegacyV3Entry =\n\t| LegacyV3MessageEntry\n\t| LegacyV3CustomEntry\n\t| LegacyV3CustomMessageEntry\n\t| LegacyV3BranchSummaryEntry\n\t| LegacyV3CompactionEntry;\n\ntype DiscardedLegacyV3Entry =\n\t| LegacyV3ModelChangeEntry\n\t| LegacyV3ThinkingLevelChangeEntry\n\t| LegacyV3ActiveToolsChangeEntry\n\t| LegacyV3SessionInfoEntry\n\t| LegacyV3LabelEntry;\n\ntype LegacyV3Entry = RetainedLegacyV3Entry | DiscardedLegacyV3Entry;\n\nexport interface NormalizedLegacyV3Records {\n\twrites: CommittedWrite[];\n\timportedUsage: Usage;\n\tnextSeq: number;\n}\n\ntype JsonlSessionMetadataBase = Omit<JsonlSessionMetadata, \"path\" | \"modifiedAt\">;\n\nasync function resolveLegacyV3ParentSessionId(\n\tfileSystem: FileSystem,\n\tparentSessionPath: string,\n\tcontext: Context,\n): Promise<string | undefined> {\n\tconst lines = await fileSystem.readTextLines(parentSessionPath, { maxLines: 1 }, context);\n\tif (!lines.ok || lines.value[0] === undefined) return undefined;\n\tconst parsed = parseJsonlSessionHeader(lines.value[0]);\n\treturn parsed.ok ? parsed.value.header.id : undefined;\n}\n\nexport async function metadataFromLegacyV3Header(\n\tfileSystem: FileSystem,\n\theader: LegacyV3SessionHeader,\n\tcontext: Context,\n): Promise<JsonlSessionMetadataBase> {\n\tconst metadata: JsonlSessionMetadataBase = {\n\t\tid: header.id,\n\t\tcreatedAt: Date.parse(header.timestamp),\n\t\tstorageVersion: JSONL_STORAGE_VERSION,\n\t\tcwd: header.cwd,\n\t};\n\tif (header.parentSession !== undefined) {\n\t\tconst parentSessionId = await resolveLegacyV3ParentSessionId(fileSystem, header.parentSession, context);\n\t\tif (parentSessionId !== undefined) metadata.parentSessionId = parentSessionId;\n\t\telse metadata.legacyParentSessionPath = header.parentSession;\n\t}\n\treturn metadata;\n}\n\nexport async function normalizeLegacyV3Header(\n\tfileSystem: FileSystem,\n\theader: LegacyV3SessionHeader,\n\tcontext: Context,\n): Promise<JsonlStorageHeader> {\n\treturn {\n\t\tv: JSONL_FORMAT_VERSION,\n\t\tkind: \"header\",\n\t\t...(await metadataFromLegacyV3Header(fileSystem, header, context)),\n\t};\n}\n\nfunction parseLegacyV3Entry(line: string, lineNumber: number): LegacyV3Entry {\n\tlet entry: LegacyV3Entry;\n\ttry {\n\t\tentry = JSON.parse(line) as LegacyV3Entry;\n\t} catch (error) {\n\t\tthrow new Error(`Invalid legacy v3 JSONL record at line ${lineNumber}: not valid JSON`, { cause: error });\n\t}\n\tconst recordType: unknown = entry.type;\n\tif (\n\t\trecordType !== \"message\" &&\n\t\trecordType !== \"custom\" &&\n\t\trecordType !== \"custom_message\" &&\n\t\trecordType !== \"branch_summary\" &&\n\t\trecordType !== \"compaction\" &&\n\t\trecordType !== \"model_change\" &&\n\t\trecordType !== \"thinking_level_change\" &&\n\t\trecordType !== \"active_tools_change\" &&\n\t\trecordType !== \"session_info\" &&\n\t\trecordType !== \"label\"\n\t) {\n\t\tthrow new Error(`Unsupported legacy v3 record type at line ${lineNumber}: ${String(recordType)}`);\n\t}\n\treturn entry;\n}\n\nfunction importedCustomMessage(entry: LegacyV3CustomMessageEntry): AgentMessage {\n\tconst message: ImportedCustomMessage = {\n\t\trole: \"custom\",\n\t\tcustomType: entry.customType,\n\t\tcontent: entry.content,\n\t\tdetails: entry.details,\n\t\tdisplay: entry.display,\n\t\ttimestamp: Date.parse(entry.timestamp),\n\t};\n\t// The coding-agent CustomAgentMessages declaration merge is not visible in this package.\n\treturn message as unknown as AgentMessage;\n}\n\nfunction isRetainedEntry(entry: LegacyV3Entry): entry is RetainedLegacyV3Entry {\n\treturn (\n\t\tentry.type !== \"model_change\" &&\n\t\tentry.type !== \"thinking_level_change\" &&\n\t\tentry.type !== \"active_tools_change\" &&\n\t\tentry.type !== \"session_info\" &&\n\t\tentry.type !== \"label\"\n\t);\n}\n\nclass RetainedIdResolver {\n\t/** Caches the reminted ID of each discarded legacy node's nearest retained ancestor, or null. */\n\tprivate resolvedIds = new Map<string, string | null>();\n\tprivate entriesById: Map<string, LegacyV3Entry>;\n\tprivate remintedIds: Map<string, string>;\n\n\t/**\n\t * @param entriesById Complete inventory of legacy physical nodes, including discarded nodes.\n\t * @param remintedIds Legacy-to-current ID map containing retained nodes only.\n\t */\n\tconstructor(entriesById: Map<string, LegacyV3Entry>, remintedIds: Map<string, string>) {\n\t\tthis.entriesById = entriesById;\n\t\tthis.remintedIds = remintedIds;\n\t}\n\n\t/**\n\t * Resolve a legacy node to its reminted ID, or to its nearest retained ancestor when discarded.\n\t * Returns null when the reference is null or no retained ancestor exists.\n\t */\n\tresolve(legacyId: string | null): string | null {\n\t\tconst traversedIds: string[] = [];\n\t\tconst visitedIds = new Set<string>();\n\t\tlet currentId = legacyId;\n\t\tlet resolvedId: string | null = null;\n\n\t\twhile (currentId !== null) {\n\t\t\tconst remintedId = this.remintedIds.get(currentId);\n\t\t\tif (remintedId !== undefined) {\n\t\t\t\tresolvedId = remintedId;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (this.resolvedIds.has(currentId)) {\n\t\t\t\tresolvedId = this.resolvedIds.get(currentId) ?? null;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (visitedIds.has(currentId)) throw new Error(`Cycle in legacy v3 parent chain at entry: ${currentId}`);\n\t\t\tvisitedIds.add(currentId);\n\t\t\tconst entry = this.entriesById.get(currentId);\n\t\t\tif (entry === undefined) throw new Error(`Missing legacy v3 entry reference: ${currentId}`);\n\t\t\ttraversedIds.push(currentId);\n\t\t\tcurrentId = entry.parentId;\n\t\t}\n\n\t\tfor (const traversedId of traversedIds) this.resolvedIds.set(traversedId, resolvedId);\n\t\treturn resolvedId;\n\t}\n}\n\nfunction requireRetainedId(resolver: RetainedIdResolver, legacyId: string): string {\n\tconst importedId = resolver.resolve(legacyId);\n\tif (importedId === null) throw new Error(`Legacy v3 entry reference has no retained ancestor: ${legacyId}`);\n\treturn importedId;\n}\n\nfunction resolveBranchSummaryFromId(resolver: RetainedIdResolver, legacyFromId: string): string | null {\n\t// Legacy branchWithSummary() encoded a root source as the \"root\" sentinel instead of null.\n\treturn legacyFromId === \"root\" ? null : resolver.resolve(legacyFromId);\n}\n\nfunction projectContextMessages(entry: LegacyV3Entry, resolver: RetainedIdResolver): AgentMessage[] {\n\tswitch (entry.type) {\n\t\tcase \"message\":\n\t\t\treturn [entry.message];\n\t\tcase \"custom_message\":\n\t\t\treturn [importedCustomMessage(entry)];\n\t\tcase \"branch_summary\":\n\t\t\treturn entry.summary\n\t\t\t\t? [\n\t\t\t\t\t\tcreateBranchSummaryMessage(\n\t\t\t\t\t\t\tentry.summary,\n\t\t\t\t\t\t\tresolveBranchSummaryFromId(resolver, entry.fromId),\n\t\t\t\t\t\t\tentry.timestamp,\n\t\t\t\t\t\t),\n\t\t\t\t\t]\n\t\t\t\t: [];\n\t\tcase \"compaction\":\n\t\t\treturn [createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp)];\n\t\tcase \"custom\":\n\t\tcase \"model_change\":\n\t\tcase \"thinking_level_change\":\n\t\tcase \"active_tools_change\":\n\t\tcase \"session_info\":\n\t\tcase \"label\":\n\t\t\treturn [];\n\t}\n}\n\nfunction materializeRetainedTail(\n\tcompaction: LegacyV3CompactionEntry,\n\tentriesById: ReadonlyMap<string, LegacyV3Entry>,\n\tresolver: RetainedIdResolver,\n): AgentMessage[] {\n\tconst reversedTail: LegacyV3Entry[] = [];\n\tconst visited = new Set<string>();\n\tlet currentId = compaction.parentId;\n\twhile (currentId !== null) {\n\t\tif (visited.has(currentId)) throw new Error(`Cycle in legacy v3 parent chain at entry: ${currentId}`);\n\t\tvisited.add(currentId);\n\t\tconst entry = entriesById.get(currentId);\n\t\tif (entry === undefined) throw new Error(`Missing legacy v3 parent entry: ${currentId}`);\n\t\treversedTail.push(entry);\n\t\tif (currentId === compaction.firstKeptEntryId) {\n\t\t\treturn reversedTail.reverse().flatMap((tailEntry) => projectContextMessages(tailEntry, resolver));\n\t\t}\n\t\tcurrentId = entry.parentId;\n\t}\n\tthrow new Error(\n\t\t`Legacy v3 compaction ${compaction.id} firstKeptEntryId is not on its parent branch: ${compaction.firstKeptEntryId}`,\n\t);\n}\n\nfunction normalizeRetainedEntry(\n\tentry: RetainedLegacyV3Entry,\n\tseq: number,\n\tentriesById: ReadonlyMap<string, LegacyV3Entry>,\n\tresolver: RetainedIdResolver,\n): CommittedEntryWrite {\n\tconst committedBase = {\n\t\tkind: \"entry\" as const,\n\t\tid: requireRetainedId(resolver, entry.id),\n\t\tparentId: resolver.resolve(entry.parentId),\n\t\tseq,\n\t\ttimestamp: Date.parse(entry.timestamp),\n\t};\n\tif (entry.type === \"message\") {\n\t\treturn { ...committedBase, type: \"message\", message: entry.message };\n\t}\n\tif (entry.type === \"custom_message\") {\n\t\treturn {\n\t\t\t...committedBase,\n\t\t\ttype: \"message\",\n\t\t\tmessage: importedCustomMessage(entry),\n\t\t};\n\t}\n\tif (entry.type === \"branch_summary\") {\n\t\treturn {\n\t\t\t...committedBase,\n\t\t\ttype: \"branch_summary\",\n\t\t\tfromId: resolveBranchSummaryFromId(resolver, entry.fromId),\n\t\t\tsummary: entry.summary,\n\t\t\tdetails: entry.details,\n\t\t\tusage: entry.usage,\n\t\t\tfromHook: entry.fromHook ?? false,\n\t\t};\n\t}\n\tif (entry.type === \"compaction\") {\n\t\treturn {\n\t\t\t...committedBase,\n\t\t\ttype: \"compaction\",\n\t\t\tsummary: entry.summary,\n\t\t\tretainedTail: materializeRetainedTail(entry, entriesById, resolver),\n\t\t\ttokensBefore: entry.tokensBefore,\n\t\t\tdetails: entry.details,\n\t\t\tusage: entry.usage,\n\t\t\tfromHook: entry.fromHook ?? false,\n\t\t};\n\t}\n\treturn {\n\t\t...committedBase,\n\t\ttype: \"custom\",\n\t\tcustomType: entry.customType,\n\t\tdata: entry.data,\n\t};\n}\n\nconst THINKING_LEVELS = new Set<ThinkingLevel>([\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"]);\n\nfunction selectedConfiguration(\n\tentriesById: ReadonlyMap<string, LegacyV3Entry>,\n\tselectedId: string | null,\n): LaneConfiguration | undefined {\n\tlet model: LaneConfiguration[\"model\"] | undefined;\n\tlet thinkingLevel: ThinkingLevel | undefined;\n\tlet activeToolNames: string[] | undefined;\n\tlet sawModel = false;\n\tlet sawThinkingLevel = false;\n\tlet sawActiveToolNames = false;\n\tconst visited = new Set<string>();\n\tlet currentId = selectedId;\n\twhile (currentId !== null && (!sawModel || !sawThinkingLevel || !sawActiveToolNames)) {\n\t\tif (visited.has(currentId)) throw new Error(`Cycle in legacy v3 parent chain at entry: ${currentId}`);\n\t\tvisited.add(currentId);\n\t\tconst entry = entriesById.get(currentId);\n\t\tif (entry === undefined) throw new Error(`Missing legacy v3 entry reference: ${currentId}`);\n\t\tif (entry.type === \"model_change\" && !sawModel) {\n\t\t\tsawModel = true;\n\t\t\tif (\n\t\t\t\ttypeof entry.provider === \"string\" &&\n\t\t\t\tentry.provider.length !== 0 &&\n\t\t\t\ttypeof entry.modelId === \"string\" &&\n\t\t\t\tentry.modelId.length !== 0\n\t\t\t) {\n\t\t\t\tmodel = { provider: entry.provider, modelId: entry.modelId };\n\t\t\t}\n\t\t} else if (entry.type === \"thinking_level_change\" && !sawThinkingLevel) {\n\t\t\tsawThinkingLevel = true;\n\t\t\tif (typeof entry.thinkingLevel === \"string\" && THINKING_LEVELS.has(entry.thinkingLevel as ThinkingLevel)) {\n\t\t\t\tthinkingLevel = entry.thinkingLevel as ThinkingLevel;\n\t\t\t}\n\t\t} else if (entry.type === \"active_tools_change\" && !sawActiveToolNames) {\n\t\t\tsawActiveToolNames = true;\n\t\t\tif (Array.isArray(entry.activeToolNames) && entry.activeToolNames.every((name) => typeof name === \"string\")) {\n\t\t\t\tactiveToolNames = [...entry.activeToolNames];\n\t\t\t}\n\t\t}\n\t\tcurrentId = entry.parentId;\n\t}\n\tif (model === undefined || thinkingLevel === undefined) return undefined;\n\treturn { model, thinkingLevel, activeToolNames: activeToolNames ?? [] };\n}\n\nfunction normalizeLegacyV3Values(\n\tentries: readonly LegacyV3Entry[],\n\tentriesById: ReadonlyMap<string, LegacyV3Entry>,\n\tresolver: RetainedIdResolver,\n\tfirstSeq: number,\n): CommittedValueSetWrite[] {\n\tconst writes: CommittedValueSetWrite[] = [];\n\tlet latestSessionInfo: LegacyV3SessionInfoEntry | undefined;\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"session_info\") latestSessionInfo = entry;\n\t}\n\tif (latestSessionInfo?.name) {\n\t\twrites.push({\n\t\t\tkind: \"value\",\n\t\t\top: \"set\",\n\t\t\tseq: firstSeq + writes.length,\n\t\t\tnamespace: sessionName.namespace,\n\t\t\tkey: sessionName.key,\n\t\t\tvalue: latestSessionInfo.name,\n\t\t});\n\t}\n\n\tconst labels = new Map<string, string>();\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"label\") continue;\n\t\tconst targetId = resolver.resolve(entry.targetId);\n\t\t// Labels have no current address when their target has no retained ancestor.\n\t\tif (targetId === null) continue;\n\t\t// Legacy v3 treated both undefined and the empty string as clearing a label.\n\t\tif (entry.label) labels.set(targetId, entry.label);\n\t\telse labels.delete(targetId);\n\t}\n\tfor (const [targetId, label] of labels) {\n\t\tconst address = entryLabel(targetId);\n\t\twrites.push({\n\t\t\tkind: \"value\",\n\t\t\top: \"set\",\n\t\t\tseq: firstSeq + writes.length,\n\t\t\tnamespace: address.namespace,\n\t\t\tkey: address.key,\n\t\t\tvalue: label,\n\t\t});\n\t}\n\n\tconst finalEntry = entries.at(-1);\n\tconst tipAddress = branchTip(\"main\");\n\twrites.push({\n\t\tkind: \"value\",\n\t\top: \"set\",\n\t\tseq: firstSeq + writes.length,\n\t\tnamespace: tipAddress.namespace,\n\t\tkey: tipAddress.key,\n\t\tvalue: finalEntry === undefined ? null : resolver.resolve(finalEntry.id),\n\t});\n\tconst configuration = selectedConfiguration(entriesById, finalEntry?.id ?? null);\n\tif (configuration !== undefined) {\n\t\tconst configAddress = laneConfig(\"main\");\n\t\twrites.push({\n\t\t\tkind: \"value\",\n\t\t\top: \"set\",\n\t\t\tseq: firstSeq + writes.length,\n\t\t\tnamespace: configAddress.namespace,\n\t\t\tkey: configAddress.key,\n\t\t\tvalue: configuration,\n\t\t});\n\t\tconst stateAddress = laneState(\"main\");\n\t\twrites.push({\n\t\t\tkind: \"value\",\n\t\t\top: \"set\",\n\t\t\tseq: firstSeq + writes.length,\n\t\t\tnamespace: stateAddress.namespace,\n\t\t\tkey: stateAddress.key,\n\t\t\tvalue: { currentOperationId: null, lastOperationId: null, inbox: [] },\n\t\t});\n\t}\n\treturn writes;\n}\n\nfunction aggregateImportedUsage(entries: readonly LegacyV3Entry[]): Usage {\n\tlet aggregate = emptyUsage();\n\tfor (const entry of entries) {\n\t\tlet usage: Usage | undefined;\n\t\tif (entry.type === \"message\") {\n\t\t\tif (entry.message.role === \"assistant\") usage = entry.message.usage;\n\t\t\telse if (entry.message.role === \"toolResult\") usage = entry.message.usage;\n\t\t} else if (entry.type === \"compaction\" || entry.type === \"branch_summary\") {\n\t\t\tusage = entry.usage;\n\t\t}\n\t\tif (usage !== undefined) aggregate = addUsage(aggregate, usage);\n\t}\n\treturn aggregate;\n}\n\n/** Normalize the currently supported v3 records without touching their source file. */\nexport function normalizeLegacyV3Records(recordLines: readonly string[]): NormalizedLegacyV3Records {\n\tconst entries = recordLines.map((line, index) => parseLegacyV3Entry(line, index + 2));\n\tconst entriesById = new Map<string, LegacyV3Entry>();\n\tfor (const entry of entries) {\n\t\tif (entriesById.has(entry.id)) throw new Error(`Duplicate legacy v3 entry id: ${entry.id}`);\n\t\tentriesById.set(entry.id, entry);\n\t}\n\tconst retainedEntries = entries.filter(isRetainedEntry);\n\tconst remintedIds = new Map(retainedEntries.map((entry) => [entry.id, uuidv7(Date.parse(entry.timestamp))]));\n\tconst resolver = new RetainedIdResolver(entriesById, remintedIds);\n\tconst entryWrites = retainedEntries.map((entry, index) =>\n\t\tnormalizeRetainedEntry(entry, index + 1, entriesById, resolver),\n\t);\n\tconst valueWrites = normalizeLegacyV3Values(entries, entriesById, resolver, entryWrites.length + 1);\n\tconst writes: CommittedWrite[] = [...entryWrites, ...valueWrites];\n\treturn {\n\t\twrites,\n\t\timportedUsage: aggregateImportedUsage(entries),\n\t\tnextSeq: writes.length + 1,\n\t};\n}\n"]}