@averyyy/pi-server 0.82.1-piclient.9 → 0.84.1-piclient.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,IAAI,UAAU,EAA6C,MAAM,WAAW,CAAC;AAY/G,OAAO,EAGN,KAAK,OAAO,EAGZ,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,mBAAmB,EACxB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,OAAO,EAYN,KAAK,YAAY,EACjB,KAAK,oBAAoB,EAGzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAc5D,UAAU,iBAAiB;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,aAAa,CAAC,EAAE,oBAAoB,CAAC;IACrC,iBAAiB,CAAC,EAAE,OAAO,EAAE,CAAC;IAC9B,cAAc,CAAC,EAAE,OAAO,EAAE,CAAC;CAC3B;AA4GD,UAAU,cAAc;IACvB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,OAAO,EAAE,mBAAmB,CAAC;CAC7B;AAgJD,wBAAgB,oBAAoB,CACnC,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,IAAI,EAAE,iBAAiB,GACrB,cAAc,CAEhB;AA0VD,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,gBAAgB,GAAG,mBAAmB,CAAC,GACnE,OAAO,CAOT;AA4KD,wBAAgB,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,UAAU,CAgHjF;AA+DD,wBAAgB,WAAW,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,UAAU,CAO9E","sourcesContent":["import { createServer, type Server as HttpServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { createRequire } from \"node:module\";\nimport {\n\ttype CompactionPreparationOptions,\n\ttype CompactionSettings,\n\ttype CompactResult,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\ttype ProxyAssistantMessageEvent,\n\tprepareCompaction,\n\ttype SessionTreeEntry,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tcreateModels,\n\tcreateProvider,\n\ttype Message,\n\ttype Model,\n\ttype SimpleStreamOptions,\n} from \"@earendil-works/pi-ai\";\nimport { streamSimple } from \"@earendil-works/pi-ai/compat\";\nimport type { ServerConfig } from \"./config.ts\";\nimport { loadConfig } from \"./config.ts\";\nimport { encodeErrorEvent, encodeProxyEvent } from \"./event-encoding.ts\";\nimport { ReceiveUploadError, receiveUpload } from \"./receive-upload.ts\";\nimport { CHUNK_ENDPOINT, type RequestChunkBody, receiveRequestChunk } from \"./request-chunks.ts\";\nimport { deletePersistedSession, loadPersistedSessions, savePersistedSession } from \"./session-persistence.ts\";\nimport {\n\tappendCompactionEntry,\n\tappendMessages,\n\tappendSessionEntries,\n\tdeleteSession as deleteSessionFromStore,\n\tdropLastAssistantError,\n\tgetOrCreateSession,\n\tgetSession,\n\tgetSessionBranch,\n\tlistSessions,\n\treplaceMessages,\n\treplaceSessionTree,\n\ttype SessionState,\n\ttype SessionStaticContext,\n\tsetStaticContext,\n\tswitchSessionLeaf,\n} from \"./session-store.ts\";\n\nexport { loadConfig, type ServerConfig } from \"./config.ts\";\n\ninterface PackageMetadata {\n\tversion: string;\n}\n\nconst packageMetadata = createRequire(import.meta.url)(\"../package.json\") as PackageMetadata;\nconst PI_SERVER_VERSION = packageMetadata.version;\n\ninterface SessionInitBody {\n\tsessionId: string;\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface StreamRequestBody {\n\tsessionId: string;\n\trunId?: string;\n\tmodel: Model<any>;\n\toptions?: SimpleStreamOptions;\n\tstaticContext?: SessionStaticContext;\n\tephemeralMessages?: Message[];\n\tcontextOverlay?: Message[];\n}\n\ninterface SessionSyncBody {\n\tsessionId: string;\n\tmessages: Message[];\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface SessionAppendBody {\n\tsessionId: string;\n\tmessages: Message[];\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface SessionTreeSyncBody {\n\tsessionId: string;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface SessionTreeSwitchBody {\n\tsessionId: string;\n\tleafId: string | null;\n}\n\ninterface SessionCompactBody {\n\tsessionId: string;\n\tmodel: Model<any>;\n\toptions?: SimpleStreamOptions;\n\tsettings?: CompactionSettings;\n\tpreparation?: CompactionPreparationOptions;\n\tcustomInstructions?: string;\n\tbaseTreeHash?: string;\n\tfullResponse?: boolean;\n\tstreamResponse?: boolean;\n}\n\nfunction createRequestModels(model: Model<any>, options: SimpleStreamOptions) {\n\tconst models = createModels();\n\tmodels.setProvider(\n\t\tcreateProvider({\n\t\t\tid: model.provider,\n\t\t\tname: model.provider,\n\t\t\tmodels: [model],\n\t\t\tauth: {\n\t\t\t\tapiKey: {\n\t\t\t\t\tname: \"pi-server request auth\",\n\t\t\t\t\tresolve: async () => ({ auth: { apiKey: options.apiKey, headers: options.headers } }),\n\t\t\t\t},\n\t\t\t},\n\t\t\tapi: {\n\t\t\t\tstream: (requestModel, context, streamOptions) => streamSimple(requestModel, context, streamOptions),\n\t\t\t\tstreamSimple: (requestModel, context, streamOptions) => streamSimple(requestModel, context, streamOptions),\n\t\t\t},\n\t\t}),\n\t);\n\treturn models;\n}\n\ninterface SessionIdBody {\n\tsessionId: string;\n}\n\ninterface StreamRunRecord {\n\tsessionId: string;\n\trunId: string;\n\tstatus: \"running\" | \"completed\" | \"failed\";\n\tevents: ProxyAssistantMessageEvent[];\n\tmessage?: AssistantMessage;\n\terrorMessage?: string;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nconst STREAM_HEARTBEAT = \": keep-alive\\n\\n\";\nconst JSON_HEARTBEAT = \" \\n\";\nconst STREAM_HEARTBEAT_INTERVAL_MS = 25_000;\nconst streamRuns = new Map<string, StreamRunRecord>();\n\nfunction readBody(req: IncomingMessage): Promise<string> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst chunks: Buffer[] = [];\n\t\treq.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n\t\treq.on(\"end\", () => resolve(Buffer.concat(chunks).toString(\"utf-8\")));\n\t\treq.on(\"error\", reject);\n\t});\n}\n\nfunction sendJson(res: ServerResponse, status: number, body: unknown): void {\n\tconst data = JSON.stringify(body);\n\tres.writeHead(status, { \"Content-Type\": \"application/json\", \"Content-Length\": Buffer.byteLength(data) });\n\tres.end(data);\n}\n\nfunction logRequestError(req: IncomingMessage, error: unknown): void {\n\tconst message = error instanceof Error ? error.stack || error.message : String(error);\n\tconsole.error(`${req.method ?? \"UNKNOWN\"} ${req.url ?? \"/\"} failed: ${message}`);\n}\n\nfunction authenticate(config: ServerConfig, req: IncomingMessage): boolean {\n\tif (!config.authToken) return true;\n\tconst header = req.headers.authorization;\n\tif (!header) return false;\n\tconst token = header.startsWith(\"Bearer \") ? header.slice(7) : header;\n\treturn token === config.authToken;\n}\n\ninterface ResolvedStream {\n\tmodel: Model<any>;\n\toptions: SimpleStreamOptions;\n}\n\nfunction sessionResponseBody(session: SessionState) {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContextHash: session.staticContextHash,\n\t\ttreeHash: session.treeHash,\n\t\tmessageCount: session.messages.length,\n\t\tentryCount: session.entries.length,\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t};\n}\n\nfunction persistSession(config: ServerConfig, session: SessionState): void {\n\tsavePersistedSession(config.sessionStoreDir, session);\n}\n\nfunction runKey(sessionId: string, runId: string): string {\n\treturn `${sessionId}\\0${runId}`;\n}\n\nfunction getStreamRun(sessionId: string, runId: string): StreamRunRecord | undefined {\n\treturn streamRuns.get(runKey(sessionId, runId));\n}\n\nfunction startStreamRun(sessionId: string, runId: string): StreamRunRecord {\n\tconst existing = getStreamRun(sessionId, runId);\n\tif (existing?.status === \"completed\" || existing?.status === \"failed\") return existing;\n\tconst now = Date.now();\n\tconst run: StreamRunRecord = existing ?? {\n\t\tsessionId,\n\t\trunId,\n\t\tstatus: \"running\",\n\t\tevents: [],\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t};\n\trun.status = \"running\";\n\trun.updatedAt = now;\n\tstreamRuns.set(runKey(sessionId, runId), run);\n\treturn run;\n}\n\nfunction recordStreamRunEvent(run: StreamRunRecord | undefined, event: ProxyAssistantMessageEvent): void {\n\tif (!run) return;\n\trun.events.push(event);\n\trun.updatedAt = Date.now();\n}\n\nfunction completeStreamRun(run: StreamRunRecord | undefined, message: AssistantMessage): void {\n\tif (!run) return;\n\trun.status = \"completed\";\n\trun.message = message;\n\trun.errorMessage = undefined;\n\trun.updatedAt = Date.now();\n}\n\nfunction failStreamRun(run: StreamRunRecord | undefined, errorMessage: string): void {\n\tif (!run) return;\n\trun.status = \"failed\";\n\trun.errorMessage = errorMessage;\n\trun.updatedAt = Date.now();\n}\n\nfunction sessionHistoryFullResponseBody(session: SessionState, baseMessageCount: number) {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tstaticContextHash: session.staticContextHash,\n\t\ttreeHash: session.treeHash,\n\t\tmessageCount: session.messages.length,\n\t\tentryCount: session.entries.length,\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tentries: session.entries,\n\t\tbaseMessageCount,\n\t\tmessages: session.messages.slice(baseMessageCount),\n\t};\n}\n\nfunction sessionTreePatchResponseBody(\n\tsession: SessionState,\n\tbaseMessageCount: number,\n\tentriesFrom: number,\n\tbaseRevision: number | undefined,\n) {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tstaticContextHash: session.staticContextHash,\n\t\ttreeHash: session.treeHash,\n\t\tmessageCount: session.messages.length,\n\t\tentryCount: session.entries.length,\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tbaseMessageCount,\n\t\tmessages: session.messages.slice(baseMessageCount),\n\t\ttreePatch: {\n\t\t\tentriesFrom,\n\t\t\tbaseRevision,\n\t\t\tentries: session.entries.slice(entriesFrom),\n\t\t\tleafId: session.leafId,\n\t\t\trevision: session.revision,\n\t\t},\n\t};\n}\n\ntype PreparedCompaction = Parameters<typeof compact>[0];\n\ninterface PreparedSessionCompact {\n\tsession: SessionState;\n\tpreparation: PreparedCompaction;\n\toptions: SimpleStreamOptions;\n}\n\ninterface SessionCompactSuccessBody {\n\tsuccess: true;\n\tcompaction: CompactResult;\n\tcompactionEntry: SessionTreeEntry;\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash: string;\n\tmessageCount: number;\n\tentryCount: number;\n\tleafId: string | null;\n\trevision: number;\n\tstaticContext?: SessionStaticContext;\n\ttreePatch?: {\n\t\tbaseTreeHash: string;\n\t\tentriesFrom: number;\n\t\tentries: SessionTreeEntry[];\n\t\tleafId: string | null;\n\t\trevision: number;\n\t};\n\tentries?: SessionTreeEntry[];\n\tmessages?: Message[];\n}\n\ninterface SessionCompactHttpResponse {\n\tstatus: number;\n\tbody: { error: string } | SessionCompactSuccessBody;\n}\n\nexport function resolveStreamOptions(\n\t_config: ServerConfig,\n\tmodel: Model<any>,\n\tbody: StreamRequestBody,\n): ResolvedStream {\n\treturn { model, options: { ...(body.options ?? {}) } };\n}\n\nfunction handleSessionInit(config: ServerConfig, body: SessionInitBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t} else {\n\t\tgetOrCreateSession(body.sessionId);\n\t}\n\tconst session = getSession(body.sessionId)!;\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionUpdate(\n\tconfig: ServerConfig,\n\tbody: SessionInitBody & { staticContext: SessionStaticContext },\n\tres: ServerResponse,\n): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!body.staticContext) {\n\t\tsendJson(res, 400, { error: \"staticContext is required for update\" });\n\t\treturn;\n\t}\n\tsetStaticContext(body.sessionId, body.staticContext);\n\tconst session = getSession(body.sessionId)!;\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionSync(config: ServerConfig, body: SessionSyncBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.messages)) {\n\t\tsendJson(res, 400, { error: \"messages is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = replaceMessages(body.sessionId, body.messages);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionAppend(config: ServerConfig, body: SessionAppendBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.messages)) {\n\t\tsendJson(res, 400, { error: \"messages is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = appendMessages(body.sessionId, body.messages);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionTreeSync(config: ServerConfig, body: SessionTreeSyncBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.entries)) {\n\t\tsendJson(res, 400, { error: \"entries is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = replaceSessionTree(body.sessionId, body.entries, body.leafId ?? null);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionTreeAppend(config: ServerConfig, body: SessionTreeSyncBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.entries)) {\n\t\tsendJson(res, 400, { error: \"entries is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = appendSessionEntries(body.sessionId, body.entries, body.leafId ?? null);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionTreeSwitch(config: ServerConfig, body: SessionTreeSwitchBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tconst session = switchSessionLeaf(body.sessionId, body.leafId ?? null);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction prepareSessionCompact(body: SessionCompactBody): PreparedSessionCompact | SessionCompactHttpResponse {\n\tif (!body.sessionId) {\n\t\treturn { status: 400, body: { error: \"sessionId is required\" } };\n\t}\n\tif (!body.model) {\n\t\treturn { status: 400, body: { error: \"model is required\" } };\n\t}\n\n\tconst session = getSession(body.sessionId);\n\tif (!session) {\n\t\treturn { status: 404, body: { error: \"session not found\" } };\n\t}\n\n\tconst entries = getSessionBranch(session);\n\tconst preparationResult = prepareCompaction(entries, body.settings ?? DEFAULT_COMPACTION_SETTINGS, body.preparation);\n\tif (!preparationResult.ok) {\n\t\treturn { status: 400, body: { error: preparationResult.error.message } };\n\t}\n\tif (!preparationResult.value) {\n\t\treturn { status: 400, body: { error: \"Nothing to compact\" } };\n\t}\n\n\tconst options = body.options ?? {};\n\treturn {\n\t\tsession,\n\t\tpreparation: preparationResult.value,\n\t\toptions,\n\t};\n}\n\nasync function completeSessionCompact(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tprepared: PreparedSessionCompact,\n): Promise<SessionCompactHttpResponse> {\n\tconst result = await compact(\n\t\tprepared.preparation,\n\t\tcreateRequestModels(body.model, prepared.options),\n\t\tbody.model,\n\t\tbody.customInstructions,\n\t\tundefined,\n\t\tprepared.options.reasoning,\n\t);\n\tif (!result.ok) {\n\t\treturn { status: 500, body: { error: result.error.message } };\n\t}\n\n\tconst baseTreeHash = prepared.session.treeHash;\n\tconst baseEntryCount = prepared.session.entries.length;\n\tconst compaction = result.value;\n\tconst firstKeptEntryId = compaction.firstKeptEntryId;\n\tif (!firstKeptEntryId) {\n\t\treturn { status: 500, body: { error: \"Compaction result is missing firstKeptEntryId\" } };\n\t}\n\tconst normalizedCompaction = { ...compaction, firstKeptEntryId };\n\tconst { session: updatedSession, entry: compactionEntry } = appendCompactionEntry(\n\t\tbody.sessionId,\n\t\tnormalizedCompaction,\n\t);\n\tpersistSession(config, updatedSession);\n\tif (!body.fullResponse && body.baseTreeHash === baseTreeHash) {\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\tbody: {\n\t\t\t\tsuccess: true,\n\t\t\t\tcompaction: normalizedCompaction satisfies CompactResult,\n\t\t\t\tcompactionEntry,\n\t\t\t\t...sessionResponseBody(updatedSession),\n\t\t\t\tstaticContext: updatedSession.staticContext,\n\t\t\t\ttreePatch: {\n\t\t\t\t\tbaseTreeHash,\n\t\t\t\t\tentriesFrom: baseEntryCount,\n\t\t\t\t\tentries: [compactionEntry],\n\t\t\t\t\tleafId: updatedSession.leafId,\n\t\t\t\t\trevision: updatedSession.revision,\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\treturn {\n\t\tstatus: 200,\n\t\tbody: {\n\t\t\tsuccess: true,\n\t\t\tcompaction: normalizedCompaction satisfies CompactResult,\n\t\t\tcompactionEntry,\n\t\t\t...sessionResponseBody(updatedSession),\n\t\t\tstaticContext: updatedSession.staticContext,\n\t\t\tentries: updatedSession.entries,\n\t\t\tmessages: updatedSession.messages,\n\t\t},\n\t};\n}\n\nfunction writeServerSentEvent(res: ServerResponse, event: string, body: unknown): void {\n\tres.write(`event: ${event}\\n`);\n\tres.write(`data: ${JSON.stringify(body)}\\n\\n`);\n}\n\nasync function handleSessionCompactStream(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tprepared: PreparedSessionCompact,\n\tres: ServerResponse,\n): Promise<void> {\n\tres.writeHead(200, {\n\t\t\"Content-Type\": \"text/event-stream\",\n\t\t\"Cache-Control\": \"no-cache\",\n\t\tConnection: \"keep-alive\",\n\t});\n\tres.flushHeaders();\n\tres.write(STREAM_HEARTBEAT);\n\n\tconst heartbeat = setInterval(() => {\n\t\tif (!res.writableEnded) {\n\t\t\tres.write(STREAM_HEARTBEAT);\n\t\t}\n\t}, STREAM_HEARTBEAT_INTERVAL_MS);\n\theartbeat.unref();\n\n\ttry {\n\t\tconst result = await completeSessionCompact(config, body, prepared);\n\t\twriteServerSentEvent(res, result.status >= 400 ? \"error\" : \"result\", result.body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\twriteServerSentEvent(res, \"error\", { error: message });\n\t} finally {\n\t\tclearInterval(heartbeat);\n\t\tres.end();\n\t}\n}\n\nasync function handleSessionCompactJsonStream(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tprepared: PreparedSessionCompact,\n\tres: ServerResponse,\n): Promise<void> {\n\tres.writeHead(200, {\n\t\t\"Content-Type\": \"application/json\",\n\t\t\"Cache-Control\": \"no-cache\",\n\t\tConnection: \"keep-alive\",\n\t});\n\tres.flushHeaders();\n\tres.write(JSON_HEARTBEAT);\n\n\tconst heartbeat = setInterval(() => {\n\t\tif (!res.writableEnded) {\n\t\t\tres.write(JSON_HEARTBEAT);\n\t\t}\n\t}, STREAM_HEARTBEAT_INTERVAL_MS);\n\theartbeat.unref();\n\n\ttry {\n\t\tconst result = await completeSessionCompact(config, body, prepared);\n\t\tres.write(JSON.stringify(result.body));\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tres.write(JSON.stringify({ error: message }));\n\t} finally {\n\t\tclearInterval(heartbeat);\n\t\tres.end();\n\t}\n}\n\nasync function handleSessionCompact(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tres: ServerResponse,\n): Promise<void> {\n\tconst prepared = prepareSessionCompact(body);\n\tif (\"status\" in prepared) {\n\t\tsendJson(res, prepared.status, prepared.body);\n\t\treturn;\n\t}\n\n\tif (body.streamResponse) {\n\t\tawait handleSessionCompactStream(config, body, prepared, res);\n\t\treturn;\n\t}\n\n\tawait handleSessionCompactJsonStream(config, body, prepared, res);\n}\n\nfunction handleDropLastAssistantError(config: ServerConfig, body: SessionIdBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tconst dropped = dropLastAssistantError(body.sessionId);\n\tconst session = getSession(body.sessionId);\n\tif (session) {\n\t\tpersistSession(config, session);\n\t}\n\tconst messageCount = session?.messages.length ?? 0;\n\tsendJson(res, 200, { success: true, dropped, messageCount });\n}\n\nfunction handleSessionHistory(\n\tsessionId: string,\n\tfrom: number | undefined,\n\tentriesFrom: number | undefined,\n\trevision: number | undefined,\n\tbaseTreeHash: string | undefined,\n\tres: ServerResponse,\n): void {\n\tconst session = getSession(sessionId);\n\tif (!session) {\n\t\tsendJson(res, 404, { error: \"session not found\" });\n\t\treturn;\n\t}\n\tconst baseMessageCount = from ?? 0;\n\tif (\n\t\tentriesFrom !== undefined &&\n\t\tentriesFrom <= session.entries.length &&\n\t\t(revision === undefined || revision <= session.revision) &&\n\t\t(baseTreeHash === undefined || baseTreeHash === session.prefixHashes[entriesFrom])\n\t) {\n\t\tsendJson(res, 200, sessionTreePatchResponseBody(session, baseMessageCount, entriesFrom, revision));\n\t\treturn;\n\t}\n\tsendJson(res, 200, sessionHistoryFullResponseBody(session, baseMessageCount));\n}\n\nfunction handleSessionRun(sessionId: string, runId: string, res: ServerResponse): void {\n\tconst run = getStreamRun(sessionId, runId);\n\tif (!run) {\n\t\tsendJson(res, 404, { error: \"run not found\" });\n\t\treturn;\n\t}\n\tsendJson(res, 200, run);\n}\n\nexport function buildStreamContext(\n\tsession: SessionState,\n\tbody: Pick<StreamRequestBody, \"contextOverlay\" | \"ephemeralMessages\">,\n): Context {\n\tconst messages = body.contextOverlay ?? [...session.messages, ...(body.ephemeralMessages ?? [])];\n\treturn {\n\t\tsystemPrompt: session.staticContext?.systemPrompt,\n\t\tmessages,\n\t\ttools: session.staticContext?.tools,\n\t};\n}\n\nfunction handleStream(config: ServerConfig, body: StreamRequestBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!body.model) {\n\t\tsendJson(res, 400, { error: \"model is required\" });\n\t\treturn;\n\t}\n\n\tconst session = getOrCreateSession(body.sessionId);\n\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t\tpersistSession(config, session);\n\t}\n\n\tif (!session.staticContext && !body.staticContext) {\n\t\tsendJson(res, 400, { error: \"Session has no static context. Initialize with /api/session/init first.\" });\n\t\treturn;\n\t}\n\n\tif (body.ephemeralMessages !== undefined && !Array.isArray(body.ephemeralMessages)) {\n\t\tsendJson(res, 400, { error: \"ephemeralMessages must be an array\" });\n\t\treturn;\n\t}\n\tif (body.contextOverlay !== undefined && !Array.isArray(body.contextOverlay)) {\n\t\tsendJson(res, 400, { error: \"contextOverlay must be an array\" });\n\t\treturn;\n\t}\n\n\tconst context = buildStreamContext(session, body);\n\tconst existingRun = body.runId ? getStreamRun(body.sessionId, body.runId) : undefined;\n\n\tconst { model: resolvedModel, options: streamOptions } = resolveStreamOptions(config, body.model, body);\n\n\tres.writeHead(200, {\n\t\t\"Content-Type\": \"text/event-stream\",\n\t\t\"Cache-Control\": \"no-cache\",\n\t\tConnection: \"keep-alive\",\n\t});\n\tres.flushHeaders();\n\tres.write(STREAM_HEARTBEAT);\n\n\tif (existingRun?.status === \"completed\") {\n\t\tfor (const event of existingRun.events) {\n\t\t\tres.write(encodeProxyEvent(event));\n\t\t}\n\t\tres.end();\n\t\treturn;\n\t}\n\n\tconst run = body.runId ? startStreamRun(body.sessionId, body.runId) : undefined;\n\n\tconst heartbeat = setInterval(() => {\n\t\tif (!res.writableEnded) {\n\t\t\tres.write(STREAM_HEARTBEAT);\n\t\t}\n\t}, STREAM_HEARTBEAT_INTERVAL_MS);\n\theartbeat.unref();\n\n\tlet stream: AsyncIterable<AssistantMessageEvent>;\n\ttry {\n\t\tstream = streamSimple(resolvedModel, context, streamOptions);\n\t} catch (err) {\n\t\tclearInterval(heartbeat);\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tfailStreamRun(run, message);\n\t\tres.write(encodeErrorEvent(message));\n\t\tres.end();\n\t\treturn;\n\t}\n\n\t(async () => {\n\t\ttry {\n\t\t\tfor await (const event of stream) {\n\t\t\t\tconst proxyEvent = toProxyEvent(event);\n\t\t\t\tif (proxyEvent) {\n\t\t\t\t\trecordStreamRunEvent(run, proxyEvent);\n\t\t\t\t\tres.write(encodeProxyEvent(proxyEvent));\n\t\t\t\t}\n\t\t\t\tif (event.type === \"done\") {\n\t\t\t\t\tcompleteStreamRun(run, event.message);\n\t\t\t\t} else if (event.type === \"error\") {\n\t\t\t\t\tfailStreamRun(run, event.error.errorMessage ?? event.reason);\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tclearInterval(heartbeat);\n\t\t}\n\n\t\tres.end();\n\t})().catch((err) => {\n\t\tclearInterval(heartbeat);\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tfailStreamRun(run, message);\n\t\tres.write(encodeErrorEvent(message));\n\t\tres.end();\n\t});\n}\n\nasync function handlePostRequest(\n\tconfig: ServerConfig,\n\tpathname: string,\n\tbody: unknown,\n\tres: ServerResponse,\n): Promise<boolean> {\n\tif (pathname === \"/api/receive\") {\n\t\ttry {\n\t\t\tsendJson(res, 200, receiveUpload(config.uploadDir, body));\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof ReceiveUploadError)) throw error;\n\t\t\tsendJson(res, error.status, { error: error.message });\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/init\") {\n\t\thandleSessionInit(config, body as SessionInitBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/update\") {\n\t\thandleSessionUpdate(config, body as SessionInitBody & { staticContext: SessionStaticContext }, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/sync\") {\n\t\thandleSessionSync(config, body as SessionSyncBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/append\") {\n\t\thandleSessionAppend(config, body as SessionAppendBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/tree/sync\") {\n\t\thandleSessionTreeSync(config, body as SessionTreeSyncBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/tree/append\") {\n\t\thandleSessionTreeAppend(config, body as SessionTreeSyncBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/tree/switch\") {\n\t\thandleSessionTreeSwitch(config, body as SessionTreeSwitchBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/drop-last-assistant-error\") {\n\t\thandleDropLastAssistantError(config, body as SessionIdBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/compact\") {\n\t\tawait handleSessionCompact(config, body as SessionCompactBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/stream\") {\n\t\thandleStream(config, body as StreamRequestBody, res);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nexport function createPiServer(configOverride?: Partial<ServerConfig>): HttpServer {\n\tconst config = loadConfig(configOverride);\n\tloadPersistedSessions(config.sessionStoreDir);\n\n\tconst server = createServer(async (req, res) => {\n\t\tconst url = new URL(req.url ?? \"/\", `http://${config.host}:${config.port}`);\n\n\t\tif (req.method === \"GET\" && url.pathname === \"/health\") {\n\t\t\tsendJson(res, 200, { status: \"ok\" });\n\t\t\treturn;\n\t\t}\n\n\t\tif (!authenticate(config, req)) {\n\t\t\tconst body =\n\t\t\t\treq.method === \"GET\" && url.pathname === \"/\"\n\t\t\t\t\t? { error: \"Unauthorized\", version: PI_SERVER_VERSION }\n\t\t\t\t\t: { error: \"Unauthorized\" };\n\t\t\tsendJson(res, 401, body);\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"GET\" && url.pathname === \"/api/sessions\") {\n\t\t\tsendJson(res, 200, { sessions: listSessions() });\n\t\t\treturn;\n\t\t}\n\n\t\tconst runMatch = /^\\/api\\/session\\/([^/]+)\\/runs\\/([^/]+)$/.exec(url.pathname);\n\t\tif (req.method === \"GET\" && runMatch) {\n\t\t\thandleSessionRun(decodeURIComponent(runMatch[1]), decodeURIComponent(runMatch[2]), res);\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"GET\" && url.pathname.startsWith(\"/api/session/\") && url.pathname.endsWith(\"/history\")) {\n\t\t\tconst encodedSessionId = url.pathname.slice(\"/api/session/\".length, -\"/history\".length);\n\t\t\tconst fromParam = url.searchParams.get(\"from\");\n\t\t\tconst from = fromParam === null ? undefined : Number(fromParam);\n\t\t\tif (from !== undefined && (!Number.isInteger(from) || from < 0)) {\n\t\t\t\tsendJson(res, 400, { error: \"from must be a non-negative integer\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst entriesFromParam = url.searchParams.get(\"entriesFrom\");\n\t\t\tconst entriesFrom = entriesFromParam === null ? undefined : Number(entriesFromParam);\n\t\t\tif (entriesFrom !== undefined && (!Number.isInteger(entriesFrom) || entriesFrom < 0)) {\n\t\t\t\tsendJson(res, 400, { error: \"entriesFrom must be a non-negative integer\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst revisionParam = url.searchParams.get(\"revision\");\n\t\t\tconst revision = revisionParam === null ? undefined : Number(revisionParam);\n\t\t\tif (revision !== undefined && (!Number.isInteger(revision) || revision < 0)) {\n\t\t\t\tsendJson(res, 400, { error: \"revision must be a non-negative integer\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thandleSessionHistory(\n\t\t\t\tdecodeURIComponent(encodedSessionId),\n\t\t\t\tfrom,\n\t\t\t\tentriesFrom,\n\t\t\t\trevision,\n\t\t\t\turl.searchParams.get(\"baseTreeHash\") ?? undefined,\n\t\t\t\tres,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"POST\" && url.pathname === CHUNK_ENDPOINT) {\n\t\t\ttry {\n\t\t\t\tconst body = JSON.parse(await readBody(req)) as RequestChunkBody;\n\t\t\t\tconst chunkResult = receiveRequestChunk(body);\n\t\t\t\tif (!chunkResult.complete) {\n\t\t\t\t\tsendJson(res, 200, chunkResult.ack);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait handlePostRequest(config, chunkResult.target, JSON.parse(chunkResult.bodyJson) as unknown, res);\n\t\t\t} catch (err) {\n\t\t\t\tlogRequestError(req, err);\n\t\t\t\tif (!res.headersSent) {\n\t\t\t\t\tsendJson(res, 400, { error: err instanceof Error ? err.message : String(err) });\n\t\t\t\t} else {\n\t\t\t\t\tres.write(encodeErrorEvent(err instanceof Error ? err.message : String(err)));\n\t\t\t\t\tres.end();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"POST\") {\n\t\t\ttry {\n\t\t\t\tconst body = JSON.parse(await readBody(req)) as unknown;\n\t\t\t\tif (await handlePostRequest(config, url.pathname, body, res)) return;\n\t\t\t} catch (err) {\n\t\t\t\tlogRequestError(req, err);\n\t\t\t\tif (!res.headersSent) {\n\t\t\t\t\tsendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });\n\t\t\t\t} else {\n\t\t\t\t\tres.write(encodeErrorEvent(err instanceof Error ? err.message : String(err)));\n\t\t\t\t\tres.end();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (req.method === \"DELETE\" && url.pathname.startsWith(\"/api/session/\")) {\n\t\t\tconst sessionId = decodeURIComponent(url.pathname.slice(\"/api/session/\".length));\n\t\t\tdeleteSessionFromStore(sessionId);\n\t\t\tdeletePersistedSession(config.sessionStoreDir, sessionId);\n\t\t\tsendJson(res, 200, { deleted: sessionId });\n\t\t\treturn;\n\t\t}\n\n\t\tsendJson(res, 404, { error: \"Not found\" });\n\t});\n\n\treturn server;\n}\n\nfunction toProxyEvent(event: AssistantMessageEvent): ProxyAssistantMessageEvent | undefined {\n\tswitch (event.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\" };\n\t\tcase \"text_start\":\n\t\t\treturn { type: \"text_start\", contentIndex: event.contentIndex };\n\t\tcase \"text_delta\":\n\t\t\treturn { type: \"text_delta\", contentIndex: event.contentIndex, delta: event.delta };\n\t\tcase \"text_end\":\n\t\t\treturn {\n\t\t\t\ttype: \"text_end\",\n\t\t\t\tcontentIndex: event.contentIndex,\n\t\t\t\tcontentSignature:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"text\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { textSignature?: string }).textSignature\n\t\t\t\t\t\t: undefined,\n\t\t\t};\n\t\tcase \"thinking_start\":\n\t\t\treturn { type: \"thinking_start\", contentIndex: event.contentIndex };\n\t\tcase \"thinking_delta\":\n\t\t\treturn { type: \"thinking_delta\", contentIndex: event.contentIndex, delta: event.delta };\n\t\tcase \"thinking_end\":\n\t\t\treturn {\n\t\t\t\ttype: \"thinking_end\",\n\t\t\t\tcontentIndex: event.contentIndex,\n\t\t\t\tcontentSignature:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"thinking\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { thinkingSignature?: string }).thinkingSignature\n\t\t\t\t\t\t: undefined,\n\t\t\t};\n\t\tcase \"toolcall_start\":\n\t\t\treturn {\n\t\t\t\ttype: \"toolcall_start\",\n\t\t\t\tcontentIndex: event.contentIndex,\n\t\t\t\tid:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"toolCall\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { id: string }).id\n\t\t\t\t\t\t: \"\",\n\t\t\t\ttoolName:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"toolCall\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { name: string }).name\n\t\t\t\t\t\t: \"\",\n\t\t\t};\n\t\tcase \"toolcall_delta\":\n\t\t\treturn { type: \"toolcall_delta\", contentIndex: event.contentIndex, delta: event.delta };\n\t\tcase \"toolcall_end\":\n\t\t\treturn { type: \"toolcall_end\", contentIndex: event.contentIndex };\n\t\tcase \"done\":\n\t\t\treturn { type: \"done\", reason: event.reason, usage: event.message.usage };\n\t\tcase \"error\":\n\t\t\treturn {\n\t\t\t\ttype: \"error\",\n\t\t\t\treason: event.reason,\n\t\t\t\terrorMessage: event.error.errorMessage,\n\t\t\t\tusage: event.error.usage,\n\t\t\t};\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nexport function startServer(configOverride?: Partial<ServerConfig>): HttpServer {\n\tconst config = loadConfig(configOverride);\n\tconst server = createPiServer(configOverride);\n\tserver.listen(config.port, config.host, () => {\n\t\tconsole.log(`pi-server listening on ${config.host}:${config.port}`);\n\t});\n\treturn server;\n}\n"]}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,IAAI,UAAU,EAA6C,MAAM,WAAW,CAAC;AAY/G,OAAO,EAGN,KAAK,OAAO,EAGZ,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,mBAAmB,EACxB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,OAAO,EAYN,KAAK,YAAY,EACjB,KAAK,oBAAoB,EAGzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAc5D,UAAU,iBAAiB;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,aAAa,CAAC,EAAE,oBAAoB,CAAC;IACrC,iBAAiB,CAAC,EAAE,OAAO,EAAE,CAAC;IAC9B,cAAc,CAAC,EAAE,OAAO,EAAE,CAAC;CAC3B;AA4GD,UAAU,cAAc;IACvB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,OAAO,EAAE,mBAAmB,CAAC;CAC7B;AAgJD,wBAAgB,oBAAoB,CACnC,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,IAAI,EAAE,iBAAiB,GACrB,cAAc,CAEhB;AAsVD,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,gBAAgB,GAAG,mBAAmB,CAAC,GACnE,OAAO,CAOT;AA4KD,wBAAgB,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,UAAU,CAgHjF;AAoED,wBAAgB,WAAW,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,UAAU,CAO9E","sourcesContent":["import { createServer, type Server as HttpServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { createRequire } from \"node:module\";\nimport {\n\ttype CompactionPreparationOptions,\n\ttype CompactionSettings,\n\tcompactLegacy,\n\tDEFAULT_COMPACTION_SETTINGS,\n\ttype LegacyCompactResult,\n\ttype ProxyAssistantMessageEvent,\n\tprepareLegacyCompaction,\n\ttype SessionTreeEntry,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tcreateModels,\n\tcreateProvider,\n\ttype Message,\n\ttype Model,\n\ttype SimpleStreamOptions,\n} from \"@earendil-works/pi-ai\";\nimport { streamSimple } from \"@earendil-works/pi-ai/compat\";\nimport type { ServerConfig } from \"./config.ts\";\nimport { loadConfig } from \"./config.ts\";\nimport { encodeErrorEvent, encodeProxyEvent } from \"./event-encoding.ts\";\nimport { ReceiveUploadError, receiveUpload } from \"./receive-upload.ts\";\nimport { CHUNK_ENDPOINT, type RequestChunkBody, receiveRequestChunk } from \"./request-chunks.ts\";\nimport { deletePersistedSession, loadPersistedSessions, savePersistedSession } from \"./session-persistence.ts\";\nimport {\n\tappendCompactionEntry,\n\tappendMessages,\n\tappendSessionEntries,\n\tdeleteSession as deleteSessionFromStore,\n\tdropLastAssistantError,\n\tgetOrCreateSession,\n\tgetSession,\n\tgetSessionBranch,\n\tlistSessions,\n\treplaceMessages,\n\treplaceSessionTree,\n\ttype SessionState,\n\ttype SessionStaticContext,\n\tsetStaticContext,\n\tswitchSessionLeaf,\n} from \"./session-store.ts\";\n\nexport { loadConfig, type ServerConfig } from \"./config.ts\";\n\ninterface PackageMetadata {\n\tversion: string;\n}\n\nconst packageMetadata = createRequire(import.meta.url)(\"../package.json\") as PackageMetadata;\nconst PI_SERVER_VERSION = packageMetadata.version;\n\ninterface SessionInitBody {\n\tsessionId: string;\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface StreamRequestBody {\n\tsessionId: string;\n\trunId?: string;\n\tmodel: Model<any>;\n\toptions?: SimpleStreamOptions;\n\tstaticContext?: SessionStaticContext;\n\tephemeralMessages?: Message[];\n\tcontextOverlay?: Message[];\n}\n\ninterface SessionSyncBody {\n\tsessionId: string;\n\tmessages: Message[];\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface SessionAppendBody {\n\tsessionId: string;\n\tmessages: Message[];\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface SessionTreeSyncBody {\n\tsessionId: string;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface SessionTreeSwitchBody {\n\tsessionId: string;\n\tleafId: string | null;\n}\n\ninterface SessionCompactBody {\n\tsessionId: string;\n\tmodel: Model<any>;\n\toptions?: SimpleStreamOptions;\n\tsettings?: CompactionSettings;\n\tpreparation?: CompactionPreparationOptions;\n\tcustomInstructions?: string;\n\tbaseTreeHash?: string;\n\tfullResponse?: boolean;\n\tstreamResponse?: boolean;\n}\n\nfunction createRequestModels(model: Model<any>, options: SimpleStreamOptions) {\n\tconst models = createModels();\n\tmodels.setProvider(\n\t\tcreateProvider({\n\t\t\tid: model.provider,\n\t\t\tname: model.provider,\n\t\t\tmodels: [model],\n\t\t\tauth: {\n\t\t\t\tapiKey: {\n\t\t\t\t\tname: \"pi-server request auth\",\n\t\t\t\t\tresolve: async () => ({ auth: { apiKey: options.apiKey, headers: options.headers } }),\n\t\t\t\t},\n\t\t\t},\n\t\t\tapi: {\n\t\t\t\tstream: (requestModel, context, streamOptions) => streamSimple(requestModel, context, streamOptions),\n\t\t\t\tstreamSimple: (requestModel, context, streamOptions) => streamSimple(requestModel, context, streamOptions),\n\t\t\t},\n\t\t}),\n\t);\n\treturn models;\n}\n\ninterface SessionIdBody {\n\tsessionId: string;\n}\n\ninterface StreamRunRecord {\n\tsessionId: string;\n\trunId: string;\n\tstatus: \"running\" | \"completed\" | \"failed\";\n\tevents: ProxyAssistantMessageEvent[];\n\tmessage?: AssistantMessage;\n\terrorMessage?: string;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nconst STREAM_HEARTBEAT = \": keep-alive\\n\\n\";\nconst JSON_HEARTBEAT = \" \\n\";\nconst STREAM_HEARTBEAT_INTERVAL_MS = 25_000;\nconst streamRuns = new Map<string, StreamRunRecord>();\n\nfunction readBody(req: IncomingMessage): Promise<string> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst chunks: Buffer[] = [];\n\t\treq.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n\t\treq.on(\"end\", () => resolve(Buffer.concat(chunks).toString(\"utf-8\")));\n\t\treq.on(\"error\", reject);\n\t});\n}\n\nfunction sendJson(res: ServerResponse, status: number, body: unknown): void {\n\tconst data = JSON.stringify(body);\n\tres.writeHead(status, { \"Content-Type\": \"application/json\", \"Content-Length\": Buffer.byteLength(data) });\n\tres.end(data);\n}\n\nfunction logRequestError(req: IncomingMessage, error: unknown): void {\n\tconst message = error instanceof Error ? error.stack || error.message : String(error);\n\tconsole.error(`${req.method ?? \"UNKNOWN\"} ${req.url ?? \"/\"} failed: ${message}`);\n}\n\nfunction authenticate(config: ServerConfig, req: IncomingMessage): boolean {\n\tif (!config.authToken) return true;\n\tconst header = req.headers.authorization;\n\tif (!header) return false;\n\tconst token = header.startsWith(\"Bearer \") ? header.slice(7) : header;\n\treturn token === config.authToken;\n}\n\ninterface ResolvedStream {\n\tmodel: Model<any>;\n\toptions: SimpleStreamOptions;\n}\n\nfunction sessionResponseBody(session: SessionState) {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContextHash: session.staticContextHash,\n\t\ttreeHash: session.treeHash,\n\t\tmessageCount: session.messages.length,\n\t\tentryCount: session.entries.length,\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t};\n}\n\nfunction persistSession(config: ServerConfig, session: SessionState): void {\n\tsavePersistedSession(config.sessionStoreDir, session);\n}\n\nfunction runKey(sessionId: string, runId: string): string {\n\treturn `${sessionId}\\0${runId}`;\n}\n\nfunction getStreamRun(sessionId: string, runId: string): StreamRunRecord | undefined {\n\treturn streamRuns.get(runKey(sessionId, runId));\n}\n\nfunction startStreamRun(sessionId: string, runId: string): StreamRunRecord {\n\tconst existing = getStreamRun(sessionId, runId);\n\tif (existing?.status === \"completed\" || existing?.status === \"failed\") return existing;\n\tconst now = Date.now();\n\tconst run: StreamRunRecord = existing ?? {\n\t\tsessionId,\n\t\trunId,\n\t\tstatus: \"running\",\n\t\tevents: [],\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t};\n\trun.status = \"running\";\n\trun.updatedAt = now;\n\tstreamRuns.set(runKey(sessionId, runId), run);\n\treturn run;\n}\n\nfunction recordStreamRunEvent(run: StreamRunRecord | undefined, event: ProxyAssistantMessageEvent): void {\n\tif (!run) return;\n\trun.events.push(event);\n\trun.updatedAt = Date.now();\n}\n\nfunction completeStreamRun(run: StreamRunRecord | undefined, message: AssistantMessage): void {\n\tif (!run) return;\n\trun.status = \"completed\";\n\trun.message = message;\n\trun.errorMessage = undefined;\n\trun.updatedAt = Date.now();\n}\n\nfunction failStreamRun(run: StreamRunRecord | undefined, errorMessage: string): void {\n\tif (!run) return;\n\trun.status = \"failed\";\n\trun.errorMessage = errorMessage;\n\trun.updatedAt = Date.now();\n}\n\nfunction sessionHistoryFullResponseBody(session: SessionState, baseMessageCount: number) {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tstaticContextHash: session.staticContextHash,\n\t\ttreeHash: session.treeHash,\n\t\tmessageCount: session.messages.length,\n\t\tentryCount: session.entries.length,\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tentries: session.entries,\n\t\tbaseMessageCount,\n\t\tmessages: session.messages.slice(baseMessageCount),\n\t};\n}\n\nfunction sessionTreePatchResponseBody(\n\tsession: SessionState,\n\tbaseMessageCount: number,\n\tentriesFrom: number,\n\tbaseRevision: number | undefined,\n) {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tstaticContextHash: session.staticContextHash,\n\t\ttreeHash: session.treeHash,\n\t\tmessageCount: session.messages.length,\n\t\tentryCount: session.entries.length,\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tbaseMessageCount,\n\t\tmessages: session.messages.slice(baseMessageCount),\n\t\ttreePatch: {\n\t\t\tentriesFrom,\n\t\t\tbaseRevision,\n\t\t\tentries: session.entries.slice(entriesFrom),\n\t\t\tleafId: session.leafId,\n\t\t\trevision: session.revision,\n\t\t},\n\t};\n}\n\ntype PreparedCompaction = Parameters<typeof compactLegacy>[0];\n\ninterface PreparedSessionCompact {\n\tsession: SessionState;\n\tpreparation: PreparedCompaction;\n\toptions: SimpleStreamOptions;\n}\n\ninterface SessionCompactSuccessBody {\n\tsuccess: true;\n\tcompaction: LegacyCompactResult;\n\tcompactionEntry: SessionTreeEntry;\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash: string;\n\tmessageCount: number;\n\tentryCount: number;\n\tleafId: string | null;\n\trevision: number;\n\tstaticContext?: SessionStaticContext;\n\ttreePatch?: {\n\t\tbaseTreeHash: string;\n\t\tentriesFrom: number;\n\t\tentries: SessionTreeEntry[];\n\t\tleafId: string | null;\n\t\trevision: number;\n\t};\n\tentries?: SessionTreeEntry[];\n\tmessages?: Message[];\n}\n\ninterface SessionCompactHttpResponse {\n\tstatus: number;\n\tbody: { error: string } | SessionCompactSuccessBody;\n}\n\nexport function resolveStreamOptions(\n\t_config: ServerConfig,\n\tmodel: Model<any>,\n\tbody: StreamRequestBody,\n): ResolvedStream {\n\treturn { model, options: { ...(body.options ?? {}) } };\n}\n\nfunction handleSessionInit(config: ServerConfig, body: SessionInitBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t} else {\n\t\tgetOrCreateSession(body.sessionId);\n\t}\n\tconst session = getSession(body.sessionId)!;\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionUpdate(\n\tconfig: ServerConfig,\n\tbody: SessionInitBody & { staticContext: SessionStaticContext },\n\tres: ServerResponse,\n): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!body.staticContext) {\n\t\tsendJson(res, 400, { error: \"staticContext is required for update\" });\n\t\treturn;\n\t}\n\tsetStaticContext(body.sessionId, body.staticContext);\n\tconst session = getSession(body.sessionId)!;\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionSync(config: ServerConfig, body: SessionSyncBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.messages)) {\n\t\tsendJson(res, 400, { error: \"messages is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = replaceMessages(body.sessionId, body.messages);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionAppend(config: ServerConfig, body: SessionAppendBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.messages)) {\n\t\tsendJson(res, 400, { error: \"messages is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = appendMessages(body.sessionId, body.messages);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionTreeSync(config: ServerConfig, body: SessionTreeSyncBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.entries)) {\n\t\tsendJson(res, 400, { error: \"entries is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = replaceSessionTree(body.sessionId, body.entries, body.leafId ?? null);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionTreeAppend(config: ServerConfig, body: SessionTreeSyncBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.entries)) {\n\t\tsendJson(res, 400, { error: \"entries is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = appendSessionEntries(body.sessionId, body.entries, body.leafId ?? null);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionTreeSwitch(config: ServerConfig, body: SessionTreeSwitchBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tconst session = switchSessionLeaf(body.sessionId, body.leafId ?? null);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction prepareSessionCompact(body: SessionCompactBody): PreparedSessionCompact | SessionCompactHttpResponse {\n\tif (!body.sessionId) {\n\t\treturn { status: 400, body: { error: \"sessionId is required\" } };\n\t}\n\tif (!body.model) {\n\t\treturn { status: 400, body: { error: \"model is required\" } };\n\t}\n\n\tconst session = getSession(body.sessionId);\n\tif (!session) {\n\t\treturn { status: 404, body: { error: \"session not found\" } };\n\t}\n\n\tconst entries = getSessionBranch(session);\n\tconst preparationResult = prepareLegacyCompaction(\n\t\tentries,\n\t\tbody.settings ?? DEFAULT_COMPACTION_SETTINGS,\n\t\tbody.preparation,\n\t);\n\tif (!preparationResult.ok) {\n\t\treturn { status: 400, body: { error: preparationResult.error.message } };\n\t}\n\tif (!preparationResult.value) {\n\t\treturn { status: 400, body: { error: \"Nothing to compact\" } };\n\t}\n\n\tconst options = body.options ?? {};\n\treturn {\n\t\tsession,\n\t\tpreparation: preparationResult.value,\n\t\toptions,\n\t};\n}\n\nasync function completeSessionCompact(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tprepared: PreparedSessionCompact,\n): Promise<SessionCompactHttpResponse> {\n\tconst result = await compactLegacy(\n\t\tprepared.preparation,\n\t\tcreateRequestModels(body.model, prepared.options),\n\t\tbody.model,\n\t\tbody.customInstructions,\n\t\tundefined,\n\t\tprepared.options.reasoning,\n\t);\n\tif (!result.ok) {\n\t\treturn { status: 500, body: { error: result.error.message } };\n\t}\n\n\tconst baseTreeHash = prepared.session.treeHash;\n\tconst baseEntryCount = prepared.session.entries.length;\n\tconst compaction = result.value;\n\tconst { session: updatedSession, entry: compactionEntry } = appendCompactionEntry(body.sessionId, compaction);\n\tpersistSession(config, updatedSession);\n\tif (!body.fullResponse && body.baseTreeHash === baseTreeHash) {\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\tbody: {\n\t\t\t\tsuccess: true,\n\t\t\t\tcompaction: compaction satisfies LegacyCompactResult,\n\t\t\t\tcompactionEntry,\n\t\t\t\t...sessionResponseBody(updatedSession),\n\t\t\t\tstaticContext: updatedSession.staticContext,\n\t\t\t\ttreePatch: {\n\t\t\t\t\tbaseTreeHash,\n\t\t\t\t\tentriesFrom: baseEntryCount,\n\t\t\t\t\tentries: [compactionEntry],\n\t\t\t\t\tleafId: updatedSession.leafId,\n\t\t\t\t\trevision: updatedSession.revision,\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\treturn {\n\t\tstatus: 200,\n\t\tbody: {\n\t\t\tsuccess: true,\n\t\t\tcompaction: compaction satisfies LegacyCompactResult,\n\t\t\tcompactionEntry,\n\t\t\t...sessionResponseBody(updatedSession),\n\t\t\tstaticContext: updatedSession.staticContext,\n\t\t\tentries: updatedSession.entries,\n\t\t\tmessages: updatedSession.messages,\n\t\t},\n\t};\n}\n\nfunction writeServerSentEvent(res: ServerResponse, event: string, body: unknown): void {\n\tres.write(`event: ${event}\\n`);\n\tres.write(`data: ${JSON.stringify(body)}\\n\\n`);\n}\n\nasync function handleSessionCompactStream(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tprepared: PreparedSessionCompact,\n\tres: ServerResponse,\n): Promise<void> {\n\tres.writeHead(200, {\n\t\t\"Content-Type\": \"text/event-stream\",\n\t\t\"Cache-Control\": \"no-cache\",\n\t\tConnection: \"keep-alive\",\n\t});\n\tres.flushHeaders();\n\tres.write(STREAM_HEARTBEAT);\n\n\tconst heartbeat = setInterval(() => {\n\t\tif (!res.writableEnded) {\n\t\t\tres.write(STREAM_HEARTBEAT);\n\t\t}\n\t}, STREAM_HEARTBEAT_INTERVAL_MS);\n\theartbeat.unref();\n\n\ttry {\n\t\tconst result = await completeSessionCompact(config, body, prepared);\n\t\twriteServerSentEvent(res, result.status >= 400 ? \"error\" : \"result\", result.body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\twriteServerSentEvent(res, \"error\", { error: message });\n\t} finally {\n\t\tclearInterval(heartbeat);\n\t\tres.end();\n\t}\n}\n\nasync function handleSessionCompactJsonStream(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tprepared: PreparedSessionCompact,\n\tres: ServerResponse,\n): Promise<void> {\n\tres.writeHead(200, {\n\t\t\"Content-Type\": \"application/json\",\n\t\t\"Cache-Control\": \"no-cache\",\n\t\tConnection: \"keep-alive\",\n\t});\n\tres.flushHeaders();\n\tres.write(JSON_HEARTBEAT);\n\n\tconst heartbeat = setInterval(() => {\n\t\tif (!res.writableEnded) {\n\t\t\tres.write(JSON_HEARTBEAT);\n\t\t}\n\t}, STREAM_HEARTBEAT_INTERVAL_MS);\n\theartbeat.unref();\n\n\ttry {\n\t\tconst result = await completeSessionCompact(config, body, prepared);\n\t\tres.write(JSON.stringify(result.body));\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tres.write(JSON.stringify({ error: message }));\n\t} finally {\n\t\tclearInterval(heartbeat);\n\t\tres.end();\n\t}\n}\n\nasync function handleSessionCompact(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tres: ServerResponse,\n): Promise<void> {\n\tconst prepared = prepareSessionCompact(body);\n\tif (\"status\" in prepared) {\n\t\tsendJson(res, prepared.status, prepared.body);\n\t\treturn;\n\t}\n\n\tif (body.streamResponse) {\n\t\tawait handleSessionCompactStream(config, body, prepared, res);\n\t\treturn;\n\t}\n\n\tawait handleSessionCompactJsonStream(config, body, prepared, res);\n}\n\nfunction handleDropLastAssistantError(config: ServerConfig, body: SessionIdBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tconst dropped = dropLastAssistantError(body.sessionId);\n\tconst session = getSession(body.sessionId);\n\tif (session) {\n\t\tpersistSession(config, session);\n\t}\n\tconst messageCount = session?.messages.length ?? 0;\n\tsendJson(res, 200, { success: true, dropped, messageCount });\n}\n\nfunction handleSessionHistory(\n\tsessionId: string,\n\tfrom: number | undefined,\n\tentriesFrom: number | undefined,\n\trevision: number | undefined,\n\tbaseTreeHash: string | undefined,\n\tres: ServerResponse,\n): void {\n\tconst session = getSession(sessionId);\n\tif (!session) {\n\t\tsendJson(res, 404, { error: \"session not found\" });\n\t\treturn;\n\t}\n\tconst baseMessageCount = from ?? 0;\n\tif (\n\t\tentriesFrom !== undefined &&\n\t\tentriesFrom <= session.entries.length &&\n\t\t(revision === undefined || revision <= session.revision) &&\n\t\t(baseTreeHash === undefined || baseTreeHash === session.prefixHashes[entriesFrom])\n\t) {\n\t\tsendJson(res, 200, sessionTreePatchResponseBody(session, baseMessageCount, entriesFrom, revision));\n\t\treturn;\n\t}\n\tsendJson(res, 200, sessionHistoryFullResponseBody(session, baseMessageCount));\n}\n\nfunction handleSessionRun(sessionId: string, runId: string, res: ServerResponse): void {\n\tconst run = getStreamRun(sessionId, runId);\n\tif (!run) {\n\t\tsendJson(res, 404, { error: \"run not found\" });\n\t\treturn;\n\t}\n\tsendJson(res, 200, run);\n}\n\nexport function buildStreamContext(\n\tsession: SessionState,\n\tbody: Pick<StreamRequestBody, \"contextOverlay\" | \"ephemeralMessages\">,\n): Context {\n\tconst messages = body.contextOverlay ?? [...session.messages, ...(body.ephemeralMessages ?? [])];\n\treturn {\n\t\tsystemPrompt: session.staticContext?.systemPrompt,\n\t\tmessages,\n\t\ttools: session.staticContext?.tools,\n\t};\n}\n\nfunction handleStream(config: ServerConfig, body: StreamRequestBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!body.model) {\n\t\tsendJson(res, 400, { error: \"model is required\" });\n\t\treturn;\n\t}\n\n\tconst session = getOrCreateSession(body.sessionId);\n\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t\tpersistSession(config, session);\n\t}\n\n\tif (!session.staticContext && !body.staticContext) {\n\t\tsendJson(res, 400, { error: \"Session has no static context. Initialize with /api/session/init first.\" });\n\t\treturn;\n\t}\n\n\tif (body.ephemeralMessages !== undefined && !Array.isArray(body.ephemeralMessages)) {\n\t\tsendJson(res, 400, { error: \"ephemeralMessages must be an array\" });\n\t\treturn;\n\t}\n\tif (body.contextOverlay !== undefined && !Array.isArray(body.contextOverlay)) {\n\t\tsendJson(res, 400, { error: \"contextOverlay must be an array\" });\n\t\treturn;\n\t}\n\n\tconst context = buildStreamContext(session, body);\n\tconst existingRun = body.runId ? getStreamRun(body.sessionId, body.runId) : undefined;\n\n\tconst { model: resolvedModel, options: streamOptions } = resolveStreamOptions(config, body.model, body);\n\n\tres.writeHead(200, {\n\t\t\"Content-Type\": \"text/event-stream\",\n\t\t\"Cache-Control\": \"no-cache\",\n\t\tConnection: \"keep-alive\",\n\t});\n\tres.flushHeaders();\n\tres.write(STREAM_HEARTBEAT);\n\n\tif (existingRun?.status === \"completed\") {\n\t\tfor (const event of existingRun.events) {\n\t\t\tres.write(encodeProxyEvent(event));\n\t\t}\n\t\tres.end();\n\t\treturn;\n\t}\n\n\tconst run = body.runId ? startStreamRun(body.sessionId, body.runId) : undefined;\n\n\tconst heartbeat = setInterval(() => {\n\t\tif (!res.writableEnded) {\n\t\t\tres.write(STREAM_HEARTBEAT);\n\t\t}\n\t}, STREAM_HEARTBEAT_INTERVAL_MS);\n\theartbeat.unref();\n\n\tlet stream: AsyncIterable<AssistantMessageEvent>;\n\ttry {\n\t\tstream = streamSimple(resolvedModel, context, streamOptions);\n\t} catch (err) {\n\t\tclearInterval(heartbeat);\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tfailStreamRun(run, message);\n\t\tres.write(encodeErrorEvent(message));\n\t\tres.end();\n\t\treturn;\n\t}\n\n\t(async () => {\n\t\ttry {\n\t\t\tfor await (const event of stream) {\n\t\t\t\tconst proxyEvent = toProxyEvent(event);\n\t\t\t\tif (proxyEvent) {\n\t\t\t\t\trecordStreamRunEvent(run, proxyEvent);\n\t\t\t\t\tres.write(encodeProxyEvent(proxyEvent));\n\t\t\t\t}\n\t\t\t\tif (event.type === \"done\") {\n\t\t\t\t\tcompleteStreamRun(run, event.message);\n\t\t\t\t} else if (event.type === \"error\") {\n\t\t\t\t\tfailStreamRun(run, event.error.errorMessage ?? event.reason);\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tclearInterval(heartbeat);\n\t\t}\n\n\t\tres.end();\n\t})().catch((err) => {\n\t\tclearInterval(heartbeat);\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tfailStreamRun(run, message);\n\t\tres.write(encodeErrorEvent(message));\n\t\tres.end();\n\t});\n}\n\nasync function handlePostRequest(\n\tconfig: ServerConfig,\n\tpathname: string,\n\tbody: unknown,\n\tres: ServerResponse,\n): Promise<boolean> {\n\tif (pathname === \"/api/receive\") {\n\t\ttry {\n\t\t\tsendJson(res, 200, receiveUpload(config.uploadDir, body));\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof ReceiveUploadError)) throw error;\n\t\t\tsendJson(res, error.status, { error: error.message });\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/init\") {\n\t\thandleSessionInit(config, body as SessionInitBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/update\") {\n\t\thandleSessionUpdate(config, body as SessionInitBody & { staticContext: SessionStaticContext }, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/sync\") {\n\t\thandleSessionSync(config, body as SessionSyncBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/append\") {\n\t\thandleSessionAppend(config, body as SessionAppendBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/tree/sync\") {\n\t\thandleSessionTreeSync(config, body as SessionTreeSyncBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/tree/append\") {\n\t\thandleSessionTreeAppend(config, body as SessionTreeSyncBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/tree/switch\") {\n\t\thandleSessionTreeSwitch(config, body as SessionTreeSwitchBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/drop-last-assistant-error\") {\n\t\thandleDropLastAssistantError(config, body as SessionIdBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/compact\") {\n\t\tawait handleSessionCompact(config, body as SessionCompactBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/stream\") {\n\t\thandleStream(config, body as StreamRequestBody, res);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nexport function createPiServer(configOverride?: Partial<ServerConfig>): HttpServer {\n\tconst config = loadConfig(configOverride);\n\tloadPersistedSessions(config.sessionStoreDir);\n\n\tconst server = createServer(async (req, res) => {\n\t\tconst url = new URL(req.url ?? \"/\", `http://${config.host}:${config.port}`);\n\n\t\tif (req.method === \"GET\" && url.pathname === \"/health\") {\n\t\t\tsendJson(res, 200, { status: \"ok\" });\n\t\t\treturn;\n\t\t}\n\n\t\tif (!authenticate(config, req)) {\n\t\t\tconst body =\n\t\t\t\treq.method === \"GET\" && url.pathname === \"/\"\n\t\t\t\t\t? { error: \"Unauthorized\", version: PI_SERVER_VERSION }\n\t\t\t\t\t: { error: \"Unauthorized\" };\n\t\t\tsendJson(res, 401, body);\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"GET\" && url.pathname === \"/api/sessions\") {\n\t\t\tsendJson(res, 200, { sessions: listSessions() });\n\t\t\treturn;\n\t\t}\n\n\t\tconst runMatch = /^\\/api\\/session\\/([^/]+)\\/runs\\/([^/]+)$/.exec(url.pathname);\n\t\tif (req.method === \"GET\" && runMatch) {\n\t\t\thandleSessionRun(decodeURIComponent(runMatch[1]), decodeURIComponent(runMatch[2]), res);\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"GET\" && url.pathname.startsWith(\"/api/session/\") && url.pathname.endsWith(\"/history\")) {\n\t\t\tconst encodedSessionId = url.pathname.slice(\"/api/session/\".length, -\"/history\".length);\n\t\t\tconst fromParam = url.searchParams.get(\"from\");\n\t\t\tconst from = fromParam === null ? undefined : Number(fromParam);\n\t\t\tif (from !== undefined && (!Number.isInteger(from) || from < 0)) {\n\t\t\t\tsendJson(res, 400, { error: \"from must be a non-negative integer\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst entriesFromParam = url.searchParams.get(\"entriesFrom\");\n\t\t\tconst entriesFrom = entriesFromParam === null ? undefined : Number(entriesFromParam);\n\t\t\tif (entriesFrom !== undefined && (!Number.isInteger(entriesFrom) || entriesFrom < 0)) {\n\t\t\t\tsendJson(res, 400, { error: \"entriesFrom must be a non-negative integer\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst revisionParam = url.searchParams.get(\"revision\");\n\t\t\tconst revision = revisionParam === null ? undefined : Number(revisionParam);\n\t\t\tif (revision !== undefined && (!Number.isInteger(revision) || revision < 0)) {\n\t\t\t\tsendJson(res, 400, { error: \"revision must be a non-negative integer\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thandleSessionHistory(\n\t\t\t\tdecodeURIComponent(encodedSessionId),\n\t\t\t\tfrom,\n\t\t\t\tentriesFrom,\n\t\t\t\trevision,\n\t\t\t\turl.searchParams.get(\"baseTreeHash\") ?? undefined,\n\t\t\t\tres,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"POST\" && url.pathname === CHUNK_ENDPOINT) {\n\t\t\ttry {\n\t\t\t\tconst body = JSON.parse(await readBody(req)) as RequestChunkBody;\n\t\t\t\tconst chunkResult = receiveRequestChunk(body);\n\t\t\t\tif (!chunkResult.complete) {\n\t\t\t\t\tsendJson(res, 200, chunkResult.ack);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait handlePostRequest(config, chunkResult.target, JSON.parse(chunkResult.bodyJson) as unknown, res);\n\t\t\t} catch (err) {\n\t\t\t\tlogRequestError(req, err);\n\t\t\t\tif (!res.headersSent) {\n\t\t\t\t\tsendJson(res, 400, { error: err instanceof Error ? err.message : String(err) });\n\t\t\t\t} else {\n\t\t\t\t\tres.write(encodeErrorEvent(err instanceof Error ? err.message : String(err)));\n\t\t\t\t\tres.end();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"POST\") {\n\t\t\ttry {\n\t\t\t\tconst body = JSON.parse(await readBody(req)) as unknown;\n\t\t\t\tif (await handlePostRequest(config, url.pathname, body, res)) return;\n\t\t\t} catch (err) {\n\t\t\t\tlogRequestError(req, err);\n\t\t\t\tif (!res.headersSent) {\n\t\t\t\t\tsendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });\n\t\t\t\t} else {\n\t\t\t\t\tres.write(encodeErrorEvent(err instanceof Error ? err.message : String(err)));\n\t\t\t\t\tres.end();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (req.method === \"DELETE\" && url.pathname.startsWith(\"/api/session/\")) {\n\t\t\tconst sessionId = decodeURIComponent(url.pathname.slice(\"/api/session/\".length));\n\t\t\tdeleteSessionFromStore(sessionId);\n\t\t\tdeletePersistedSession(config.sessionStoreDir, sessionId);\n\t\t\tsendJson(res, 200, { deleted: sessionId });\n\t\t\treturn;\n\t\t}\n\n\t\tsendJson(res, 404, { error: \"Not found\" });\n\t});\n\n\treturn server;\n}\n\nfunction toProxyEvent(event: AssistantMessageEvent): ProxyAssistantMessageEvent | undefined {\n\tswitch (event.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\" };\n\t\tcase \"text_start\":\n\t\t\treturn { type: \"text_start\", contentIndex: event.contentIndex };\n\t\tcase \"text_delta\":\n\t\t\treturn { type: \"text_delta\", contentIndex: event.contentIndex, delta: event.delta };\n\t\tcase \"text_end\":\n\t\t\treturn {\n\t\t\t\ttype: \"text_end\",\n\t\t\t\tcontentIndex: event.contentIndex,\n\t\t\t\tcontentSignature:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"text\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { textSignature?: string }).textSignature\n\t\t\t\t\t\t: undefined,\n\t\t\t};\n\t\tcase \"thinking_start\":\n\t\t\treturn { type: \"thinking_start\", contentIndex: event.contentIndex };\n\t\tcase \"thinking_delta\":\n\t\t\treturn { type: \"thinking_delta\", contentIndex: event.contentIndex, delta: event.delta };\n\t\tcase \"thinking_end\":\n\t\t\treturn {\n\t\t\t\ttype: \"thinking_end\",\n\t\t\t\tcontentIndex: event.contentIndex,\n\t\t\t\tcontentSignature:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"thinking\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { thinkingSignature?: string }).thinkingSignature\n\t\t\t\t\t\t: undefined,\n\t\t\t};\n\t\tcase \"toolcall_start\":\n\t\t\treturn {\n\t\t\t\ttype: \"toolcall_start\",\n\t\t\t\tcontentIndex: event.contentIndex,\n\t\t\t\tid:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"toolCall\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { id: string }).id\n\t\t\t\t\t\t: \"\",\n\t\t\t\ttoolName:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"toolCall\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { name: string }).name\n\t\t\t\t\t\t: \"\",\n\t\t\t};\n\t\tcase \"toolcall_delta\":\n\t\t\treturn { type: \"toolcall_delta\", contentIndex: event.contentIndex, delta: event.delta };\n\t\tcase \"toolcall_end\":\n\t\t\treturn { type: \"toolcall_end\", contentIndex: event.contentIndex };\n\t\tcase \"done\":\n\t\t\treturn {\n\t\t\t\ttype: \"done\",\n\t\t\t\treason: event.reason,\n\t\t\t\tusage: event.message.usage,\n\t\t\t\tdeferred: event.message.deferred,\n\t\t\t};\n\t\tcase \"error\":\n\t\t\treturn {\n\t\t\t\ttype: \"error\",\n\t\t\t\treason: event.reason,\n\t\t\t\terrorMessage: event.error.errorMessage,\n\t\t\t\tusage: event.error.usage,\n\t\t\t};\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nexport function startServer(configOverride?: Partial<ServerConfig>): HttpServer {\n\tconst config = loadConfig(configOverride);\n\tconst server = createPiServer(configOverride);\n\tserver.listen(config.port, config.host, () => {\n\t\tconsole.log(`pi-server listening on ${config.host}:${config.port}`);\n\t});\n\treturn server;\n}\n"]}
package/dist/server.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createServer } from "node:http";
2
2
  import { createRequire } from "node:module";
3
- import { compact, DEFAULT_COMPACTION_SETTINGS, prepareCompaction, } from "@earendil-works/pi-agent-core";
3
+ import { compactLegacy, DEFAULT_COMPACTION_SETTINGS, prepareLegacyCompaction, } from "@earendil-works/pi-agent-core";
4
4
  import { createModels, createProvider, } from "@earendil-works/pi-ai";
5
5
  import { streamSimple } from "@earendil-works/pi-ai/compat";
6
6
  import { loadConfig } from "./config.js";
@@ -273,7 +273,7 @@ function prepareSessionCompact(body) {
273
273
  return { status: 404, body: { error: "session not found" } };
274
274
  }
275
275
  const entries = getSessionBranch(session);
276
- const preparationResult = prepareCompaction(entries, body.settings ?? DEFAULT_COMPACTION_SETTINGS, body.preparation);
276
+ const preparationResult = prepareLegacyCompaction(entries, body.settings ?? DEFAULT_COMPACTION_SETTINGS, body.preparation);
277
277
  if (!preparationResult.ok) {
278
278
  return { status: 400, body: { error: preparationResult.error.message } };
279
279
  }
@@ -288,26 +288,21 @@ function prepareSessionCompact(body) {
288
288
  };
289
289
  }
290
290
  async function completeSessionCompact(config, body, prepared) {
291
- const result = await compact(prepared.preparation, createRequestModels(body.model, prepared.options), body.model, body.customInstructions, undefined, prepared.options.reasoning);
291
+ const result = await compactLegacy(prepared.preparation, createRequestModels(body.model, prepared.options), body.model, body.customInstructions, undefined, prepared.options.reasoning);
292
292
  if (!result.ok) {
293
293
  return { status: 500, body: { error: result.error.message } };
294
294
  }
295
295
  const baseTreeHash = prepared.session.treeHash;
296
296
  const baseEntryCount = prepared.session.entries.length;
297
297
  const compaction = result.value;
298
- const firstKeptEntryId = compaction.firstKeptEntryId;
299
- if (!firstKeptEntryId) {
300
- return { status: 500, body: { error: "Compaction result is missing firstKeptEntryId" } };
301
- }
302
- const normalizedCompaction = { ...compaction, firstKeptEntryId };
303
- const { session: updatedSession, entry: compactionEntry } = appendCompactionEntry(body.sessionId, normalizedCompaction);
298
+ const { session: updatedSession, entry: compactionEntry } = appendCompactionEntry(body.sessionId, compaction);
304
299
  persistSession(config, updatedSession);
305
300
  if (!body.fullResponse && body.baseTreeHash === baseTreeHash) {
306
301
  return {
307
302
  status: 200,
308
303
  body: {
309
304
  success: true,
310
- compaction: normalizedCompaction,
305
+ compaction: compaction,
311
306
  compactionEntry,
312
307
  ...sessionResponseBody(updatedSession),
313
308
  staticContext: updatedSession.staticContext,
@@ -325,7 +320,7 @@ async function completeSessionCompact(config, body, prepared) {
325
320
  status: 200,
326
321
  body: {
327
322
  success: true,
328
- compaction: normalizedCompaction,
323
+ compaction: compaction,
329
324
  compactionEntry,
330
325
  ...sessionResponseBody(updatedSession),
331
326
  staticContext: updatedSession.staticContext,
@@ -736,7 +731,12 @@ function toProxyEvent(event) {
736
731
  case "toolcall_end":
737
732
  return { type: "toolcall_end", contentIndex: event.contentIndex };
738
733
  case "done":
739
- return { type: "done", reason: event.reason, usage: event.message.usage };
734
+ return {
735
+ type: "done",
736
+ reason: event.reason,
737
+ usage: event.message.usage,
738
+ deferred: event.message.deferred,
739
+ };
740
740
  case "error":
741
741
  return {
742
742
  type: "error",
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer, type Server as HttpServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { createRequire } from \"node:module\";\nimport {\n\ttype CompactionPreparationOptions,\n\ttype CompactionSettings,\n\ttype CompactResult,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\ttype ProxyAssistantMessageEvent,\n\tprepareCompaction,\n\ttype SessionTreeEntry,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tcreateModels,\n\tcreateProvider,\n\ttype Message,\n\ttype Model,\n\ttype SimpleStreamOptions,\n} from \"@earendil-works/pi-ai\";\nimport { streamSimple } from \"@earendil-works/pi-ai/compat\";\nimport type { ServerConfig } from \"./config.ts\";\nimport { loadConfig } from \"./config.ts\";\nimport { encodeErrorEvent, encodeProxyEvent } from \"./event-encoding.ts\";\nimport { ReceiveUploadError, receiveUpload } from \"./receive-upload.ts\";\nimport { CHUNK_ENDPOINT, type RequestChunkBody, receiveRequestChunk } from \"./request-chunks.ts\";\nimport { deletePersistedSession, loadPersistedSessions, savePersistedSession } from \"./session-persistence.ts\";\nimport {\n\tappendCompactionEntry,\n\tappendMessages,\n\tappendSessionEntries,\n\tdeleteSession as deleteSessionFromStore,\n\tdropLastAssistantError,\n\tgetOrCreateSession,\n\tgetSession,\n\tgetSessionBranch,\n\tlistSessions,\n\treplaceMessages,\n\treplaceSessionTree,\n\ttype SessionState,\n\ttype SessionStaticContext,\n\tsetStaticContext,\n\tswitchSessionLeaf,\n} from \"./session-store.ts\";\n\nexport { loadConfig, type ServerConfig } from \"./config.ts\";\n\ninterface PackageMetadata {\n\tversion: string;\n}\n\nconst packageMetadata = createRequire(import.meta.url)(\"../package.json\") as PackageMetadata;\nconst PI_SERVER_VERSION = packageMetadata.version;\n\ninterface SessionInitBody {\n\tsessionId: string;\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface StreamRequestBody {\n\tsessionId: string;\n\trunId?: string;\n\tmodel: Model<any>;\n\toptions?: SimpleStreamOptions;\n\tstaticContext?: SessionStaticContext;\n\tephemeralMessages?: Message[];\n\tcontextOverlay?: Message[];\n}\n\ninterface SessionSyncBody {\n\tsessionId: string;\n\tmessages: Message[];\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface SessionAppendBody {\n\tsessionId: string;\n\tmessages: Message[];\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface SessionTreeSyncBody {\n\tsessionId: string;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface SessionTreeSwitchBody {\n\tsessionId: string;\n\tleafId: string | null;\n}\n\ninterface SessionCompactBody {\n\tsessionId: string;\n\tmodel: Model<any>;\n\toptions?: SimpleStreamOptions;\n\tsettings?: CompactionSettings;\n\tpreparation?: CompactionPreparationOptions;\n\tcustomInstructions?: string;\n\tbaseTreeHash?: string;\n\tfullResponse?: boolean;\n\tstreamResponse?: boolean;\n}\n\nfunction createRequestModels(model: Model<any>, options: SimpleStreamOptions) {\n\tconst models = createModels();\n\tmodels.setProvider(\n\t\tcreateProvider({\n\t\t\tid: model.provider,\n\t\t\tname: model.provider,\n\t\t\tmodels: [model],\n\t\t\tauth: {\n\t\t\t\tapiKey: {\n\t\t\t\t\tname: \"pi-server request auth\",\n\t\t\t\t\tresolve: async () => ({ auth: { apiKey: options.apiKey, headers: options.headers } }),\n\t\t\t\t},\n\t\t\t},\n\t\t\tapi: {\n\t\t\t\tstream: (requestModel, context, streamOptions) => streamSimple(requestModel, context, streamOptions),\n\t\t\t\tstreamSimple: (requestModel, context, streamOptions) => streamSimple(requestModel, context, streamOptions),\n\t\t\t},\n\t\t}),\n\t);\n\treturn models;\n}\n\ninterface SessionIdBody {\n\tsessionId: string;\n}\n\ninterface StreamRunRecord {\n\tsessionId: string;\n\trunId: string;\n\tstatus: \"running\" | \"completed\" | \"failed\";\n\tevents: ProxyAssistantMessageEvent[];\n\tmessage?: AssistantMessage;\n\terrorMessage?: string;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nconst STREAM_HEARTBEAT = \": keep-alive\\n\\n\";\nconst JSON_HEARTBEAT = \" \\n\";\nconst STREAM_HEARTBEAT_INTERVAL_MS = 25_000;\nconst streamRuns = new Map<string, StreamRunRecord>();\n\nfunction readBody(req: IncomingMessage): Promise<string> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst chunks: Buffer[] = [];\n\t\treq.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n\t\treq.on(\"end\", () => resolve(Buffer.concat(chunks).toString(\"utf-8\")));\n\t\treq.on(\"error\", reject);\n\t});\n}\n\nfunction sendJson(res: ServerResponse, status: number, body: unknown): void {\n\tconst data = JSON.stringify(body);\n\tres.writeHead(status, { \"Content-Type\": \"application/json\", \"Content-Length\": Buffer.byteLength(data) });\n\tres.end(data);\n}\n\nfunction logRequestError(req: IncomingMessage, error: unknown): void {\n\tconst message = error instanceof Error ? error.stack || error.message : String(error);\n\tconsole.error(`${req.method ?? \"UNKNOWN\"} ${req.url ?? \"/\"} failed: ${message}`);\n}\n\nfunction authenticate(config: ServerConfig, req: IncomingMessage): boolean {\n\tif (!config.authToken) return true;\n\tconst header = req.headers.authorization;\n\tif (!header) return false;\n\tconst token = header.startsWith(\"Bearer \") ? header.slice(7) : header;\n\treturn token === config.authToken;\n}\n\ninterface ResolvedStream {\n\tmodel: Model<any>;\n\toptions: SimpleStreamOptions;\n}\n\nfunction sessionResponseBody(session: SessionState) {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContextHash: session.staticContextHash,\n\t\ttreeHash: session.treeHash,\n\t\tmessageCount: session.messages.length,\n\t\tentryCount: session.entries.length,\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t};\n}\n\nfunction persistSession(config: ServerConfig, session: SessionState): void {\n\tsavePersistedSession(config.sessionStoreDir, session);\n}\n\nfunction runKey(sessionId: string, runId: string): string {\n\treturn `${sessionId}\\0${runId}`;\n}\n\nfunction getStreamRun(sessionId: string, runId: string): StreamRunRecord | undefined {\n\treturn streamRuns.get(runKey(sessionId, runId));\n}\n\nfunction startStreamRun(sessionId: string, runId: string): StreamRunRecord {\n\tconst existing = getStreamRun(sessionId, runId);\n\tif (existing?.status === \"completed\" || existing?.status === \"failed\") return existing;\n\tconst now = Date.now();\n\tconst run: StreamRunRecord = existing ?? {\n\t\tsessionId,\n\t\trunId,\n\t\tstatus: \"running\",\n\t\tevents: [],\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t};\n\trun.status = \"running\";\n\trun.updatedAt = now;\n\tstreamRuns.set(runKey(sessionId, runId), run);\n\treturn run;\n}\n\nfunction recordStreamRunEvent(run: StreamRunRecord | undefined, event: ProxyAssistantMessageEvent): void {\n\tif (!run) return;\n\trun.events.push(event);\n\trun.updatedAt = Date.now();\n}\n\nfunction completeStreamRun(run: StreamRunRecord | undefined, message: AssistantMessage): void {\n\tif (!run) return;\n\trun.status = \"completed\";\n\trun.message = message;\n\trun.errorMessage = undefined;\n\trun.updatedAt = Date.now();\n}\n\nfunction failStreamRun(run: StreamRunRecord | undefined, errorMessage: string): void {\n\tif (!run) return;\n\trun.status = \"failed\";\n\trun.errorMessage = errorMessage;\n\trun.updatedAt = Date.now();\n}\n\nfunction sessionHistoryFullResponseBody(session: SessionState, baseMessageCount: number) {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tstaticContextHash: session.staticContextHash,\n\t\ttreeHash: session.treeHash,\n\t\tmessageCount: session.messages.length,\n\t\tentryCount: session.entries.length,\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tentries: session.entries,\n\t\tbaseMessageCount,\n\t\tmessages: session.messages.slice(baseMessageCount),\n\t};\n}\n\nfunction sessionTreePatchResponseBody(\n\tsession: SessionState,\n\tbaseMessageCount: number,\n\tentriesFrom: number,\n\tbaseRevision: number | undefined,\n) {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tstaticContextHash: session.staticContextHash,\n\t\ttreeHash: session.treeHash,\n\t\tmessageCount: session.messages.length,\n\t\tentryCount: session.entries.length,\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tbaseMessageCount,\n\t\tmessages: session.messages.slice(baseMessageCount),\n\t\ttreePatch: {\n\t\t\tentriesFrom,\n\t\t\tbaseRevision,\n\t\t\tentries: session.entries.slice(entriesFrom),\n\t\t\tleafId: session.leafId,\n\t\t\trevision: session.revision,\n\t\t},\n\t};\n}\n\ntype PreparedCompaction = Parameters<typeof compact>[0];\n\ninterface PreparedSessionCompact {\n\tsession: SessionState;\n\tpreparation: PreparedCompaction;\n\toptions: SimpleStreamOptions;\n}\n\ninterface SessionCompactSuccessBody {\n\tsuccess: true;\n\tcompaction: CompactResult;\n\tcompactionEntry: SessionTreeEntry;\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash: string;\n\tmessageCount: number;\n\tentryCount: number;\n\tleafId: string | null;\n\trevision: number;\n\tstaticContext?: SessionStaticContext;\n\ttreePatch?: {\n\t\tbaseTreeHash: string;\n\t\tentriesFrom: number;\n\t\tentries: SessionTreeEntry[];\n\t\tleafId: string | null;\n\t\trevision: number;\n\t};\n\tentries?: SessionTreeEntry[];\n\tmessages?: Message[];\n}\n\ninterface SessionCompactHttpResponse {\n\tstatus: number;\n\tbody: { error: string } | SessionCompactSuccessBody;\n}\n\nexport function resolveStreamOptions(\n\t_config: ServerConfig,\n\tmodel: Model<any>,\n\tbody: StreamRequestBody,\n): ResolvedStream {\n\treturn { model, options: { ...(body.options ?? {}) } };\n}\n\nfunction handleSessionInit(config: ServerConfig, body: SessionInitBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t} else {\n\t\tgetOrCreateSession(body.sessionId);\n\t}\n\tconst session = getSession(body.sessionId)!;\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionUpdate(\n\tconfig: ServerConfig,\n\tbody: SessionInitBody & { staticContext: SessionStaticContext },\n\tres: ServerResponse,\n): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!body.staticContext) {\n\t\tsendJson(res, 400, { error: \"staticContext is required for update\" });\n\t\treturn;\n\t}\n\tsetStaticContext(body.sessionId, body.staticContext);\n\tconst session = getSession(body.sessionId)!;\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionSync(config: ServerConfig, body: SessionSyncBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.messages)) {\n\t\tsendJson(res, 400, { error: \"messages is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = replaceMessages(body.sessionId, body.messages);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionAppend(config: ServerConfig, body: SessionAppendBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.messages)) {\n\t\tsendJson(res, 400, { error: \"messages is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = appendMessages(body.sessionId, body.messages);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionTreeSync(config: ServerConfig, body: SessionTreeSyncBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.entries)) {\n\t\tsendJson(res, 400, { error: \"entries is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = replaceSessionTree(body.sessionId, body.entries, body.leafId ?? null);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionTreeAppend(config: ServerConfig, body: SessionTreeSyncBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.entries)) {\n\t\tsendJson(res, 400, { error: \"entries is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = appendSessionEntries(body.sessionId, body.entries, body.leafId ?? null);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionTreeSwitch(config: ServerConfig, body: SessionTreeSwitchBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tconst session = switchSessionLeaf(body.sessionId, body.leafId ?? null);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction prepareSessionCompact(body: SessionCompactBody): PreparedSessionCompact | SessionCompactHttpResponse {\n\tif (!body.sessionId) {\n\t\treturn { status: 400, body: { error: \"sessionId is required\" } };\n\t}\n\tif (!body.model) {\n\t\treturn { status: 400, body: { error: \"model is required\" } };\n\t}\n\n\tconst session = getSession(body.sessionId);\n\tif (!session) {\n\t\treturn { status: 404, body: { error: \"session not found\" } };\n\t}\n\n\tconst entries = getSessionBranch(session);\n\tconst preparationResult = prepareCompaction(entries, body.settings ?? DEFAULT_COMPACTION_SETTINGS, body.preparation);\n\tif (!preparationResult.ok) {\n\t\treturn { status: 400, body: { error: preparationResult.error.message } };\n\t}\n\tif (!preparationResult.value) {\n\t\treturn { status: 400, body: { error: \"Nothing to compact\" } };\n\t}\n\n\tconst options = body.options ?? {};\n\treturn {\n\t\tsession,\n\t\tpreparation: preparationResult.value,\n\t\toptions,\n\t};\n}\n\nasync function completeSessionCompact(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tprepared: PreparedSessionCompact,\n): Promise<SessionCompactHttpResponse> {\n\tconst result = await compact(\n\t\tprepared.preparation,\n\t\tcreateRequestModels(body.model, prepared.options),\n\t\tbody.model,\n\t\tbody.customInstructions,\n\t\tundefined,\n\t\tprepared.options.reasoning,\n\t);\n\tif (!result.ok) {\n\t\treturn { status: 500, body: { error: result.error.message } };\n\t}\n\n\tconst baseTreeHash = prepared.session.treeHash;\n\tconst baseEntryCount = prepared.session.entries.length;\n\tconst compaction = result.value;\n\tconst firstKeptEntryId = compaction.firstKeptEntryId;\n\tif (!firstKeptEntryId) {\n\t\treturn { status: 500, body: { error: \"Compaction result is missing firstKeptEntryId\" } };\n\t}\n\tconst normalizedCompaction = { ...compaction, firstKeptEntryId };\n\tconst { session: updatedSession, entry: compactionEntry } = appendCompactionEntry(\n\t\tbody.sessionId,\n\t\tnormalizedCompaction,\n\t);\n\tpersistSession(config, updatedSession);\n\tif (!body.fullResponse && body.baseTreeHash === baseTreeHash) {\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\tbody: {\n\t\t\t\tsuccess: true,\n\t\t\t\tcompaction: normalizedCompaction satisfies CompactResult,\n\t\t\t\tcompactionEntry,\n\t\t\t\t...sessionResponseBody(updatedSession),\n\t\t\t\tstaticContext: updatedSession.staticContext,\n\t\t\t\ttreePatch: {\n\t\t\t\t\tbaseTreeHash,\n\t\t\t\t\tentriesFrom: baseEntryCount,\n\t\t\t\t\tentries: [compactionEntry],\n\t\t\t\t\tleafId: updatedSession.leafId,\n\t\t\t\t\trevision: updatedSession.revision,\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\treturn {\n\t\tstatus: 200,\n\t\tbody: {\n\t\t\tsuccess: true,\n\t\t\tcompaction: normalizedCompaction satisfies CompactResult,\n\t\t\tcompactionEntry,\n\t\t\t...sessionResponseBody(updatedSession),\n\t\t\tstaticContext: updatedSession.staticContext,\n\t\t\tentries: updatedSession.entries,\n\t\t\tmessages: updatedSession.messages,\n\t\t},\n\t};\n}\n\nfunction writeServerSentEvent(res: ServerResponse, event: string, body: unknown): void {\n\tres.write(`event: ${event}\\n`);\n\tres.write(`data: ${JSON.stringify(body)}\\n\\n`);\n}\n\nasync function handleSessionCompactStream(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tprepared: PreparedSessionCompact,\n\tres: ServerResponse,\n): Promise<void> {\n\tres.writeHead(200, {\n\t\t\"Content-Type\": \"text/event-stream\",\n\t\t\"Cache-Control\": \"no-cache\",\n\t\tConnection: \"keep-alive\",\n\t});\n\tres.flushHeaders();\n\tres.write(STREAM_HEARTBEAT);\n\n\tconst heartbeat = setInterval(() => {\n\t\tif (!res.writableEnded) {\n\t\t\tres.write(STREAM_HEARTBEAT);\n\t\t}\n\t}, STREAM_HEARTBEAT_INTERVAL_MS);\n\theartbeat.unref();\n\n\ttry {\n\t\tconst result = await completeSessionCompact(config, body, prepared);\n\t\twriteServerSentEvent(res, result.status >= 400 ? \"error\" : \"result\", result.body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\twriteServerSentEvent(res, \"error\", { error: message });\n\t} finally {\n\t\tclearInterval(heartbeat);\n\t\tres.end();\n\t}\n}\n\nasync function handleSessionCompactJsonStream(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tprepared: PreparedSessionCompact,\n\tres: ServerResponse,\n): Promise<void> {\n\tres.writeHead(200, {\n\t\t\"Content-Type\": \"application/json\",\n\t\t\"Cache-Control\": \"no-cache\",\n\t\tConnection: \"keep-alive\",\n\t});\n\tres.flushHeaders();\n\tres.write(JSON_HEARTBEAT);\n\n\tconst heartbeat = setInterval(() => {\n\t\tif (!res.writableEnded) {\n\t\t\tres.write(JSON_HEARTBEAT);\n\t\t}\n\t}, STREAM_HEARTBEAT_INTERVAL_MS);\n\theartbeat.unref();\n\n\ttry {\n\t\tconst result = await completeSessionCompact(config, body, prepared);\n\t\tres.write(JSON.stringify(result.body));\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tres.write(JSON.stringify({ error: message }));\n\t} finally {\n\t\tclearInterval(heartbeat);\n\t\tres.end();\n\t}\n}\n\nasync function handleSessionCompact(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tres: ServerResponse,\n): Promise<void> {\n\tconst prepared = prepareSessionCompact(body);\n\tif (\"status\" in prepared) {\n\t\tsendJson(res, prepared.status, prepared.body);\n\t\treturn;\n\t}\n\n\tif (body.streamResponse) {\n\t\tawait handleSessionCompactStream(config, body, prepared, res);\n\t\treturn;\n\t}\n\n\tawait handleSessionCompactJsonStream(config, body, prepared, res);\n}\n\nfunction handleDropLastAssistantError(config: ServerConfig, body: SessionIdBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tconst dropped = dropLastAssistantError(body.sessionId);\n\tconst session = getSession(body.sessionId);\n\tif (session) {\n\t\tpersistSession(config, session);\n\t}\n\tconst messageCount = session?.messages.length ?? 0;\n\tsendJson(res, 200, { success: true, dropped, messageCount });\n}\n\nfunction handleSessionHistory(\n\tsessionId: string,\n\tfrom: number | undefined,\n\tentriesFrom: number | undefined,\n\trevision: number | undefined,\n\tbaseTreeHash: string | undefined,\n\tres: ServerResponse,\n): void {\n\tconst session = getSession(sessionId);\n\tif (!session) {\n\t\tsendJson(res, 404, { error: \"session not found\" });\n\t\treturn;\n\t}\n\tconst baseMessageCount = from ?? 0;\n\tif (\n\t\tentriesFrom !== undefined &&\n\t\tentriesFrom <= session.entries.length &&\n\t\t(revision === undefined || revision <= session.revision) &&\n\t\t(baseTreeHash === undefined || baseTreeHash === session.prefixHashes[entriesFrom])\n\t) {\n\t\tsendJson(res, 200, sessionTreePatchResponseBody(session, baseMessageCount, entriesFrom, revision));\n\t\treturn;\n\t}\n\tsendJson(res, 200, sessionHistoryFullResponseBody(session, baseMessageCount));\n}\n\nfunction handleSessionRun(sessionId: string, runId: string, res: ServerResponse): void {\n\tconst run = getStreamRun(sessionId, runId);\n\tif (!run) {\n\t\tsendJson(res, 404, { error: \"run not found\" });\n\t\treturn;\n\t}\n\tsendJson(res, 200, run);\n}\n\nexport function buildStreamContext(\n\tsession: SessionState,\n\tbody: Pick<StreamRequestBody, \"contextOverlay\" | \"ephemeralMessages\">,\n): Context {\n\tconst messages = body.contextOverlay ?? [...session.messages, ...(body.ephemeralMessages ?? [])];\n\treturn {\n\t\tsystemPrompt: session.staticContext?.systemPrompt,\n\t\tmessages,\n\t\ttools: session.staticContext?.tools,\n\t};\n}\n\nfunction handleStream(config: ServerConfig, body: StreamRequestBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!body.model) {\n\t\tsendJson(res, 400, { error: \"model is required\" });\n\t\treturn;\n\t}\n\n\tconst session = getOrCreateSession(body.sessionId);\n\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t\tpersistSession(config, session);\n\t}\n\n\tif (!session.staticContext && !body.staticContext) {\n\t\tsendJson(res, 400, { error: \"Session has no static context. Initialize with /api/session/init first.\" });\n\t\treturn;\n\t}\n\n\tif (body.ephemeralMessages !== undefined && !Array.isArray(body.ephemeralMessages)) {\n\t\tsendJson(res, 400, { error: \"ephemeralMessages must be an array\" });\n\t\treturn;\n\t}\n\tif (body.contextOverlay !== undefined && !Array.isArray(body.contextOverlay)) {\n\t\tsendJson(res, 400, { error: \"contextOverlay must be an array\" });\n\t\treturn;\n\t}\n\n\tconst context = buildStreamContext(session, body);\n\tconst existingRun = body.runId ? getStreamRun(body.sessionId, body.runId) : undefined;\n\n\tconst { model: resolvedModel, options: streamOptions } = resolveStreamOptions(config, body.model, body);\n\n\tres.writeHead(200, {\n\t\t\"Content-Type\": \"text/event-stream\",\n\t\t\"Cache-Control\": \"no-cache\",\n\t\tConnection: \"keep-alive\",\n\t});\n\tres.flushHeaders();\n\tres.write(STREAM_HEARTBEAT);\n\n\tif (existingRun?.status === \"completed\") {\n\t\tfor (const event of existingRun.events) {\n\t\t\tres.write(encodeProxyEvent(event));\n\t\t}\n\t\tres.end();\n\t\treturn;\n\t}\n\n\tconst run = body.runId ? startStreamRun(body.sessionId, body.runId) : undefined;\n\n\tconst heartbeat = setInterval(() => {\n\t\tif (!res.writableEnded) {\n\t\t\tres.write(STREAM_HEARTBEAT);\n\t\t}\n\t}, STREAM_HEARTBEAT_INTERVAL_MS);\n\theartbeat.unref();\n\n\tlet stream: AsyncIterable<AssistantMessageEvent>;\n\ttry {\n\t\tstream = streamSimple(resolvedModel, context, streamOptions);\n\t} catch (err) {\n\t\tclearInterval(heartbeat);\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tfailStreamRun(run, message);\n\t\tres.write(encodeErrorEvent(message));\n\t\tres.end();\n\t\treturn;\n\t}\n\n\t(async () => {\n\t\ttry {\n\t\t\tfor await (const event of stream) {\n\t\t\t\tconst proxyEvent = toProxyEvent(event);\n\t\t\t\tif (proxyEvent) {\n\t\t\t\t\trecordStreamRunEvent(run, proxyEvent);\n\t\t\t\t\tres.write(encodeProxyEvent(proxyEvent));\n\t\t\t\t}\n\t\t\t\tif (event.type === \"done\") {\n\t\t\t\t\tcompleteStreamRun(run, event.message);\n\t\t\t\t} else if (event.type === \"error\") {\n\t\t\t\t\tfailStreamRun(run, event.error.errorMessage ?? event.reason);\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tclearInterval(heartbeat);\n\t\t}\n\n\t\tres.end();\n\t})().catch((err) => {\n\t\tclearInterval(heartbeat);\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tfailStreamRun(run, message);\n\t\tres.write(encodeErrorEvent(message));\n\t\tres.end();\n\t});\n}\n\nasync function handlePostRequest(\n\tconfig: ServerConfig,\n\tpathname: string,\n\tbody: unknown,\n\tres: ServerResponse,\n): Promise<boolean> {\n\tif (pathname === \"/api/receive\") {\n\t\ttry {\n\t\t\tsendJson(res, 200, receiveUpload(config.uploadDir, body));\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof ReceiveUploadError)) throw error;\n\t\t\tsendJson(res, error.status, { error: error.message });\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/init\") {\n\t\thandleSessionInit(config, body as SessionInitBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/update\") {\n\t\thandleSessionUpdate(config, body as SessionInitBody & { staticContext: SessionStaticContext }, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/sync\") {\n\t\thandleSessionSync(config, body as SessionSyncBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/append\") {\n\t\thandleSessionAppend(config, body as SessionAppendBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/tree/sync\") {\n\t\thandleSessionTreeSync(config, body as SessionTreeSyncBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/tree/append\") {\n\t\thandleSessionTreeAppend(config, body as SessionTreeSyncBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/tree/switch\") {\n\t\thandleSessionTreeSwitch(config, body as SessionTreeSwitchBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/drop-last-assistant-error\") {\n\t\thandleDropLastAssistantError(config, body as SessionIdBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/compact\") {\n\t\tawait handleSessionCompact(config, body as SessionCompactBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/stream\") {\n\t\thandleStream(config, body as StreamRequestBody, res);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nexport function createPiServer(configOverride?: Partial<ServerConfig>): HttpServer {\n\tconst config = loadConfig(configOverride);\n\tloadPersistedSessions(config.sessionStoreDir);\n\n\tconst server = createServer(async (req, res) => {\n\t\tconst url = new URL(req.url ?? \"/\", `http://${config.host}:${config.port}`);\n\n\t\tif (req.method === \"GET\" && url.pathname === \"/health\") {\n\t\t\tsendJson(res, 200, { status: \"ok\" });\n\t\t\treturn;\n\t\t}\n\n\t\tif (!authenticate(config, req)) {\n\t\t\tconst body =\n\t\t\t\treq.method === \"GET\" && url.pathname === \"/\"\n\t\t\t\t\t? { error: \"Unauthorized\", version: PI_SERVER_VERSION }\n\t\t\t\t\t: { error: \"Unauthorized\" };\n\t\t\tsendJson(res, 401, body);\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"GET\" && url.pathname === \"/api/sessions\") {\n\t\t\tsendJson(res, 200, { sessions: listSessions() });\n\t\t\treturn;\n\t\t}\n\n\t\tconst runMatch = /^\\/api\\/session\\/([^/]+)\\/runs\\/([^/]+)$/.exec(url.pathname);\n\t\tif (req.method === \"GET\" && runMatch) {\n\t\t\thandleSessionRun(decodeURIComponent(runMatch[1]), decodeURIComponent(runMatch[2]), res);\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"GET\" && url.pathname.startsWith(\"/api/session/\") && url.pathname.endsWith(\"/history\")) {\n\t\t\tconst encodedSessionId = url.pathname.slice(\"/api/session/\".length, -\"/history\".length);\n\t\t\tconst fromParam = url.searchParams.get(\"from\");\n\t\t\tconst from = fromParam === null ? undefined : Number(fromParam);\n\t\t\tif (from !== undefined && (!Number.isInteger(from) || from < 0)) {\n\t\t\t\tsendJson(res, 400, { error: \"from must be a non-negative integer\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst entriesFromParam = url.searchParams.get(\"entriesFrom\");\n\t\t\tconst entriesFrom = entriesFromParam === null ? undefined : Number(entriesFromParam);\n\t\t\tif (entriesFrom !== undefined && (!Number.isInteger(entriesFrom) || entriesFrom < 0)) {\n\t\t\t\tsendJson(res, 400, { error: \"entriesFrom must be a non-negative integer\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst revisionParam = url.searchParams.get(\"revision\");\n\t\t\tconst revision = revisionParam === null ? undefined : Number(revisionParam);\n\t\t\tif (revision !== undefined && (!Number.isInteger(revision) || revision < 0)) {\n\t\t\t\tsendJson(res, 400, { error: \"revision must be a non-negative integer\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thandleSessionHistory(\n\t\t\t\tdecodeURIComponent(encodedSessionId),\n\t\t\t\tfrom,\n\t\t\t\tentriesFrom,\n\t\t\t\trevision,\n\t\t\t\turl.searchParams.get(\"baseTreeHash\") ?? undefined,\n\t\t\t\tres,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"POST\" && url.pathname === CHUNK_ENDPOINT) {\n\t\t\ttry {\n\t\t\t\tconst body = JSON.parse(await readBody(req)) as RequestChunkBody;\n\t\t\t\tconst chunkResult = receiveRequestChunk(body);\n\t\t\t\tif (!chunkResult.complete) {\n\t\t\t\t\tsendJson(res, 200, chunkResult.ack);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait handlePostRequest(config, chunkResult.target, JSON.parse(chunkResult.bodyJson) as unknown, res);\n\t\t\t} catch (err) {\n\t\t\t\tlogRequestError(req, err);\n\t\t\t\tif (!res.headersSent) {\n\t\t\t\t\tsendJson(res, 400, { error: err instanceof Error ? err.message : String(err) });\n\t\t\t\t} else {\n\t\t\t\t\tres.write(encodeErrorEvent(err instanceof Error ? err.message : String(err)));\n\t\t\t\t\tres.end();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"POST\") {\n\t\t\ttry {\n\t\t\t\tconst body = JSON.parse(await readBody(req)) as unknown;\n\t\t\t\tif (await handlePostRequest(config, url.pathname, body, res)) return;\n\t\t\t} catch (err) {\n\t\t\t\tlogRequestError(req, err);\n\t\t\t\tif (!res.headersSent) {\n\t\t\t\t\tsendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });\n\t\t\t\t} else {\n\t\t\t\t\tres.write(encodeErrorEvent(err instanceof Error ? err.message : String(err)));\n\t\t\t\t\tres.end();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (req.method === \"DELETE\" && url.pathname.startsWith(\"/api/session/\")) {\n\t\t\tconst sessionId = decodeURIComponent(url.pathname.slice(\"/api/session/\".length));\n\t\t\tdeleteSessionFromStore(sessionId);\n\t\t\tdeletePersistedSession(config.sessionStoreDir, sessionId);\n\t\t\tsendJson(res, 200, { deleted: sessionId });\n\t\t\treturn;\n\t\t}\n\n\t\tsendJson(res, 404, { error: \"Not found\" });\n\t});\n\n\treturn server;\n}\n\nfunction toProxyEvent(event: AssistantMessageEvent): ProxyAssistantMessageEvent | undefined {\n\tswitch (event.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\" };\n\t\tcase \"text_start\":\n\t\t\treturn { type: \"text_start\", contentIndex: event.contentIndex };\n\t\tcase \"text_delta\":\n\t\t\treturn { type: \"text_delta\", contentIndex: event.contentIndex, delta: event.delta };\n\t\tcase \"text_end\":\n\t\t\treturn {\n\t\t\t\ttype: \"text_end\",\n\t\t\t\tcontentIndex: event.contentIndex,\n\t\t\t\tcontentSignature:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"text\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { textSignature?: string }).textSignature\n\t\t\t\t\t\t: undefined,\n\t\t\t};\n\t\tcase \"thinking_start\":\n\t\t\treturn { type: \"thinking_start\", contentIndex: event.contentIndex };\n\t\tcase \"thinking_delta\":\n\t\t\treturn { type: \"thinking_delta\", contentIndex: event.contentIndex, delta: event.delta };\n\t\tcase \"thinking_end\":\n\t\t\treturn {\n\t\t\t\ttype: \"thinking_end\",\n\t\t\t\tcontentIndex: event.contentIndex,\n\t\t\t\tcontentSignature:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"thinking\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { thinkingSignature?: string }).thinkingSignature\n\t\t\t\t\t\t: undefined,\n\t\t\t};\n\t\tcase \"toolcall_start\":\n\t\t\treturn {\n\t\t\t\ttype: \"toolcall_start\",\n\t\t\t\tcontentIndex: event.contentIndex,\n\t\t\t\tid:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"toolCall\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { id: string }).id\n\t\t\t\t\t\t: \"\",\n\t\t\t\ttoolName:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"toolCall\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { name: string }).name\n\t\t\t\t\t\t: \"\",\n\t\t\t};\n\t\tcase \"toolcall_delta\":\n\t\t\treturn { type: \"toolcall_delta\", contentIndex: event.contentIndex, delta: event.delta };\n\t\tcase \"toolcall_end\":\n\t\t\treturn { type: \"toolcall_end\", contentIndex: event.contentIndex };\n\t\tcase \"done\":\n\t\t\treturn { type: \"done\", reason: event.reason, usage: event.message.usage };\n\t\tcase \"error\":\n\t\t\treturn {\n\t\t\t\ttype: \"error\",\n\t\t\t\treason: event.reason,\n\t\t\t\terrorMessage: event.error.errorMessage,\n\t\t\t\tusage: event.error.usage,\n\t\t\t};\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nexport function startServer(configOverride?: Partial<ServerConfig>): HttpServer {\n\tconst config = loadConfig(configOverride);\n\tconst server = createPiServer(configOverride);\n\tserver.listen(config.port, config.host, () => {\n\t\tconsole.log(`pi-server listening on ${config.host}:${config.port}`);\n\t});\n\treturn server;\n}\n"]}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer, type Server as HttpServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { createRequire } from \"node:module\";\nimport {\n\ttype CompactionPreparationOptions,\n\ttype CompactionSettings,\n\tcompactLegacy,\n\tDEFAULT_COMPACTION_SETTINGS,\n\ttype LegacyCompactResult,\n\ttype ProxyAssistantMessageEvent,\n\tprepareLegacyCompaction,\n\ttype SessionTreeEntry,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tcreateModels,\n\tcreateProvider,\n\ttype Message,\n\ttype Model,\n\ttype SimpleStreamOptions,\n} from \"@earendil-works/pi-ai\";\nimport { streamSimple } from \"@earendil-works/pi-ai/compat\";\nimport type { ServerConfig } from \"./config.ts\";\nimport { loadConfig } from \"./config.ts\";\nimport { encodeErrorEvent, encodeProxyEvent } from \"./event-encoding.ts\";\nimport { ReceiveUploadError, receiveUpload } from \"./receive-upload.ts\";\nimport { CHUNK_ENDPOINT, type RequestChunkBody, receiveRequestChunk } from \"./request-chunks.ts\";\nimport { deletePersistedSession, loadPersistedSessions, savePersistedSession } from \"./session-persistence.ts\";\nimport {\n\tappendCompactionEntry,\n\tappendMessages,\n\tappendSessionEntries,\n\tdeleteSession as deleteSessionFromStore,\n\tdropLastAssistantError,\n\tgetOrCreateSession,\n\tgetSession,\n\tgetSessionBranch,\n\tlistSessions,\n\treplaceMessages,\n\treplaceSessionTree,\n\ttype SessionState,\n\ttype SessionStaticContext,\n\tsetStaticContext,\n\tswitchSessionLeaf,\n} from \"./session-store.ts\";\n\nexport { loadConfig, type ServerConfig } from \"./config.ts\";\n\ninterface PackageMetadata {\n\tversion: string;\n}\n\nconst packageMetadata = createRequire(import.meta.url)(\"../package.json\") as PackageMetadata;\nconst PI_SERVER_VERSION = packageMetadata.version;\n\ninterface SessionInitBody {\n\tsessionId: string;\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface StreamRequestBody {\n\tsessionId: string;\n\trunId?: string;\n\tmodel: Model<any>;\n\toptions?: SimpleStreamOptions;\n\tstaticContext?: SessionStaticContext;\n\tephemeralMessages?: Message[];\n\tcontextOverlay?: Message[];\n}\n\ninterface SessionSyncBody {\n\tsessionId: string;\n\tmessages: Message[];\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface SessionAppendBody {\n\tsessionId: string;\n\tmessages: Message[];\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface SessionTreeSyncBody {\n\tsessionId: string;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tstaticContext?: SessionStaticContext;\n}\n\ninterface SessionTreeSwitchBody {\n\tsessionId: string;\n\tleafId: string | null;\n}\n\ninterface SessionCompactBody {\n\tsessionId: string;\n\tmodel: Model<any>;\n\toptions?: SimpleStreamOptions;\n\tsettings?: CompactionSettings;\n\tpreparation?: CompactionPreparationOptions;\n\tcustomInstructions?: string;\n\tbaseTreeHash?: string;\n\tfullResponse?: boolean;\n\tstreamResponse?: boolean;\n}\n\nfunction createRequestModels(model: Model<any>, options: SimpleStreamOptions) {\n\tconst models = createModels();\n\tmodels.setProvider(\n\t\tcreateProvider({\n\t\t\tid: model.provider,\n\t\t\tname: model.provider,\n\t\t\tmodels: [model],\n\t\t\tauth: {\n\t\t\t\tapiKey: {\n\t\t\t\t\tname: \"pi-server request auth\",\n\t\t\t\t\tresolve: async () => ({ auth: { apiKey: options.apiKey, headers: options.headers } }),\n\t\t\t\t},\n\t\t\t},\n\t\t\tapi: {\n\t\t\t\tstream: (requestModel, context, streamOptions) => streamSimple(requestModel, context, streamOptions),\n\t\t\t\tstreamSimple: (requestModel, context, streamOptions) => streamSimple(requestModel, context, streamOptions),\n\t\t\t},\n\t\t}),\n\t);\n\treturn models;\n}\n\ninterface SessionIdBody {\n\tsessionId: string;\n}\n\ninterface StreamRunRecord {\n\tsessionId: string;\n\trunId: string;\n\tstatus: \"running\" | \"completed\" | \"failed\";\n\tevents: ProxyAssistantMessageEvent[];\n\tmessage?: AssistantMessage;\n\terrorMessage?: string;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nconst STREAM_HEARTBEAT = \": keep-alive\\n\\n\";\nconst JSON_HEARTBEAT = \" \\n\";\nconst STREAM_HEARTBEAT_INTERVAL_MS = 25_000;\nconst streamRuns = new Map<string, StreamRunRecord>();\n\nfunction readBody(req: IncomingMessage): Promise<string> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst chunks: Buffer[] = [];\n\t\treq.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n\t\treq.on(\"end\", () => resolve(Buffer.concat(chunks).toString(\"utf-8\")));\n\t\treq.on(\"error\", reject);\n\t});\n}\n\nfunction sendJson(res: ServerResponse, status: number, body: unknown): void {\n\tconst data = JSON.stringify(body);\n\tres.writeHead(status, { \"Content-Type\": \"application/json\", \"Content-Length\": Buffer.byteLength(data) });\n\tres.end(data);\n}\n\nfunction logRequestError(req: IncomingMessage, error: unknown): void {\n\tconst message = error instanceof Error ? error.stack || error.message : String(error);\n\tconsole.error(`${req.method ?? \"UNKNOWN\"} ${req.url ?? \"/\"} failed: ${message}`);\n}\n\nfunction authenticate(config: ServerConfig, req: IncomingMessage): boolean {\n\tif (!config.authToken) return true;\n\tconst header = req.headers.authorization;\n\tif (!header) return false;\n\tconst token = header.startsWith(\"Bearer \") ? header.slice(7) : header;\n\treturn token === config.authToken;\n}\n\ninterface ResolvedStream {\n\tmodel: Model<any>;\n\toptions: SimpleStreamOptions;\n}\n\nfunction sessionResponseBody(session: SessionState) {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContextHash: session.staticContextHash,\n\t\ttreeHash: session.treeHash,\n\t\tmessageCount: session.messages.length,\n\t\tentryCount: session.entries.length,\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t};\n}\n\nfunction persistSession(config: ServerConfig, session: SessionState): void {\n\tsavePersistedSession(config.sessionStoreDir, session);\n}\n\nfunction runKey(sessionId: string, runId: string): string {\n\treturn `${sessionId}\\0${runId}`;\n}\n\nfunction getStreamRun(sessionId: string, runId: string): StreamRunRecord | undefined {\n\treturn streamRuns.get(runKey(sessionId, runId));\n}\n\nfunction startStreamRun(sessionId: string, runId: string): StreamRunRecord {\n\tconst existing = getStreamRun(sessionId, runId);\n\tif (existing?.status === \"completed\" || existing?.status === \"failed\") return existing;\n\tconst now = Date.now();\n\tconst run: StreamRunRecord = existing ?? {\n\t\tsessionId,\n\t\trunId,\n\t\tstatus: \"running\",\n\t\tevents: [],\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t};\n\trun.status = \"running\";\n\trun.updatedAt = now;\n\tstreamRuns.set(runKey(sessionId, runId), run);\n\treturn run;\n}\n\nfunction recordStreamRunEvent(run: StreamRunRecord | undefined, event: ProxyAssistantMessageEvent): void {\n\tif (!run) return;\n\trun.events.push(event);\n\trun.updatedAt = Date.now();\n}\n\nfunction completeStreamRun(run: StreamRunRecord | undefined, message: AssistantMessage): void {\n\tif (!run) return;\n\trun.status = \"completed\";\n\trun.message = message;\n\trun.errorMessage = undefined;\n\trun.updatedAt = Date.now();\n}\n\nfunction failStreamRun(run: StreamRunRecord | undefined, errorMessage: string): void {\n\tif (!run) return;\n\trun.status = \"failed\";\n\trun.errorMessage = errorMessage;\n\trun.updatedAt = Date.now();\n}\n\nfunction sessionHistoryFullResponseBody(session: SessionState, baseMessageCount: number) {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tstaticContextHash: session.staticContextHash,\n\t\ttreeHash: session.treeHash,\n\t\tmessageCount: session.messages.length,\n\t\tentryCount: session.entries.length,\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tentries: session.entries,\n\t\tbaseMessageCount,\n\t\tmessages: session.messages.slice(baseMessageCount),\n\t};\n}\n\nfunction sessionTreePatchResponseBody(\n\tsession: SessionState,\n\tbaseMessageCount: number,\n\tentriesFrom: number,\n\tbaseRevision: number | undefined,\n) {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tstaticContextHash: session.staticContextHash,\n\t\ttreeHash: session.treeHash,\n\t\tmessageCount: session.messages.length,\n\t\tentryCount: session.entries.length,\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tbaseMessageCount,\n\t\tmessages: session.messages.slice(baseMessageCount),\n\t\ttreePatch: {\n\t\t\tentriesFrom,\n\t\t\tbaseRevision,\n\t\t\tentries: session.entries.slice(entriesFrom),\n\t\t\tleafId: session.leafId,\n\t\t\trevision: session.revision,\n\t\t},\n\t};\n}\n\ntype PreparedCompaction = Parameters<typeof compactLegacy>[0];\n\ninterface PreparedSessionCompact {\n\tsession: SessionState;\n\tpreparation: PreparedCompaction;\n\toptions: SimpleStreamOptions;\n}\n\ninterface SessionCompactSuccessBody {\n\tsuccess: true;\n\tcompaction: LegacyCompactResult;\n\tcompactionEntry: SessionTreeEntry;\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash: string;\n\tmessageCount: number;\n\tentryCount: number;\n\tleafId: string | null;\n\trevision: number;\n\tstaticContext?: SessionStaticContext;\n\ttreePatch?: {\n\t\tbaseTreeHash: string;\n\t\tentriesFrom: number;\n\t\tentries: SessionTreeEntry[];\n\t\tleafId: string | null;\n\t\trevision: number;\n\t};\n\tentries?: SessionTreeEntry[];\n\tmessages?: Message[];\n}\n\ninterface SessionCompactHttpResponse {\n\tstatus: number;\n\tbody: { error: string } | SessionCompactSuccessBody;\n}\n\nexport function resolveStreamOptions(\n\t_config: ServerConfig,\n\tmodel: Model<any>,\n\tbody: StreamRequestBody,\n): ResolvedStream {\n\treturn { model, options: { ...(body.options ?? {}) } };\n}\n\nfunction handleSessionInit(config: ServerConfig, body: SessionInitBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t} else {\n\t\tgetOrCreateSession(body.sessionId);\n\t}\n\tconst session = getSession(body.sessionId)!;\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionUpdate(\n\tconfig: ServerConfig,\n\tbody: SessionInitBody & { staticContext: SessionStaticContext },\n\tres: ServerResponse,\n): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!body.staticContext) {\n\t\tsendJson(res, 400, { error: \"staticContext is required for update\" });\n\t\treturn;\n\t}\n\tsetStaticContext(body.sessionId, body.staticContext);\n\tconst session = getSession(body.sessionId)!;\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionSync(config: ServerConfig, body: SessionSyncBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.messages)) {\n\t\tsendJson(res, 400, { error: \"messages is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = replaceMessages(body.sessionId, body.messages);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionAppend(config: ServerConfig, body: SessionAppendBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.messages)) {\n\t\tsendJson(res, 400, { error: \"messages is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = appendMessages(body.sessionId, body.messages);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionTreeSync(config: ServerConfig, body: SessionTreeSyncBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.entries)) {\n\t\tsendJson(res, 400, { error: \"entries is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = replaceSessionTree(body.sessionId, body.entries, body.leafId ?? null);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionTreeAppend(config: ServerConfig, body: SessionTreeSyncBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!Array.isArray(body.entries)) {\n\t\tsendJson(res, 400, { error: \"entries is required\" });\n\t\treturn;\n\t}\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t}\n\tconst session = appendSessionEntries(body.sessionId, body.entries, body.leafId ?? null);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction handleSessionTreeSwitch(config: ServerConfig, body: SessionTreeSwitchBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tconst session = switchSessionLeaf(body.sessionId, body.leafId ?? null);\n\tpersistSession(config, session);\n\tsendJson(res, 200, sessionResponseBody(session));\n}\n\nfunction prepareSessionCompact(body: SessionCompactBody): PreparedSessionCompact | SessionCompactHttpResponse {\n\tif (!body.sessionId) {\n\t\treturn { status: 400, body: { error: \"sessionId is required\" } };\n\t}\n\tif (!body.model) {\n\t\treturn { status: 400, body: { error: \"model is required\" } };\n\t}\n\n\tconst session = getSession(body.sessionId);\n\tif (!session) {\n\t\treturn { status: 404, body: { error: \"session not found\" } };\n\t}\n\n\tconst entries = getSessionBranch(session);\n\tconst preparationResult = prepareLegacyCompaction(\n\t\tentries,\n\t\tbody.settings ?? DEFAULT_COMPACTION_SETTINGS,\n\t\tbody.preparation,\n\t);\n\tif (!preparationResult.ok) {\n\t\treturn { status: 400, body: { error: preparationResult.error.message } };\n\t}\n\tif (!preparationResult.value) {\n\t\treturn { status: 400, body: { error: \"Nothing to compact\" } };\n\t}\n\n\tconst options = body.options ?? {};\n\treturn {\n\t\tsession,\n\t\tpreparation: preparationResult.value,\n\t\toptions,\n\t};\n}\n\nasync function completeSessionCompact(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tprepared: PreparedSessionCompact,\n): Promise<SessionCompactHttpResponse> {\n\tconst result = await compactLegacy(\n\t\tprepared.preparation,\n\t\tcreateRequestModels(body.model, prepared.options),\n\t\tbody.model,\n\t\tbody.customInstructions,\n\t\tundefined,\n\t\tprepared.options.reasoning,\n\t);\n\tif (!result.ok) {\n\t\treturn { status: 500, body: { error: result.error.message } };\n\t}\n\n\tconst baseTreeHash = prepared.session.treeHash;\n\tconst baseEntryCount = prepared.session.entries.length;\n\tconst compaction = result.value;\n\tconst { session: updatedSession, entry: compactionEntry } = appendCompactionEntry(body.sessionId, compaction);\n\tpersistSession(config, updatedSession);\n\tif (!body.fullResponse && body.baseTreeHash === baseTreeHash) {\n\t\treturn {\n\t\t\tstatus: 200,\n\t\t\tbody: {\n\t\t\t\tsuccess: true,\n\t\t\t\tcompaction: compaction satisfies LegacyCompactResult,\n\t\t\t\tcompactionEntry,\n\t\t\t\t...sessionResponseBody(updatedSession),\n\t\t\t\tstaticContext: updatedSession.staticContext,\n\t\t\t\ttreePatch: {\n\t\t\t\t\tbaseTreeHash,\n\t\t\t\t\tentriesFrom: baseEntryCount,\n\t\t\t\t\tentries: [compactionEntry],\n\t\t\t\t\tleafId: updatedSession.leafId,\n\t\t\t\t\trevision: updatedSession.revision,\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\treturn {\n\t\tstatus: 200,\n\t\tbody: {\n\t\t\tsuccess: true,\n\t\t\tcompaction: compaction satisfies LegacyCompactResult,\n\t\t\tcompactionEntry,\n\t\t\t...sessionResponseBody(updatedSession),\n\t\t\tstaticContext: updatedSession.staticContext,\n\t\t\tentries: updatedSession.entries,\n\t\t\tmessages: updatedSession.messages,\n\t\t},\n\t};\n}\n\nfunction writeServerSentEvent(res: ServerResponse, event: string, body: unknown): void {\n\tres.write(`event: ${event}\\n`);\n\tres.write(`data: ${JSON.stringify(body)}\\n\\n`);\n}\n\nasync function handleSessionCompactStream(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tprepared: PreparedSessionCompact,\n\tres: ServerResponse,\n): Promise<void> {\n\tres.writeHead(200, {\n\t\t\"Content-Type\": \"text/event-stream\",\n\t\t\"Cache-Control\": \"no-cache\",\n\t\tConnection: \"keep-alive\",\n\t});\n\tres.flushHeaders();\n\tres.write(STREAM_HEARTBEAT);\n\n\tconst heartbeat = setInterval(() => {\n\t\tif (!res.writableEnded) {\n\t\t\tres.write(STREAM_HEARTBEAT);\n\t\t}\n\t}, STREAM_HEARTBEAT_INTERVAL_MS);\n\theartbeat.unref();\n\n\ttry {\n\t\tconst result = await completeSessionCompact(config, body, prepared);\n\t\twriteServerSentEvent(res, result.status >= 400 ? \"error\" : \"result\", result.body);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\twriteServerSentEvent(res, \"error\", { error: message });\n\t} finally {\n\t\tclearInterval(heartbeat);\n\t\tres.end();\n\t}\n}\n\nasync function handleSessionCompactJsonStream(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tprepared: PreparedSessionCompact,\n\tres: ServerResponse,\n): Promise<void> {\n\tres.writeHead(200, {\n\t\t\"Content-Type\": \"application/json\",\n\t\t\"Cache-Control\": \"no-cache\",\n\t\tConnection: \"keep-alive\",\n\t});\n\tres.flushHeaders();\n\tres.write(JSON_HEARTBEAT);\n\n\tconst heartbeat = setInterval(() => {\n\t\tif (!res.writableEnded) {\n\t\t\tres.write(JSON_HEARTBEAT);\n\t\t}\n\t}, STREAM_HEARTBEAT_INTERVAL_MS);\n\theartbeat.unref();\n\n\ttry {\n\t\tconst result = await completeSessionCompact(config, body, prepared);\n\t\tres.write(JSON.stringify(result.body));\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tres.write(JSON.stringify({ error: message }));\n\t} finally {\n\t\tclearInterval(heartbeat);\n\t\tres.end();\n\t}\n}\n\nasync function handleSessionCompact(\n\tconfig: ServerConfig,\n\tbody: SessionCompactBody,\n\tres: ServerResponse,\n): Promise<void> {\n\tconst prepared = prepareSessionCompact(body);\n\tif (\"status\" in prepared) {\n\t\tsendJson(res, prepared.status, prepared.body);\n\t\treturn;\n\t}\n\n\tif (body.streamResponse) {\n\t\tawait handleSessionCompactStream(config, body, prepared, res);\n\t\treturn;\n\t}\n\n\tawait handleSessionCompactJsonStream(config, body, prepared, res);\n}\n\nfunction handleDropLastAssistantError(config: ServerConfig, body: SessionIdBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tconst dropped = dropLastAssistantError(body.sessionId);\n\tconst session = getSession(body.sessionId);\n\tif (session) {\n\t\tpersistSession(config, session);\n\t}\n\tconst messageCount = session?.messages.length ?? 0;\n\tsendJson(res, 200, { success: true, dropped, messageCount });\n}\n\nfunction handleSessionHistory(\n\tsessionId: string,\n\tfrom: number | undefined,\n\tentriesFrom: number | undefined,\n\trevision: number | undefined,\n\tbaseTreeHash: string | undefined,\n\tres: ServerResponse,\n): void {\n\tconst session = getSession(sessionId);\n\tif (!session) {\n\t\tsendJson(res, 404, { error: \"session not found\" });\n\t\treturn;\n\t}\n\tconst baseMessageCount = from ?? 0;\n\tif (\n\t\tentriesFrom !== undefined &&\n\t\tentriesFrom <= session.entries.length &&\n\t\t(revision === undefined || revision <= session.revision) &&\n\t\t(baseTreeHash === undefined || baseTreeHash === session.prefixHashes[entriesFrom])\n\t) {\n\t\tsendJson(res, 200, sessionTreePatchResponseBody(session, baseMessageCount, entriesFrom, revision));\n\t\treturn;\n\t}\n\tsendJson(res, 200, sessionHistoryFullResponseBody(session, baseMessageCount));\n}\n\nfunction handleSessionRun(sessionId: string, runId: string, res: ServerResponse): void {\n\tconst run = getStreamRun(sessionId, runId);\n\tif (!run) {\n\t\tsendJson(res, 404, { error: \"run not found\" });\n\t\treturn;\n\t}\n\tsendJson(res, 200, run);\n}\n\nexport function buildStreamContext(\n\tsession: SessionState,\n\tbody: Pick<StreamRequestBody, \"contextOverlay\" | \"ephemeralMessages\">,\n): Context {\n\tconst messages = body.contextOverlay ?? [...session.messages, ...(body.ephemeralMessages ?? [])];\n\treturn {\n\t\tsystemPrompt: session.staticContext?.systemPrompt,\n\t\tmessages,\n\t\ttools: session.staticContext?.tools,\n\t};\n}\n\nfunction handleStream(config: ServerConfig, body: StreamRequestBody, res: ServerResponse): void {\n\tif (!body.sessionId) {\n\t\tsendJson(res, 400, { error: \"sessionId is required\" });\n\t\treturn;\n\t}\n\tif (!body.model) {\n\t\tsendJson(res, 400, { error: \"model is required\" });\n\t\treturn;\n\t}\n\n\tconst session = getOrCreateSession(body.sessionId);\n\n\tif (body.staticContext) {\n\t\tsetStaticContext(body.sessionId, body.staticContext);\n\t\tpersistSession(config, session);\n\t}\n\n\tif (!session.staticContext && !body.staticContext) {\n\t\tsendJson(res, 400, { error: \"Session has no static context. Initialize with /api/session/init first.\" });\n\t\treturn;\n\t}\n\n\tif (body.ephemeralMessages !== undefined && !Array.isArray(body.ephemeralMessages)) {\n\t\tsendJson(res, 400, { error: \"ephemeralMessages must be an array\" });\n\t\treturn;\n\t}\n\tif (body.contextOverlay !== undefined && !Array.isArray(body.contextOverlay)) {\n\t\tsendJson(res, 400, { error: \"contextOverlay must be an array\" });\n\t\treturn;\n\t}\n\n\tconst context = buildStreamContext(session, body);\n\tconst existingRun = body.runId ? getStreamRun(body.sessionId, body.runId) : undefined;\n\n\tconst { model: resolvedModel, options: streamOptions } = resolveStreamOptions(config, body.model, body);\n\n\tres.writeHead(200, {\n\t\t\"Content-Type\": \"text/event-stream\",\n\t\t\"Cache-Control\": \"no-cache\",\n\t\tConnection: \"keep-alive\",\n\t});\n\tres.flushHeaders();\n\tres.write(STREAM_HEARTBEAT);\n\n\tif (existingRun?.status === \"completed\") {\n\t\tfor (const event of existingRun.events) {\n\t\t\tres.write(encodeProxyEvent(event));\n\t\t}\n\t\tres.end();\n\t\treturn;\n\t}\n\n\tconst run = body.runId ? startStreamRun(body.sessionId, body.runId) : undefined;\n\n\tconst heartbeat = setInterval(() => {\n\t\tif (!res.writableEnded) {\n\t\t\tres.write(STREAM_HEARTBEAT);\n\t\t}\n\t}, STREAM_HEARTBEAT_INTERVAL_MS);\n\theartbeat.unref();\n\n\tlet stream: AsyncIterable<AssistantMessageEvent>;\n\ttry {\n\t\tstream = streamSimple(resolvedModel, context, streamOptions);\n\t} catch (err) {\n\t\tclearInterval(heartbeat);\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tfailStreamRun(run, message);\n\t\tres.write(encodeErrorEvent(message));\n\t\tres.end();\n\t\treturn;\n\t}\n\n\t(async () => {\n\t\ttry {\n\t\t\tfor await (const event of stream) {\n\t\t\t\tconst proxyEvent = toProxyEvent(event);\n\t\t\t\tif (proxyEvent) {\n\t\t\t\t\trecordStreamRunEvent(run, proxyEvent);\n\t\t\t\t\tres.write(encodeProxyEvent(proxyEvent));\n\t\t\t\t}\n\t\t\t\tif (event.type === \"done\") {\n\t\t\t\t\tcompleteStreamRun(run, event.message);\n\t\t\t\t} else if (event.type === \"error\") {\n\t\t\t\t\tfailStreamRun(run, event.error.errorMessage ?? event.reason);\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tclearInterval(heartbeat);\n\t\t}\n\n\t\tres.end();\n\t})().catch((err) => {\n\t\tclearInterval(heartbeat);\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tfailStreamRun(run, message);\n\t\tres.write(encodeErrorEvent(message));\n\t\tres.end();\n\t});\n}\n\nasync function handlePostRequest(\n\tconfig: ServerConfig,\n\tpathname: string,\n\tbody: unknown,\n\tres: ServerResponse,\n): Promise<boolean> {\n\tif (pathname === \"/api/receive\") {\n\t\ttry {\n\t\t\tsendJson(res, 200, receiveUpload(config.uploadDir, body));\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof ReceiveUploadError)) throw error;\n\t\t\tsendJson(res, error.status, { error: error.message });\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/init\") {\n\t\thandleSessionInit(config, body as SessionInitBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/update\") {\n\t\thandleSessionUpdate(config, body as SessionInitBody & { staticContext: SessionStaticContext }, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/sync\") {\n\t\thandleSessionSync(config, body as SessionSyncBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/append\") {\n\t\thandleSessionAppend(config, body as SessionAppendBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/tree/sync\") {\n\t\thandleSessionTreeSync(config, body as SessionTreeSyncBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/tree/append\") {\n\t\thandleSessionTreeAppend(config, body as SessionTreeSyncBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/tree/switch\") {\n\t\thandleSessionTreeSwitch(config, body as SessionTreeSwitchBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/drop-last-assistant-error\") {\n\t\thandleDropLastAssistantError(config, body as SessionIdBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/session/compact\") {\n\t\tawait handleSessionCompact(config, body as SessionCompactBody, res);\n\t\treturn true;\n\t}\n\n\tif (pathname === \"/api/stream\") {\n\t\thandleStream(config, body as StreamRequestBody, res);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nexport function createPiServer(configOverride?: Partial<ServerConfig>): HttpServer {\n\tconst config = loadConfig(configOverride);\n\tloadPersistedSessions(config.sessionStoreDir);\n\n\tconst server = createServer(async (req, res) => {\n\t\tconst url = new URL(req.url ?? \"/\", `http://${config.host}:${config.port}`);\n\n\t\tif (req.method === \"GET\" && url.pathname === \"/health\") {\n\t\t\tsendJson(res, 200, { status: \"ok\" });\n\t\t\treturn;\n\t\t}\n\n\t\tif (!authenticate(config, req)) {\n\t\t\tconst body =\n\t\t\t\treq.method === \"GET\" && url.pathname === \"/\"\n\t\t\t\t\t? { error: \"Unauthorized\", version: PI_SERVER_VERSION }\n\t\t\t\t\t: { error: \"Unauthorized\" };\n\t\t\tsendJson(res, 401, body);\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"GET\" && url.pathname === \"/api/sessions\") {\n\t\t\tsendJson(res, 200, { sessions: listSessions() });\n\t\t\treturn;\n\t\t}\n\n\t\tconst runMatch = /^\\/api\\/session\\/([^/]+)\\/runs\\/([^/]+)$/.exec(url.pathname);\n\t\tif (req.method === \"GET\" && runMatch) {\n\t\t\thandleSessionRun(decodeURIComponent(runMatch[1]), decodeURIComponent(runMatch[2]), res);\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"GET\" && url.pathname.startsWith(\"/api/session/\") && url.pathname.endsWith(\"/history\")) {\n\t\t\tconst encodedSessionId = url.pathname.slice(\"/api/session/\".length, -\"/history\".length);\n\t\t\tconst fromParam = url.searchParams.get(\"from\");\n\t\t\tconst from = fromParam === null ? undefined : Number(fromParam);\n\t\t\tif (from !== undefined && (!Number.isInteger(from) || from < 0)) {\n\t\t\t\tsendJson(res, 400, { error: \"from must be a non-negative integer\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst entriesFromParam = url.searchParams.get(\"entriesFrom\");\n\t\t\tconst entriesFrom = entriesFromParam === null ? undefined : Number(entriesFromParam);\n\t\t\tif (entriesFrom !== undefined && (!Number.isInteger(entriesFrom) || entriesFrom < 0)) {\n\t\t\t\tsendJson(res, 400, { error: \"entriesFrom must be a non-negative integer\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst revisionParam = url.searchParams.get(\"revision\");\n\t\t\tconst revision = revisionParam === null ? undefined : Number(revisionParam);\n\t\t\tif (revision !== undefined && (!Number.isInteger(revision) || revision < 0)) {\n\t\t\t\tsendJson(res, 400, { error: \"revision must be a non-negative integer\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thandleSessionHistory(\n\t\t\t\tdecodeURIComponent(encodedSessionId),\n\t\t\t\tfrom,\n\t\t\t\tentriesFrom,\n\t\t\t\trevision,\n\t\t\t\turl.searchParams.get(\"baseTreeHash\") ?? undefined,\n\t\t\t\tres,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"POST\" && url.pathname === CHUNK_ENDPOINT) {\n\t\t\ttry {\n\t\t\t\tconst body = JSON.parse(await readBody(req)) as RequestChunkBody;\n\t\t\t\tconst chunkResult = receiveRequestChunk(body);\n\t\t\t\tif (!chunkResult.complete) {\n\t\t\t\t\tsendJson(res, 200, chunkResult.ack);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait handlePostRequest(config, chunkResult.target, JSON.parse(chunkResult.bodyJson) as unknown, res);\n\t\t\t} catch (err) {\n\t\t\t\tlogRequestError(req, err);\n\t\t\t\tif (!res.headersSent) {\n\t\t\t\t\tsendJson(res, 400, { error: err instanceof Error ? err.message : String(err) });\n\t\t\t\t} else {\n\t\t\t\t\tres.write(encodeErrorEvent(err instanceof Error ? err.message : String(err)));\n\t\t\t\t\tres.end();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (req.method === \"POST\") {\n\t\t\ttry {\n\t\t\t\tconst body = JSON.parse(await readBody(req)) as unknown;\n\t\t\t\tif (await handlePostRequest(config, url.pathname, body, res)) return;\n\t\t\t} catch (err) {\n\t\t\t\tlogRequestError(req, err);\n\t\t\t\tif (!res.headersSent) {\n\t\t\t\t\tsendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });\n\t\t\t\t} else {\n\t\t\t\t\tres.write(encodeErrorEvent(err instanceof Error ? err.message : String(err)));\n\t\t\t\t\tres.end();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (req.method === \"DELETE\" && url.pathname.startsWith(\"/api/session/\")) {\n\t\t\tconst sessionId = decodeURIComponent(url.pathname.slice(\"/api/session/\".length));\n\t\t\tdeleteSessionFromStore(sessionId);\n\t\t\tdeletePersistedSession(config.sessionStoreDir, sessionId);\n\t\t\tsendJson(res, 200, { deleted: sessionId });\n\t\t\treturn;\n\t\t}\n\n\t\tsendJson(res, 404, { error: \"Not found\" });\n\t});\n\n\treturn server;\n}\n\nfunction toProxyEvent(event: AssistantMessageEvent): ProxyAssistantMessageEvent | undefined {\n\tswitch (event.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\" };\n\t\tcase \"text_start\":\n\t\t\treturn { type: \"text_start\", contentIndex: event.contentIndex };\n\t\tcase \"text_delta\":\n\t\t\treturn { type: \"text_delta\", contentIndex: event.contentIndex, delta: event.delta };\n\t\tcase \"text_end\":\n\t\t\treturn {\n\t\t\t\ttype: \"text_end\",\n\t\t\t\tcontentIndex: event.contentIndex,\n\t\t\t\tcontentSignature:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"text\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { textSignature?: string }).textSignature\n\t\t\t\t\t\t: undefined,\n\t\t\t};\n\t\tcase \"thinking_start\":\n\t\t\treturn { type: \"thinking_start\", contentIndex: event.contentIndex };\n\t\tcase \"thinking_delta\":\n\t\t\treturn { type: \"thinking_delta\", contentIndex: event.contentIndex, delta: event.delta };\n\t\tcase \"thinking_end\":\n\t\t\treturn {\n\t\t\t\ttype: \"thinking_end\",\n\t\t\t\tcontentIndex: event.contentIndex,\n\t\t\t\tcontentSignature:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"thinking\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { thinkingSignature?: string }).thinkingSignature\n\t\t\t\t\t\t: undefined,\n\t\t\t};\n\t\tcase \"toolcall_start\":\n\t\t\treturn {\n\t\t\t\ttype: \"toolcall_start\",\n\t\t\t\tcontentIndex: event.contentIndex,\n\t\t\t\tid:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"toolCall\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { id: string }).id\n\t\t\t\t\t\t: \"\",\n\t\t\t\ttoolName:\n\t\t\t\t\tevent.partial.content[event.contentIndex]?.type === \"toolCall\"\n\t\t\t\t\t\t? (event.partial.content[event.contentIndex] as { name: string }).name\n\t\t\t\t\t\t: \"\",\n\t\t\t};\n\t\tcase \"toolcall_delta\":\n\t\t\treturn { type: \"toolcall_delta\", contentIndex: event.contentIndex, delta: event.delta };\n\t\tcase \"toolcall_end\":\n\t\t\treturn { type: \"toolcall_end\", contentIndex: event.contentIndex };\n\t\tcase \"done\":\n\t\t\treturn {\n\t\t\t\ttype: \"done\",\n\t\t\t\treason: event.reason,\n\t\t\t\tusage: event.message.usage,\n\t\t\t\tdeferred: event.message.deferred,\n\t\t\t};\n\t\tcase \"error\":\n\t\t\treturn {\n\t\t\t\ttype: \"error\",\n\t\t\t\treason: event.reason,\n\t\t\t\terrorMessage: event.error.errorMessage,\n\t\t\t\tusage: event.error.usage,\n\t\t\t};\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nexport function startServer(configOverride?: Partial<ServerConfig>): HttpServer {\n\tconst config = loadConfig(configOverride);\n\tconst server = createPiServer(configOverride);\n\tserver.listen(config.port, config.host, () => {\n\t\tconsole.log(`pi-server listening on ${config.host}:${config.port}`);\n\t});\n\treturn server;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { buildSessionContext, convertToLlm, type SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport type { Message, Tool } from \"@earendil-works/pi-ai\";\nimport {\n\tappendPiServerTreeHash,\n\tbuildPiServerTreePrefixHashes,\n\thashPiServerSessionEntries,\n\thashPiServerStaticContext,\n\thashPiServerTreeEntry,\n} from \"./pi-server-protocol.ts\";\n\nexport interface SessionStaticContext {\n\tsystemPrompt?: string;\n\ttools?: Tool[];\n}\n\nexport interface SessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tstaticContextHash: string;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\ttreeHash: string;\n\tprefixHashes: string[];\n\tmessages: Message[];\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n\tpersistenceChange: SessionPersistenceChange | undefined;\n}\n\nexport interface SessionSummary {\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash: string;\n\tmessageCount: number;\n\tentryCount: number;\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport interface PersistedSessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport type SessionPersistenceChange = { kind: \"snapshot\" } | { kind: \"wal\"; entries: SessionTreeEntry[] };\n\nconst sessions = new Map<string, SessionState>();\n\nfunction hashStaticContext(ctx: SessionStaticContext | undefined): string {\n\treturn hashPiServerStaticContext(ctx);\n}\n\nexport function getOrCreateSession(sessionId: string): SessionState {\n\tlet session = sessions.get(sessionId);\n\tif (!session) {\n\t\tconst now = Date.now();\n\t\tsession = {\n\t\t\tsessionId,\n\t\t\tstaticContext: undefined,\n\t\t\tstaticContextHash: \"\",\n\t\t\tentries: [],\n\t\t\tleafId: null,\n\t\t\ttreeHash: hashPiServerSessionEntries([]),\n\t\t\tprefixHashes: buildPiServerTreePrefixHashes([]),\n\t\t\tmessages: [],\n\t\t\trevision: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t\tpersistenceChange: { kind: \"snapshot\" },\n\t\t};\n\t\tsessions.set(sessionId, session);\n\t}\n\treturn session;\n}\n\nexport function getSession(sessionId: string): SessionState | undefined {\n\treturn sessions.get(sessionId);\n}\n\nexport function hashSessionEntries(entries: SessionTreeEntry[]): string {\n\treturn hashPiServerSessionEntries(entries);\n}\n\nexport function listSessions(): SessionSummary[] {\n\treturn Array.from(sessions.values())\n\t\t.map((session) => ({\n\t\t\tsessionId: session.sessionId,\n\t\t\tstaticContextHash: session.staticContextHash,\n\t\t\ttreeHash: session.treeHash,\n\t\t\tmessageCount: session.messages.length,\n\t\t\tentryCount: session.entries.length,\n\t\t\tleafId: session.leafId,\n\t\t\trevision: session.revision,\n\t\t\tcreatedAt: session.createdAt,\n\t\t\tupdatedAt: session.updatedAt,\n\t\t}))\n\t\t.sort((a, b) => b.updatedAt - a.updatedAt || a.sessionId.localeCompare(b.sessionId));\n}\n\nexport function exportSessionState(session: SessionState): PersistedSessionState {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tentries: session.entries.map((entry) => ({ ...entry })),\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tcreatedAt: session.createdAt,\n\t\tupdatedAt: session.updatedAt,\n\t};\n}\n\nexport function restoreSessionState(persisted: PersistedSessionState): SessionState {\n\tassertValidLeaf(persisted.entries, persisted.leafId);\n\tconst session: SessionState = {\n\t\tsessionId: persisted.sessionId,\n\t\tstaticContext: persisted.staticContext,\n\t\tstaticContextHash: hashStaticContext(persisted.staticContext),\n\t\tentries: persisted.entries.map((entry) => ({ ...entry })),\n\t\tleafId: persisted.leafId,\n\t\ttreeHash: hashSessionEntries(persisted.entries),\n\t\tprefixHashes: buildPiServerTreePrefixHashes(persisted.entries),\n\t\tmessages: [],\n\t\trevision: persisted.revision,\n\t\tcreatedAt: persisted.createdAt,\n\t\tupdatedAt: persisted.updatedAt,\n\t\tpersistenceChange: undefined,\n\t};\n\trefreshActiveMessages(session);\n\tsessions.set(session.sessionId, session);\n\treturn session;\n}\n\nexport function setStaticContext(sessionId: string, context: SessionStaticContext): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst newHash = hashStaticContext(context);\n\tsession.staticContext = context;\n\tsession.staticContextHash = newHash;\n\tsession.updatedAt = Date.now();\n\tmarkWalPersistenceChange(session, []);\n\treturn session;\n}\n\nfunction entryToMessageEntry(message: Message, parentId: string | null): SessionTreeEntry {\n\treturn {\n\t\ttype: \"message\",\n\t\tid: randomUUID(),\n\t\tparentId,\n\t\ttimestamp: new Date(message.timestamp).toISOString(),\n\t\tmessage,\n\t};\n}\n\nexport function getSessionBranch(session: SessionState): SessionTreeEntry[] {\n\tconst byId = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst branch: SessionTreeEntry[] = [];\n\tlet current = session.leafId ? byId.get(session.leafId) : undefined;\n\tconst seen = new Set<string>();\n\twhile (current) {\n\t\tif (seen.has(current.id)) {\n\t\t\tthrow new Error(`session tree contains a parent cycle at entry ${current.id}`);\n\t\t}\n\t\tseen.add(current.id);\n\t\tbranch.unshift(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\treturn branch;\n}\n\nfunction deriveActiveMessages(session: SessionState): Message[] {\n\tconst branch = getSessionBranch(session);\n\treturn convertToLlm(buildSessionContext(branch).messages);\n}\n\nfunction refreshActiveMessages(session: SessionState): void {\n\tsession.messages = deriveActiveMessages(session);\n}\n\nfunction refreshTreeHashes(session: SessionState): void {\n\tsession.prefixHashes = buildPiServerTreePrefixHashes(session.entries);\n\tsession.treeHash = session.prefixHashes[session.prefixHashes.length - 1];\n}\n\nfunction appendTreeHashes(session: SessionState, entries: SessionTreeEntry[]): void {\n\tfor (const entry of entries) {\n\t\tsession.treeHash = appendPiServerTreeHash(session.treeHash, hashPiServerTreeEntry(entry));\n\t\tsession.prefixHashes.push(session.treeHash);\n\t}\n}\n\nfunction assertValidLeaf(entries: SessionTreeEntry[], leafId: string | null): void {\n\tif (leafId === null) return;\n\tif (!entries.some((entry) => entry.id === leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n}\n\nexport function replaceSessionTree(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tassertValidLeaf(entries, leafId);\n\tconst session = getOrCreateSession(sessionId);\n\tsession.entries = entries.map((entry) => ({ ...entry }));\n\tsession.leafId = leafId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn session;\n}\n\nexport function appendSessionEntries(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst knownEntries = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst entriesToAppend: SessionTreeEntry[] = [];\n\tfor (const entry of entries) {\n\t\tconst knownEntry = knownEntries.get(entry.id);\n\t\tif (knownEntry) {\n\t\t\tif (!isDeepStrictEqual(knownEntry, entry)) {\n\t\t\t\tthrow new Error(`entry ${entry.id} already exists`);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.parentId !== null && !knownEntries.has(entry.parentId)) {\n\t\t\tthrow new Error(`parent entry ${entry.parentId} does not exist`);\n\t\t}\n\t\tconst entryToAppend = { ...entry };\n\t\tentriesToAppend.push(entryToAppend);\n\t\tknownEntries.set(entryToAppend.id, entryToAppend);\n\t}\n\tif (leafId !== null && !knownEntries.has(leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n\tif (entriesToAppend.length === 0 && session.leafId === leafId) {\n\t\treturn session;\n\t}\n\tsession.entries.push(...entriesToAppend);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tappendTreeHashes(session, entriesToAppend);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, entriesToAppend);\n\treturn session;\n}\n\nexport function switchSessionLeaf(sessionId: string, leafId: string | null): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, []);\n\treturn session;\n}\n\nexport function appendCompactionEntry(\n\tsessionId: string,\n\tcompaction: { summary: string; firstKeptEntryId: string; tokensBefore: number; details?: unknown },\n): { session: SessionState; entry: SessionTreeEntry } {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entry: SessionTreeEntry = {\n\t\ttype: \"compaction\",\n\t\tid: randomUUID(),\n\t\tparentId: session.leafId,\n\t\ttimestamp: new Date().toISOString(),\n\t\tsummary: compaction.summary,\n\t\tfirstKeptEntryId: compaction.firstKeptEntryId,\n\t\ttokensBefore: compaction.tokensBefore,\n\t\tdetails: compaction.details,\n\t};\n\tsession.entries.push(entry);\n\tsession.leafId = entry.id;\n\tappendTreeHashes(session, [entry]);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, [entry]);\n\treturn { session, entry };\n}\n\nexport function getActiveMessages(sessionId: string): Message[] {\n\tconst session = getSession(sessionId);\n\treturn session ? [...session.messages] : [];\n}\n\nexport function appendMessages(sessionId: string, delta: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entries = delta.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, session.leafId);\n\t\tsession.leafId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.entries.push(...entries);\n\tappendTreeHashes(session, entries);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, entries);\n\treturn session;\n}\n\nexport function appendAssistantResponse(sessionId: string, message: Message): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tsession.messages.push(message);\n\tsession.updatedAt = Date.now();\n\treturn session;\n}\n\nexport function replaceMessages(sessionId: string, messages: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tlet parentId: string | null = null;\n\tsession.entries = messages.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, parentId);\n\t\tparentId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.leafId = parentId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn session;\n}\n\nexport function dropLastAssistantError(sessionId: string): boolean {\n\tconst session = getSession(sessionId);\n\tif (!session) return false;\n\tconst leaf = session.leafId ? session.entries.find((entry) => entry.id === session.leafId) : undefined;\n\tif (leaf?.type !== \"message\" || leaf.message.role !== \"assistant\" || leaf.message.stopReason !== \"error\") {\n\t\treturn false;\n\t}\n\tsession.entries = session.entries.filter((entry) => entry.id !== leaf.id);\n\tsession.leafId = leaf.parentId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn true;\n}\n\nexport function deleteSession(sessionId: string): boolean {\n\treturn sessions.delete(sessionId);\n}\n\nexport function clearAllSessions(): void {\n\tsessions.clear();\n}\n\nfunction markWalPersistenceChange(session: SessionState, entries: SessionTreeEntry[]): void {\n\tif (session.persistenceChange?.kind === \"snapshot\") return;\n\tsession.persistenceChange = {\n\t\tkind: \"wal\",\n\t\tentries: [...(session.persistenceChange?.entries ?? []), ...entries.map((entry) => ({ ...entry }))],\n\t};\n}\n\nexport function markSessionPersisted(session: SessionState): void {\n\tsession.persistenceChange = undefined;\n}\n"]}
1
+ {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { buildLegacySessionContext, convertToLlm, type SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport type { Message, Tool } from \"@earendil-works/pi-ai\";\nimport {\n\tappendPiServerTreeHash,\n\tbuildPiServerTreePrefixHashes,\n\thashPiServerSessionEntries,\n\thashPiServerStaticContext,\n\thashPiServerTreeEntry,\n} from \"./pi-server-protocol.ts\";\n\nexport interface SessionStaticContext {\n\tsystemPrompt?: string;\n\ttools?: Tool[];\n}\n\nexport interface SessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tstaticContextHash: string;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\ttreeHash: string;\n\tprefixHashes: string[];\n\tmessages: Message[];\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n\tpersistenceChange: SessionPersistenceChange | undefined;\n}\n\nexport interface SessionSummary {\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash: string;\n\tmessageCount: number;\n\tentryCount: number;\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport interface PersistedSessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport type SessionPersistenceChange = { kind: \"snapshot\" } | { kind: \"wal\"; entries: SessionTreeEntry[] };\n\nconst sessions = new Map<string, SessionState>();\n\nfunction hashStaticContext(ctx: SessionStaticContext | undefined): string {\n\treturn hashPiServerStaticContext(ctx);\n}\n\nexport function getOrCreateSession(sessionId: string): SessionState {\n\tlet session = sessions.get(sessionId);\n\tif (!session) {\n\t\tconst now = Date.now();\n\t\tsession = {\n\t\t\tsessionId,\n\t\t\tstaticContext: undefined,\n\t\t\tstaticContextHash: \"\",\n\t\t\tentries: [],\n\t\t\tleafId: null,\n\t\t\ttreeHash: hashPiServerSessionEntries([]),\n\t\t\tprefixHashes: buildPiServerTreePrefixHashes([]),\n\t\t\tmessages: [],\n\t\t\trevision: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t\tpersistenceChange: { kind: \"snapshot\" },\n\t\t};\n\t\tsessions.set(sessionId, session);\n\t}\n\treturn session;\n}\n\nexport function getSession(sessionId: string): SessionState | undefined {\n\treturn sessions.get(sessionId);\n}\n\nexport function hashSessionEntries(entries: SessionTreeEntry[]): string {\n\treturn hashPiServerSessionEntries(entries);\n}\n\nexport function listSessions(): SessionSummary[] {\n\treturn Array.from(sessions.values())\n\t\t.map((session) => ({\n\t\t\tsessionId: session.sessionId,\n\t\t\tstaticContextHash: session.staticContextHash,\n\t\t\ttreeHash: session.treeHash,\n\t\t\tmessageCount: session.messages.length,\n\t\t\tentryCount: session.entries.length,\n\t\t\tleafId: session.leafId,\n\t\t\trevision: session.revision,\n\t\t\tcreatedAt: session.createdAt,\n\t\t\tupdatedAt: session.updatedAt,\n\t\t}))\n\t\t.sort((a, b) => b.updatedAt - a.updatedAt || a.sessionId.localeCompare(b.sessionId));\n}\n\nexport function exportSessionState(session: SessionState): PersistedSessionState {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tentries: session.entries.map((entry) => ({ ...entry })),\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tcreatedAt: session.createdAt,\n\t\tupdatedAt: session.updatedAt,\n\t};\n}\n\nexport function restoreSessionState(persisted: PersistedSessionState): SessionState {\n\tassertValidLeaf(persisted.entries, persisted.leafId);\n\tconst session: SessionState = {\n\t\tsessionId: persisted.sessionId,\n\t\tstaticContext: persisted.staticContext,\n\t\tstaticContextHash: hashStaticContext(persisted.staticContext),\n\t\tentries: persisted.entries.map((entry) => ({ ...entry })),\n\t\tleafId: persisted.leafId,\n\t\ttreeHash: hashSessionEntries(persisted.entries),\n\t\tprefixHashes: buildPiServerTreePrefixHashes(persisted.entries),\n\t\tmessages: [],\n\t\trevision: persisted.revision,\n\t\tcreatedAt: persisted.createdAt,\n\t\tupdatedAt: persisted.updatedAt,\n\t\tpersistenceChange: undefined,\n\t};\n\trefreshActiveMessages(session);\n\tsessions.set(session.sessionId, session);\n\treturn session;\n}\n\nexport function setStaticContext(sessionId: string, context: SessionStaticContext): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst newHash = hashStaticContext(context);\n\tsession.staticContext = context;\n\tsession.staticContextHash = newHash;\n\tsession.updatedAt = Date.now();\n\tmarkWalPersistenceChange(session, []);\n\treturn session;\n}\n\nfunction entryToMessageEntry(message: Message, parentId: string | null): SessionTreeEntry {\n\treturn {\n\t\ttype: \"message\",\n\t\tid: randomUUID(),\n\t\tparentId,\n\t\ttimestamp: new Date(message.timestamp).toISOString(),\n\t\tmessage,\n\t};\n}\n\nexport function getSessionBranch(session: SessionState): SessionTreeEntry[] {\n\tconst byId = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst branch: SessionTreeEntry[] = [];\n\tlet current = session.leafId ? byId.get(session.leafId) : undefined;\n\tconst seen = new Set<string>();\n\twhile (current) {\n\t\tif (seen.has(current.id)) {\n\t\t\tthrow new Error(`session tree contains a parent cycle at entry ${current.id}`);\n\t\t}\n\t\tseen.add(current.id);\n\t\tbranch.unshift(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\treturn branch;\n}\n\nfunction deriveActiveMessages(session: SessionState): Message[] {\n\tconst branch = getSessionBranch(session);\n\treturn convertToLlm(buildLegacySessionContext(branch).messages);\n}\n\nfunction refreshActiveMessages(session: SessionState): void {\n\tsession.messages = deriveActiveMessages(session);\n}\n\nfunction refreshTreeHashes(session: SessionState): void {\n\tsession.prefixHashes = buildPiServerTreePrefixHashes(session.entries);\n\tsession.treeHash = session.prefixHashes[session.prefixHashes.length - 1];\n}\n\nfunction appendTreeHashes(session: SessionState, entries: SessionTreeEntry[]): void {\n\tfor (const entry of entries) {\n\t\tsession.treeHash = appendPiServerTreeHash(session.treeHash, hashPiServerTreeEntry(entry));\n\t\tsession.prefixHashes.push(session.treeHash);\n\t}\n}\n\nfunction assertValidLeaf(entries: SessionTreeEntry[], leafId: string | null): void {\n\tif (leafId === null) return;\n\tif (!entries.some((entry) => entry.id === leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n}\n\nexport function replaceSessionTree(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tassertValidLeaf(entries, leafId);\n\tconst session = getOrCreateSession(sessionId);\n\tsession.entries = entries.map((entry) => ({ ...entry }));\n\tsession.leafId = leafId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn session;\n}\n\nexport function appendSessionEntries(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst knownEntries = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst entriesToAppend: SessionTreeEntry[] = [];\n\tfor (const entry of entries) {\n\t\tconst knownEntry = knownEntries.get(entry.id);\n\t\tif (knownEntry) {\n\t\t\tif (!isDeepStrictEqual(knownEntry, entry)) {\n\t\t\t\tthrow new Error(`entry ${entry.id} already exists`);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.parentId !== null && !knownEntries.has(entry.parentId)) {\n\t\t\tthrow new Error(`parent entry ${entry.parentId} does not exist`);\n\t\t}\n\t\tconst entryToAppend = { ...entry };\n\t\tentriesToAppend.push(entryToAppend);\n\t\tknownEntries.set(entryToAppend.id, entryToAppend);\n\t}\n\tif (leafId !== null && !knownEntries.has(leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n\tif (entriesToAppend.length === 0 && session.leafId === leafId) {\n\t\treturn session;\n\t}\n\tsession.entries.push(...entriesToAppend);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tappendTreeHashes(session, entriesToAppend);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, entriesToAppend);\n\treturn session;\n}\n\nexport function switchSessionLeaf(sessionId: string, leafId: string | null): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, []);\n\treturn session;\n}\n\nexport function appendCompactionEntry(\n\tsessionId: string,\n\tcompaction: { summary: string; firstKeptEntryId: string; tokensBefore: number; details?: unknown },\n): { session: SessionState; entry: SessionTreeEntry } {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entry: SessionTreeEntry = {\n\t\ttype: \"compaction\",\n\t\tid: randomUUID(),\n\t\tparentId: session.leafId,\n\t\ttimestamp: new Date().toISOString(),\n\t\tsummary: compaction.summary,\n\t\tfirstKeptEntryId: compaction.firstKeptEntryId,\n\t\ttokensBefore: compaction.tokensBefore,\n\t\tdetails: compaction.details,\n\t};\n\tsession.entries.push(entry);\n\tsession.leafId = entry.id;\n\tappendTreeHashes(session, [entry]);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, [entry]);\n\treturn { session, entry };\n}\n\nexport function getActiveMessages(sessionId: string): Message[] {\n\tconst session = getSession(sessionId);\n\treturn session ? [...session.messages] : [];\n}\n\nexport function appendMessages(sessionId: string, delta: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entries = delta.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, session.leafId);\n\t\tsession.leafId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.entries.push(...entries);\n\tappendTreeHashes(session, entries);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, entries);\n\treturn session;\n}\n\nexport function appendAssistantResponse(sessionId: string, message: Message): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tsession.messages.push(message);\n\tsession.updatedAt = Date.now();\n\treturn session;\n}\n\nexport function replaceMessages(sessionId: string, messages: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tlet parentId: string | null = null;\n\tsession.entries = messages.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, parentId);\n\t\tparentId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.leafId = parentId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn session;\n}\n\nexport function dropLastAssistantError(sessionId: string): boolean {\n\tconst session = getSession(sessionId);\n\tif (!session) return false;\n\tconst leaf = session.leafId ? session.entries.find((entry) => entry.id === session.leafId) : undefined;\n\tif (leaf?.type !== \"message\" || leaf.message.role !== \"assistant\" || leaf.message.stopReason !== \"error\") {\n\t\treturn false;\n\t}\n\tsession.entries = session.entries.filter((entry) => entry.id !== leaf.id);\n\tsession.leafId = leaf.parentId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn true;\n}\n\nexport function deleteSession(sessionId: string): boolean {\n\treturn sessions.delete(sessionId);\n}\n\nexport function clearAllSessions(): void {\n\tsessions.clear();\n}\n\nfunction markWalPersistenceChange(session: SessionState, entries: SessionTreeEntry[]): void {\n\tif (session.persistenceChange?.kind === \"snapshot\") return;\n\tsession.persistenceChange = {\n\t\tkind: \"wal\",\n\t\tentries: [...(session.persistenceChange?.entries ?? []), ...entries.map((entry) => ({ ...entry }))],\n\t};\n}\n\nexport function markSessionPersisted(session: SessionState): void {\n\tsession.persistenceChange = undefined;\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { isDeepStrictEqual } from "node:util";
3
- import { buildSessionContext, convertToLlm } from "@earendil-works/pi-agent-core";
3
+ import { buildLegacySessionContext, convertToLlm } from "@earendil-works/pi-agent-core";
4
4
  import { appendPiServerTreeHash, buildPiServerTreePrefixHashes, hashPiServerSessionEntries, hashPiServerStaticContext, hashPiServerTreeEntry, } from "./pi-server-protocol.js";
5
5
  const sessions = new Map();
6
6
  function hashStaticContext(ctx) {
@@ -115,7 +115,7 @@ export function getSessionBranch(session) {
115
115
  }
116
116
  function deriveActiveMessages(session) {
117
117
  const branch = getSessionBranch(session);
118
- return convertToLlm(buildSessionContext(branch).messages);
118
+ return convertToLlm(buildLegacySessionContext(branch).messages);
119
119
  }
120
120
  function refreshActiveMessages(session) {
121
121
  session.messages = deriveActiveMessages(session);
@@ -1 +1 @@
1
- {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { buildSessionContext, convertToLlm, type SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport type { Message, Tool } from \"@earendil-works/pi-ai\";\nimport {\n\tappendPiServerTreeHash,\n\tbuildPiServerTreePrefixHashes,\n\thashPiServerSessionEntries,\n\thashPiServerStaticContext,\n\thashPiServerTreeEntry,\n} from \"./pi-server-protocol.ts\";\n\nexport interface SessionStaticContext {\n\tsystemPrompt?: string;\n\ttools?: Tool[];\n}\n\nexport interface SessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tstaticContextHash: string;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\ttreeHash: string;\n\tprefixHashes: string[];\n\tmessages: Message[];\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n\tpersistenceChange: SessionPersistenceChange | undefined;\n}\n\nexport interface SessionSummary {\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash: string;\n\tmessageCount: number;\n\tentryCount: number;\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport interface PersistedSessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport type SessionPersistenceChange = { kind: \"snapshot\" } | { kind: \"wal\"; entries: SessionTreeEntry[] };\n\nconst sessions = new Map<string, SessionState>();\n\nfunction hashStaticContext(ctx: SessionStaticContext | undefined): string {\n\treturn hashPiServerStaticContext(ctx);\n}\n\nexport function getOrCreateSession(sessionId: string): SessionState {\n\tlet session = sessions.get(sessionId);\n\tif (!session) {\n\t\tconst now = Date.now();\n\t\tsession = {\n\t\t\tsessionId,\n\t\t\tstaticContext: undefined,\n\t\t\tstaticContextHash: \"\",\n\t\t\tentries: [],\n\t\t\tleafId: null,\n\t\t\ttreeHash: hashPiServerSessionEntries([]),\n\t\t\tprefixHashes: buildPiServerTreePrefixHashes([]),\n\t\t\tmessages: [],\n\t\t\trevision: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t\tpersistenceChange: { kind: \"snapshot\" },\n\t\t};\n\t\tsessions.set(sessionId, session);\n\t}\n\treturn session;\n}\n\nexport function getSession(sessionId: string): SessionState | undefined {\n\treturn sessions.get(sessionId);\n}\n\nexport function hashSessionEntries(entries: SessionTreeEntry[]): string {\n\treturn hashPiServerSessionEntries(entries);\n}\n\nexport function listSessions(): SessionSummary[] {\n\treturn Array.from(sessions.values())\n\t\t.map((session) => ({\n\t\t\tsessionId: session.sessionId,\n\t\t\tstaticContextHash: session.staticContextHash,\n\t\t\ttreeHash: session.treeHash,\n\t\t\tmessageCount: session.messages.length,\n\t\t\tentryCount: session.entries.length,\n\t\t\tleafId: session.leafId,\n\t\t\trevision: session.revision,\n\t\t\tcreatedAt: session.createdAt,\n\t\t\tupdatedAt: session.updatedAt,\n\t\t}))\n\t\t.sort((a, b) => b.updatedAt - a.updatedAt || a.sessionId.localeCompare(b.sessionId));\n}\n\nexport function exportSessionState(session: SessionState): PersistedSessionState {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tentries: session.entries.map((entry) => ({ ...entry })),\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tcreatedAt: session.createdAt,\n\t\tupdatedAt: session.updatedAt,\n\t};\n}\n\nexport function restoreSessionState(persisted: PersistedSessionState): SessionState {\n\tassertValidLeaf(persisted.entries, persisted.leafId);\n\tconst session: SessionState = {\n\t\tsessionId: persisted.sessionId,\n\t\tstaticContext: persisted.staticContext,\n\t\tstaticContextHash: hashStaticContext(persisted.staticContext),\n\t\tentries: persisted.entries.map((entry) => ({ ...entry })),\n\t\tleafId: persisted.leafId,\n\t\ttreeHash: hashSessionEntries(persisted.entries),\n\t\tprefixHashes: buildPiServerTreePrefixHashes(persisted.entries),\n\t\tmessages: [],\n\t\trevision: persisted.revision,\n\t\tcreatedAt: persisted.createdAt,\n\t\tupdatedAt: persisted.updatedAt,\n\t\tpersistenceChange: undefined,\n\t};\n\trefreshActiveMessages(session);\n\tsessions.set(session.sessionId, session);\n\treturn session;\n}\n\nexport function setStaticContext(sessionId: string, context: SessionStaticContext): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst newHash = hashStaticContext(context);\n\tsession.staticContext = context;\n\tsession.staticContextHash = newHash;\n\tsession.updatedAt = Date.now();\n\tmarkWalPersistenceChange(session, []);\n\treturn session;\n}\n\nfunction entryToMessageEntry(message: Message, parentId: string | null): SessionTreeEntry {\n\treturn {\n\t\ttype: \"message\",\n\t\tid: randomUUID(),\n\t\tparentId,\n\t\ttimestamp: new Date(message.timestamp).toISOString(),\n\t\tmessage,\n\t};\n}\n\nexport function getSessionBranch(session: SessionState): SessionTreeEntry[] {\n\tconst byId = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst branch: SessionTreeEntry[] = [];\n\tlet current = session.leafId ? byId.get(session.leafId) : undefined;\n\tconst seen = new Set<string>();\n\twhile (current) {\n\t\tif (seen.has(current.id)) {\n\t\t\tthrow new Error(`session tree contains a parent cycle at entry ${current.id}`);\n\t\t}\n\t\tseen.add(current.id);\n\t\tbranch.unshift(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\treturn branch;\n}\n\nfunction deriveActiveMessages(session: SessionState): Message[] {\n\tconst branch = getSessionBranch(session);\n\treturn convertToLlm(buildSessionContext(branch).messages);\n}\n\nfunction refreshActiveMessages(session: SessionState): void {\n\tsession.messages = deriveActiveMessages(session);\n}\n\nfunction refreshTreeHashes(session: SessionState): void {\n\tsession.prefixHashes = buildPiServerTreePrefixHashes(session.entries);\n\tsession.treeHash = session.prefixHashes[session.prefixHashes.length - 1];\n}\n\nfunction appendTreeHashes(session: SessionState, entries: SessionTreeEntry[]): void {\n\tfor (const entry of entries) {\n\t\tsession.treeHash = appendPiServerTreeHash(session.treeHash, hashPiServerTreeEntry(entry));\n\t\tsession.prefixHashes.push(session.treeHash);\n\t}\n}\n\nfunction assertValidLeaf(entries: SessionTreeEntry[], leafId: string | null): void {\n\tif (leafId === null) return;\n\tif (!entries.some((entry) => entry.id === leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n}\n\nexport function replaceSessionTree(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tassertValidLeaf(entries, leafId);\n\tconst session = getOrCreateSession(sessionId);\n\tsession.entries = entries.map((entry) => ({ ...entry }));\n\tsession.leafId = leafId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn session;\n}\n\nexport function appendSessionEntries(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst knownEntries = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst entriesToAppend: SessionTreeEntry[] = [];\n\tfor (const entry of entries) {\n\t\tconst knownEntry = knownEntries.get(entry.id);\n\t\tif (knownEntry) {\n\t\t\tif (!isDeepStrictEqual(knownEntry, entry)) {\n\t\t\t\tthrow new Error(`entry ${entry.id} already exists`);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.parentId !== null && !knownEntries.has(entry.parentId)) {\n\t\t\tthrow new Error(`parent entry ${entry.parentId} does not exist`);\n\t\t}\n\t\tconst entryToAppend = { ...entry };\n\t\tentriesToAppend.push(entryToAppend);\n\t\tknownEntries.set(entryToAppend.id, entryToAppend);\n\t}\n\tif (leafId !== null && !knownEntries.has(leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n\tif (entriesToAppend.length === 0 && session.leafId === leafId) {\n\t\treturn session;\n\t}\n\tsession.entries.push(...entriesToAppend);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tappendTreeHashes(session, entriesToAppend);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, entriesToAppend);\n\treturn session;\n}\n\nexport function switchSessionLeaf(sessionId: string, leafId: string | null): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, []);\n\treturn session;\n}\n\nexport function appendCompactionEntry(\n\tsessionId: string,\n\tcompaction: { summary: string; firstKeptEntryId: string; tokensBefore: number; details?: unknown },\n): { session: SessionState; entry: SessionTreeEntry } {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entry: SessionTreeEntry = {\n\t\ttype: \"compaction\",\n\t\tid: randomUUID(),\n\t\tparentId: session.leafId,\n\t\ttimestamp: new Date().toISOString(),\n\t\tsummary: compaction.summary,\n\t\tfirstKeptEntryId: compaction.firstKeptEntryId,\n\t\ttokensBefore: compaction.tokensBefore,\n\t\tdetails: compaction.details,\n\t};\n\tsession.entries.push(entry);\n\tsession.leafId = entry.id;\n\tappendTreeHashes(session, [entry]);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, [entry]);\n\treturn { session, entry };\n}\n\nexport function getActiveMessages(sessionId: string): Message[] {\n\tconst session = getSession(sessionId);\n\treturn session ? [...session.messages] : [];\n}\n\nexport function appendMessages(sessionId: string, delta: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entries = delta.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, session.leafId);\n\t\tsession.leafId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.entries.push(...entries);\n\tappendTreeHashes(session, entries);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, entries);\n\treturn session;\n}\n\nexport function appendAssistantResponse(sessionId: string, message: Message): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tsession.messages.push(message);\n\tsession.updatedAt = Date.now();\n\treturn session;\n}\n\nexport function replaceMessages(sessionId: string, messages: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tlet parentId: string | null = null;\n\tsession.entries = messages.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, parentId);\n\t\tparentId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.leafId = parentId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn session;\n}\n\nexport function dropLastAssistantError(sessionId: string): boolean {\n\tconst session = getSession(sessionId);\n\tif (!session) return false;\n\tconst leaf = session.leafId ? session.entries.find((entry) => entry.id === session.leafId) : undefined;\n\tif (leaf?.type !== \"message\" || leaf.message.role !== \"assistant\" || leaf.message.stopReason !== \"error\") {\n\t\treturn false;\n\t}\n\tsession.entries = session.entries.filter((entry) => entry.id !== leaf.id);\n\tsession.leafId = leaf.parentId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn true;\n}\n\nexport function deleteSession(sessionId: string): boolean {\n\treturn sessions.delete(sessionId);\n}\n\nexport function clearAllSessions(): void {\n\tsessions.clear();\n}\n\nfunction markWalPersistenceChange(session: SessionState, entries: SessionTreeEntry[]): void {\n\tif (session.persistenceChange?.kind === \"snapshot\") return;\n\tsession.persistenceChange = {\n\t\tkind: \"wal\",\n\t\tentries: [...(session.persistenceChange?.entries ?? []), ...entries.map((entry) => ({ ...entry }))],\n\t};\n}\n\nexport function markSessionPersisted(session: SessionState): void {\n\tsession.persistenceChange = undefined;\n}\n"]}
1
+ {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { buildLegacySessionContext, convertToLlm, type SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport type { Message, Tool } from \"@earendil-works/pi-ai\";\nimport {\n\tappendPiServerTreeHash,\n\tbuildPiServerTreePrefixHashes,\n\thashPiServerSessionEntries,\n\thashPiServerStaticContext,\n\thashPiServerTreeEntry,\n} from \"./pi-server-protocol.ts\";\n\nexport interface SessionStaticContext {\n\tsystemPrompt?: string;\n\ttools?: Tool[];\n}\n\nexport interface SessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tstaticContextHash: string;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\ttreeHash: string;\n\tprefixHashes: string[];\n\tmessages: Message[];\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n\tpersistenceChange: SessionPersistenceChange | undefined;\n}\n\nexport interface SessionSummary {\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash: string;\n\tmessageCount: number;\n\tentryCount: number;\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport interface PersistedSessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport type SessionPersistenceChange = { kind: \"snapshot\" } | { kind: \"wal\"; entries: SessionTreeEntry[] };\n\nconst sessions = new Map<string, SessionState>();\n\nfunction hashStaticContext(ctx: SessionStaticContext | undefined): string {\n\treturn hashPiServerStaticContext(ctx);\n}\n\nexport function getOrCreateSession(sessionId: string): SessionState {\n\tlet session = sessions.get(sessionId);\n\tif (!session) {\n\t\tconst now = Date.now();\n\t\tsession = {\n\t\t\tsessionId,\n\t\t\tstaticContext: undefined,\n\t\t\tstaticContextHash: \"\",\n\t\t\tentries: [],\n\t\t\tleafId: null,\n\t\t\ttreeHash: hashPiServerSessionEntries([]),\n\t\t\tprefixHashes: buildPiServerTreePrefixHashes([]),\n\t\t\tmessages: [],\n\t\t\trevision: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t\tpersistenceChange: { kind: \"snapshot\" },\n\t\t};\n\t\tsessions.set(sessionId, session);\n\t}\n\treturn session;\n}\n\nexport function getSession(sessionId: string): SessionState | undefined {\n\treturn sessions.get(sessionId);\n}\n\nexport function hashSessionEntries(entries: SessionTreeEntry[]): string {\n\treturn hashPiServerSessionEntries(entries);\n}\n\nexport function listSessions(): SessionSummary[] {\n\treturn Array.from(sessions.values())\n\t\t.map((session) => ({\n\t\t\tsessionId: session.sessionId,\n\t\t\tstaticContextHash: session.staticContextHash,\n\t\t\ttreeHash: session.treeHash,\n\t\t\tmessageCount: session.messages.length,\n\t\t\tentryCount: session.entries.length,\n\t\t\tleafId: session.leafId,\n\t\t\trevision: session.revision,\n\t\t\tcreatedAt: session.createdAt,\n\t\t\tupdatedAt: session.updatedAt,\n\t\t}))\n\t\t.sort((a, b) => b.updatedAt - a.updatedAt || a.sessionId.localeCompare(b.sessionId));\n}\n\nexport function exportSessionState(session: SessionState): PersistedSessionState {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tentries: session.entries.map((entry) => ({ ...entry })),\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tcreatedAt: session.createdAt,\n\t\tupdatedAt: session.updatedAt,\n\t};\n}\n\nexport function restoreSessionState(persisted: PersistedSessionState): SessionState {\n\tassertValidLeaf(persisted.entries, persisted.leafId);\n\tconst session: SessionState = {\n\t\tsessionId: persisted.sessionId,\n\t\tstaticContext: persisted.staticContext,\n\t\tstaticContextHash: hashStaticContext(persisted.staticContext),\n\t\tentries: persisted.entries.map((entry) => ({ ...entry })),\n\t\tleafId: persisted.leafId,\n\t\ttreeHash: hashSessionEntries(persisted.entries),\n\t\tprefixHashes: buildPiServerTreePrefixHashes(persisted.entries),\n\t\tmessages: [],\n\t\trevision: persisted.revision,\n\t\tcreatedAt: persisted.createdAt,\n\t\tupdatedAt: persisted.updatedAt,\n\t\tpersistenceChange: undefined,\n\t};\n\trefreshActiveMessages(session);\n\tsessions.set(session.sessionId, session);\n\treturn session;\n}\n\nexport function setStaticContext(sessionId: string, context: SessionStaticContext): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst newHash = hashStaticContext(context);\n\tsession.staticContext = context;\n\tsession.staticContextHash = newHash;\n\tsession.updatedAt = Date.now();\n\tmarkWalPersistenceChange(session, []);\n\treturn session;\n}\n\nfunction entryToMessageEntry(message: Message, parentId: string | null): SessionTreeEntry {\n\treturn {\n\t\ttype: \"message\",\n\t\tid: randomUUID(),\n\t\tparentId,\n\t\ttimestamp: new Date(message.timestamp).toISOString(),\n\t\tmessage,\n\t};\n}\n\nexport function getSessionBranch(session: SessionState): SessionTreeEntry[] {\n\tconst byId = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst branch: SessionTreeEntry[] = [];\n\tlet current = session.leafId ? byId.get(session.leafId) : undefined;\n\tconst seen = new Set<string>();\n\twhile (current) {\n\t\tif (seen.has(current.id)) {\n\t\t\tthrow new Error(`session tree contains a parent cycle at entry ${current.id}`);\n\t\t}\n\t\tseen.add(current.id);\n\t\tbranch.unshift(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\treturn branch;\n}\n\nfunction deriveActiveMessages(session: SessionState): Message[] {\n\tconst branch = getSessionBranch(session);\n\treturn convertToLlm(buildLegacySessionContext(branch).messages);\n}\n\nfunction refreshActiveMessages(session: SessionState): void {\n\tsession.messages = deriveActiveMessages(session);\n}\n\nfunction refreshTreeHashes(session: SessionState): void {\n\tsession.prefixHashes = buildPiServerTreePrefixHashes(session.entries);\n\tsession.treeHash = session.prefixHashes[session.prefixHashes.length - 1];\n}\n\nfunction appendTreeHashes(session: SessionState, entries: SessionTreeEntry[]): void {\n\tfor (const entry of entries) {\n\t\tsession.treeHash = appendPiServerTreeHash(session.treeHash, hashPiServerTreeEntry(entry));\n\t\tsession.prefixHashes.push(session.treeHash);\n\t}\n}\n\nfunction assertValidLeaf(entries: SessionTreeEntry[], leafId: string | null): void {\n\tif (leafId === null) return;\n\tif (!entries.some((entry) => entry.id === leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n}\n\nexport function replaceSessionTree(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tassertValidLeaf(entries, leafId);\n\tconst session = getOrCreateSession(sessionId);\n\tsession.entries = entries.map((entry) => ({ ...entry }));\n\tsession.leafId = leafId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn session;\n}\n\nexport function appendSessionEntries(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst knownEntries = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst entriesToAppend: SessionTreeEntry[] = [];\n\tfor (const entry of entries) {\n\t\tconst knownEntry = knownEntries.get(entry.id);\n\t\tif (knownEntry) {\n\t\t\tif (!isDeepStrictEqual(knownEntry, entry)) {\n\t\t\t\tthrow new Error(`entry ${entry.id} already exists`);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.parentId !== null && !knownEntries.has(entry.parentId)) {\n\t\t\tthrow new Error(`parent entry ${entry.parentId} does not exist`);\n\t\t}\n\t\tconst entryToAppend = { ...entry };\n\t\tentriesToAppend.push(entryToAppend);\n\t\tknownEntries.set(entryToAppend.id, entryToAppend);\n\t}\n\tif (leafId !== null && !knownEntries.has(leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n\tif (entriesToAppend.length === 0 && session.leafId === leafId) {\n\t\treturn session;\n\t}\n\tsession.entries.push(...entriesToAppend);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tappendTreeHashes(session, entriesToAppend);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, entriesToAppend);\n\treturn session;\n}\n\nexport function switchSessionLeaf(sessionId: string, leafId: string | null): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, []);\n\treturn session;\n}\n\nexport function appendCompactionEntry(\n\tsessionId: string,\n\tcompaction: { summary: string; firstKeptEntryId: string; tokensBefore: number; details?: unknown },\n): { session: SessionState; entry: SessionTreeEntry } {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entry: SessionTreeEntry = {\n\t\ttype: \"compaction\",\n\t\tid: randomUUID(),\n\t\tparentId: session.leafId,\n\t\ttimestamp: new Date().toISOString(),\n\t\tsummary: compaction.summary,\n\t\tfirstKeptEntryId: compaction.firstKeptEntryId,\n\t\ttokensBefore: compaction.tokensBefore,\n\t\tdetails: compaction.details,\n\t};\n\tsession.entries.push(entry);\n\tsession.leafId = entry.id;\n\tappendTreeHashes(session, [entry]);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, [entry]);\n\treturn { session, entry };\n}\n\nexport function getActiveMessages(sessionId: string): Message[] {\n\tconst session = getSession(sessionId);\n\treturn session ? [...session.messages] : [];\n}\n\nexport function appendMessages(sessionId: string, delta: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entries = delta.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, session.leafId);\n\t\tsession.leafId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.entries.push(...entries);\n\tappendTreeHashes(session, entries);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, entries);\n\treturn session;\n}\n\nexport function appendAssistantResponse(sessionId: string, message: Message): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tsession.messages.push(message);\n\tsession.updatedAt = Date.now();\n\treturn session;\n}\n\nexport function replaceMessages(sessionId: string, messages: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tlet parentId: string | null = null;\n\tsession.entries = messages.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, parentId);\n\t\tparentId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.leafId = parentId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn session;\n}\n\nexport function dropLastAssistantError(sessionId: string): boolean {\n\tconst session = getSession(sessionId);\n\tif (!session) return false;\n\tconst leaf = session.leafId ? session.entries.find((entry) => entry.id === session.leafId) : undefined;\n\tif (leaf?.type !== \"message\" || leaf.message.role !== \"assistant\" || leaf.message.stopReason !== \"error\") {\n\t\treturn false;\n\t}\n\tsession.entries = session.entries.filter((entry) => entry.id !== leaf.id);\n\tsession.leafId = leaf.parentId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn true;\n}\n\nexport function deleteSession(sessionId: string): boolean {\n\treturn sessions.delete(sessionId);\n}\n\nexport function clearAllSessions(): void {\n\tsessions.clear();\n}\n\nfunction markWalPersistenceChange(session: SessionState, entries: SessionTreeEntry[]): void {\n\tif (session.persistenceChange?.kind === \"snapshot\") return;\n\tsession.persistenceChange = {\n\t\tkind: \"wal\",\n\t\tentries: [...(session.persistenceChange?.entries ?? []), ...entries.map((entry) => ({ ...entry }))],\n\t};\n}\n\nexport function markSessionPersisted(session: SessionState): void {\n\tsession.persistenceChange = undefined;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@averyyy/pi-server",
3
- "version": "0.82.1-piclient.9",
3
+ "version": "0.84.1-piclient.1",
4
4
  "description": "Proxy server for pi-client that stores session state and forwards incremental requests to upstream LLM providers",
5
5
  "type": "module",
6
6
  "bin": {
@@ -27,8 +27,8 @@
27
27
  "prepublishOnly": "npm run clean && npm run build"
28
28
  },
29
29
  "dependencies": {
30
- "@earendil-works/pi-ai": "npm:@averyyy/pi-ai@0.82.1-piclient.9",
31
- "@earendil-works/pi-agent-core": "npm:@averyyy/pi-agent-core@0.82.1-piclient.9"
30
+ "@earendil-works/pi-ai": "npm:@averyyy/pi-ai@0.84.1-piclient.1",
31
+ "@earendil-works/pi-agent-core": "npm:@averyyy/pi-agent-core@0.84.1-piclient.1"
32
32
  },
33
33
  "keywords": [
34
34
  "pi-server",