@averyyy/pi-coding-agent 0.80.3-piclient.5 → 0.80.3-piclient.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"pi-server-client.d.ts","sourceRoot":"","sources":["../../src/core/pi-server-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAA8B,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjH,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,OAAO,EACZ,WAAW,EACX,KAAK,OAAO,EACZ,KAAK,KAAK,EAEV,KAAK,mBAAmB,EACxB,MAAM,uBAAuB,CAAC;AAG/B,cAAM,mBAAoB,SAAQ,WAAW,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;IACrF,cASC;CACD;AAED,iBAAS,YAAY,IAAI,MAAM,CAE9B;AAED,iBAAS,YAAY,IAAI,MAAM,CAE9B;AAkBD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAStD;AAuBD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAO5D;AAED,wBAAgB,uBAAuB,IAAI,IAAI,CAO9C;AAaD,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACxC,UAAU,EAAE,aAAa,CAAC;IAC1B,eAAe,EAAE,gBAAgB,CAAC;IAClC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACvC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB;IACjE,WAAW,CAAC,EAAE,oBAAoB,CAAC;IACnC,mBAAmB,CAAC,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClF;AAcD,UAAU,mBAAmB;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,mBAAmB,CAAC,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClF;AA0OD,wBAAsB,gBAAgB,CACrC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,oBAAoB,EAC1B,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAIf;AAyFD,MAAM,WAAW,sBAAuB,SAAQ,mBAAmB;IAClE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,oBAAoB,CAAC;IACnC,mBAAmB,CAAC,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClF;AAED,wBAAsB,eAAe,CACpC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,sBAAsB,GAC9B,OAAO,CAAC,wBAAwB,CAAC,CAgDnC;AAED,wBAAsB,8BAA8B,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIrF;AAED,wBAAsB,cAAc,CACnC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,qBAAqB,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CAiH9B;AA2FD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport type { CompactResult, ProxyAssistantMessageEvent, SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tEventStream,\n\ttype Message,\n\ttype Model,\n\tparseStreamingJson,\n\ttype SimpleStreamOptions,\n} from \"@earendil-works/pi-ai\";\nimport { ChunkRequest } from \"./pi-server-request.ts\";\n\nclass PiServerEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {\n\tconstructor() {\n\t\tsuper(\n\t\t\t(event) => event.type === \"done\" || event.type === \"error\",\n\t\t\t(event) => {\n\t\t\t\tif (event.type === \"done\") return event.message;\n\t\t\t\tif (event.type === \"error\") return event.error;\n\t\t\t\tthrow new Error(\"Unexpected event type\");\n\t\t\t},\n\t\t);\n\t}\n}\n\nfunction getServerUrl(): string {\n\treturn process.env.PI_SERVER_URL ?? \"http://127.0.0.1:4217\";\n}\n\nfunction getAuthToken(): string {\n\treturn process.env.PI_SERVER_AUTH_TOKEN ?? \"\";\n}\n\nfunction createPiServerRequest(signal?: AbortSignal): ChunkRequest {\n\treturn new ChunkRequest({\n\t\tserverUrl: getServerUrl(),\n\t\tauthToken: getAuthToken(),\n\t\tsignal,\n\t});\n}\n\nconst sessionStaticContextHashes = new Map<string, string>();\nconst sessionSyncedEntryIds = new Map<string, Set<string>>();\nconst sessionTreeHashes = new Map<string, string>();\nconst sessionTreeEntryCounts = new Map<string, number>();\nconst sessionTreeLeafIds = new Map<string, string | null>();\nconst sessionHasTemporaryTree = new Set<string>();\nconst RESPONSE_BODY_EXCERPT_CHARS = 500;\n\nexport function hashStaticContext(ctx: Context): string {\n\tconst parts: string[] = [];\n\tif (ctx.systemPrompt !== undefined) parts.push(`sp:${ctx.systemPrompt}`);\n\tif (ctx.tools) {\n\t\tparts.push(\n\t\t\t`t:${JSON.stringify(ctx.tools.map((t) => ({ name: t.name, description: t.description, parameters: t.parameters })))}`,\n\t\t);\n\t}\n\treturn parts.join(\"|\");\n}\n\nfunction hashEntries(entries: SessionTreeEntry[]): string {\n\treturn createHash(\"sha256\").update(JSON.stringify(entries)).digest(\"hex\");\n}\n\nfunction getLinearTreeFromMessages(messages: Message[]): { entries: SessionTreeEntry[]; leafId: string | null } {\n\tlet parentId: string | null = null;\n\tconst entries = messages.map((message, index): SessionTreeEntry => {\n\t\tconst id = `message-${index}`;\n\t\tconst entry: SessionTreeEntry = {\n\t\t\ttype: \"message\",\n\t\t\tid,\n\t\t\tparentId,\n\t\t\ttimestamp: new Date(message.timestamp).toISOString(),\n\t\t\tmessage,\n\t\t};\n\t\tparentId = id;\n\t\treturn entry;\n\t});\n\treturn { entries, leafId: parentId };\n}\n\nexport function resetSessionTracking(sessionId: string): void {\n\tsessionStaticContextHashes.delete(sessionId);\n\tsessionSyncedEntryIds.delete(sessionId);\n\tsessionTreeHashes.delete(sessionId);\n\tsessionTreeEntryCounts.delete(sessionId);\n\tsessionTreeLeafIds.delete(sessionId);\n\tsessionHasTemporaryTree.delete(sessionId);\n}\n\nexport function resetAllSessionTracking(): void {\n\tsessionStaticContextHashes.clear();\n\tsessionSyncedEntryIds.clear();\n\tsessionTreeHashes.clear();\n\tsessionTreeEntryCounts.clear();\n\tsessionTreeLeafIds.clear();\n\tsessionHasTemporaryTree.clear();\n}\n\ninterface SessionInitResponse {\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash?: string;\n\tmessageCount: number;\n\tentryCount?: number;\n\tleafId?: string | null;\n\trevision?: number;\n\tfromCache?: boolean;\n}\n\nexport interface PiServerTreeSnapshot {\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\treplace?: boolean;\n}\n\nexport interface PiServerCompactionResult {\n\tcompaction: CompactResult;\n\tcompactionEntry: SessionTreeEntry;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tmessages: Message[];\n}\n\nexport interface PiServerHistorySnapshot {\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tmessages: Message[];\n}\n\nexport interface PiServerStreamOptions extends SimpleStreamOptions {\n\tsessionTree?: PiServerTreeSnapshot;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\ninterface PiServerResponseFailure {\n\tdetails: string;\n\tmatchText: string;\n}\n\ninterface PiServerHistoryResponse extends Partial<PiServerHistorySnapshot> {\n\tsessionId: string;\n\ttreeHash?: string;\n\tentryCount?: number;\n\tleafId?: string | null;\n}\n\ninterface PiServerSyncOptions {\n\tsignal?: AbortSignal;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction getResponseStatus(response: Response): string {\n\tconst status = response.statusText ? `${response.status} ${response.statusText}` : String(response.status);\n\treturn response.status >= 500 ? `${status} (server error)` : status;\n}\n\nfunction getResponseContentType(response: Response): string {\n\treturn response.headers.get(\"content-type\") ?? \"unknown\";\n}\n\nfunction getBodyExcerpt(text: string): string {\n\tconst trimmed = text.trim();\n\tif (!trimmed) return \"<empty>\";\n\tconst excerpt = trimmed.slice(0, RESPONSE_BODY_EXCERPT_CHARS);\n\treturn excerpt.length < trimmed.length ? `${excerpt}...` : excerpt;\n}\n\nfunction getJsonErrorText(text: string): string | undefined {\n\tif (!text) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(text) as unknown;\n\t\tif (!isObject(parsed)) return undefined;\n\t\tconst error = parsed.error;\n\t\treturn typeof error === \"string\" && error.length > 0 ? error : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction formatResponseDetails(response: Response, bodyText: string): string {\n\tconst body = getJsonErrorText(bodyText) ?? getBodyExcerpt(bodyText);\n\treturn `${getResponseStatus(response)}; content-type: ${getResponseContentType(response)}; body excerpt: ${body}`;\n}\n\nasync function readPiServerFailure(response: Response): Promise<PiServerResponseFailure> {\n\tconst bodyText = await response.text();\n\treturn {\n\t\tdetails: formatResponseDetails(response, bodyText),\n\t\tmatchText: getJsonErrorText(bodyText) ?? bodyText,\n\t};\n}\n\nasync function readPiServerJson<T>(response: Response, errorPrefix: string): Promise<T> {\n\tconst bodyText = await response.text();\n\tif (!response.ok) {\n\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)})`);\n\t}\n\ttry {\n\t\treturn JSON.parse(bodyText) as T;\n\t} catch {\n\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)}; expected JSON)`);\n\t}\n}\n\nasync function ensurePiServerEventStream(response: Response): Promise<void> {\n\tconst contentType = getResponseContentType(response);\n\tif (contentType.toLowerCase().split(\";\")[0]?.trim() === \"text/event-stream\") {\n\t\treturn;\n\t}\n\tconst bodyText = await response.text();\n\tthrow new Error(`pi-server error: ${formatResponseDetails(response, bodyText)}; expected text/event-stream`);\n}\n\nasync function ensureSessionInit(\n\tsessionId: string,\n\tcontext: Context,\n\trequest: ChunkRequest,\n): Promise<SessionInitResponse> {\n\tconst currentHash = hashStaticContext(context);\n\tconst previousHash = sessionStaticContextHashes.get(sessionId);\n\n\tif (previousHash === currentHash) {\n\t\treturn {\n\t\t\tsessionId,\n\t\t\tstaticContextHash: previousHash,\n\t\t\tmessageCount: 0,\n\t\t\tfromCache: true,\n\t\t};\n\t}\n\n\tconst staticContext = {\n\t\tsystemPrompt: context.systemPrompt,\n\t\ttools: context.tools,\n\t};\n\n\tconst endpoint = previousHash === undefined ? \"/api/session/init\" : \"/api/session/update\";\n\n\tconst response = await request.postJson(endpoint, { sessionId, staticContext });\n\n\tconst result = await readPiServerJson<SessionInitResponse>(response, \"Session init failed\");\n\tsessionStaticContextHashes.set(sessionId, currentHash);\n\tif (result.treeHash !== undefined) {\n\t\tsessionTreeHashes.set(sessionId, result.treeHash);\n\t\tsessionTreeLeafIds.set(sessionId, result.leafId ?? null);\n\t\tif (result.entryCount !== undefined) {\n\t\t\tsessionTreeEntryCounts.set(sessionId, result.entryCount);\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction getStaticContext(context: Context) {\n\treturn {\n\t\tsystemPrompt: context.systemPrompt,\n\t\ttools: context.tools,\n\t};\n}\n\nfunction markTreeSynced(sessionId: string, tree: PiServerTreeSnapshot): void {\n\tsessionSyncedEntryIds.set(sessionId, new Set(tree.entries.map((entry) => entry.id)));\n\tsessionTreeHashes.set(sessionId, hashEntries(tree.entries));\n\tsessionTreeEntryCounts.set(sessionId, tree.entries.length);\n\tsessionTreeLeafIds.set(sessionId, tree.leafId);\n\tif (tree.replace) {\n\t\tsessionHasTemporaryTree.add(sessionId);\n\t} else {\n\t\tsessionHasTemporaryTree.delete(sessionId);\n\t}\n}\n\nasync function postTreeJson(\n\trequest: ChunkRequest,\n\tendpoint: string,\n\tbody: unknown,\n\terrorPrefix: string,\n): Promise<void> {\n\tconst response = await request.postJson(endpoint, body);\n\tawait readPiServerJson<unknown>(response, errorPrefix);\n}\n\nasync function fetchPiServerHistory(\n\tsessionId: string,\n\trequest: ChunkRequest,\n): Promise<PiServerHistorySnapshot | undefined> {\n\tconst response = await request.getJson(`/api/session/${encodeURIComponent(sessionId)}/history`);\n\tif (response.status === 404) {\n\t\treturn undefined;\n\t}\n\tconst history = await readPiServerJson<PiServerHistoryResponse>(response, \"Session history reconciliation failed\");\n\tif (!history.entries || history.leafId === undefined || !history.messages) {\n\t\tthrow new Error(\"Session history reconciliation failed (response did not include the session tree)\");\n\t}\n\treturn {\n\t\tentries: history.entries,\n\t\tleafId: history.leafId,\n\t\tmessages: history.messages,\n\t};\n}\n\nasync function applyPiServerHistory(\n\tsessionId: string,\n\tsnapshot: PiServerHistorySnapshot,\n\tonHistoryReconciled: ((snapshot: PiServerHistorySnapshot) => void | Promise<void>) | undefined,\n): Promise<void> {\n\tmarkTreeSynced(sessionId, snapshot);\n\tawait onHistoryReconciled?.(snapshot);\n}\n\nfunction getKnownServerPrefixIds(sessionId: string, entries: SessionTreeEntry[]): Set<string> | undefined {\n\tconst entryCount = sessionTreeEntryCounts.get(sessionId);\n\tconst treeHash = sessionTreeHashes.get(sessionId);\n\tif (entryCount === undefined || treeHash === undefined || entryCount > entries.length) {\n\t\treturn undefined;\n\t}\n\tconst prefix = entries.slice(0, entryCount);\n\tif (hashEntries(prefix) !== treeHash) {\n\t\treturn undefined;\n\t}\n\tconst ids = new Set(prefix.map((entry) => entry.id));\n\tsessionSyncedEntryIds.set(sessionId, ids);\n\treturn ids;\n}\n\nfunction isRecoverableTreeDivergenceError(error: unknown): boolean {\n\tconst message = error instanceof Error ? error.message : String(error);\n\treturn /parent entry .* does not exist|leafId .* does not exist|entry .* already exists/i.test(message);\n}\n\nfunction isRecoverableMissingServerState(response: Response, errorBody: string): boolean {\n\tif (response.status !== 400 && response.status !== 404) return false;\n\treturn /Session has no static context|session not found|parent entry .* does not exist|leafId .* does not exist|entry .* already exists/i.test(\n\t\terrorBody,\n\t);\n}\n\nasync function syncFullPiServerTree(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n): Promise<void> {\n\tawait postTreeJson(\n\t\trequest,\n\t\t\"/api/session/tree/sync\",\n\t\t{\n\t\t\tsessionId,\n\t\t\tentries: tree.entries,\n\t\t\tleafId: tree.leafId,\n\t\t\tstaticContext: getStaticContext(context),\n\t\t},\n\t\t\"Session tree sync failed\",\n\t);\n\tmarkTreeSynced(sessionId, tree);\n}\n\nfunction shouldUseServerHistory(sessionId: string, tree: PiServerTreeSnapshot): boolean {\n\treturn !tree.replace && !sessionHasTemporaryTree.has(sessionId) && (sessionTreeEntryCounts.get(sessionId) ?? 0) > 0;\n}\n\nasync function recoverPiServerTreeDivergence(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>,\n): Promise<void> {\n\tif (shouldUseServerHistory(sessionId, tree)) {\n\t\tconst snapshot = await fetchPiServerHistory(sessionId, request);\n\t\tif (snapshot && snapshot.entries.length > 0) {\n\t\t\tawait applyPiServerHistory(sessionId, snapshot, onHistoryReconciled);\n\t\t\tthrow new Error(\n\t\t\t\t\"pi-server history differed from local history; local session was reconciled to server history\",\n\t\t\t);\n\t\t}\n\t}\n\n\tawait syncFullPiServerTree(sessionId, context, tree, request);\n}\n\nexport async function syncPiServerTree(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\toptions?: PiServerSyncOptions,\n): Promise<void> {\n\tconst request = createPiServerRequest(options?.signal);\n\tawait ensureSessionInit(sessionId, context, request);\n\tawait syncPiServerTreeWithRequest(sessionId, context, tree, request, options?.onHistoryReconciled);\n}\n\nasync function syncPiServerTreeWithRequest(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>,\n): Promise<void> {\n\tconst syncTree = tree;\n\tconst currentHash = hashEntries(syncTree.entries);\n\tconst previousHash = sessionTreeHashes.get(sessionId);\n\tconst previousLeafId = sessionTreeLeafIds.get(sessionId);\n\n\tif (!tree.replace && previousHash === currentHash) {\n\t\tif (previousLeafId !== syncTree.leafId) {\n\t\t\ttry {\n\t\t\t\tawait postTreeJson(\n\t\t\t\t\trequest,\n\t\t\t\t\t\"/api/session/tree/switch\",\n\t\t\t\t\t{ sessionId, leafId: syncTree.leafId },\n\t\t\t\t\t\"Session tree switch failed\",\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (!isRecoverableTreeDivergenceError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tawait recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsessionTreeLeafIds.set(sessionId, syncTree.leafId);\n\t\t}\n\t\tmarkTreeSynced(sessionId, syncTree);\n\t\treturn;\n\t}\n\n\tconst syncedIds =\n\t\t!tree.replace && !sessionHasTemporaryTree.has(sessionId)\n\t\t\t? (sessionSyncedEntryIds.get(sessionId) ?? getKnownServerPrefixIds(sessionId, syncTree.entries))\n\t\t\t: undefined;\n\tif (syncedIds) {\n\t\tconst deltaEntries = syncTree.entries.filter((entry) => !syncedIds.has(entry.id));\n\t\tif (deltaEntries.length > 0) {\n\t\t\ttry {\n\t\t\t\tawait postTreeJson(\n\t\t\t\t\trequest,\n\t\t\t\t\t\"/api/session/tree/append\",\n\t\t\t\t\t{\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\tentries: deltaEntries,\n\t\t\t\t\t\tleafId: syncTree.leafId,\n\t\t\t\t\t\tstaticContext: getStaticContext(context),\n\t\t\t\t\t},\n\t\t\t\t\t\"Session tree append failed\",\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (!isRecoverableTreeDivergenceError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tawait recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmarkTreeSynced(sessionId, syncTree);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tawait recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n}\n\nfunction serializeOptions(options: SimpleStreamOptions | undefined): SimpleStreamOptions {\n\treturn {\n\t\ttemperature: options?.temperature,\n\t\tmaxTokens: options?.maxTokens,\n\t\treasoning: options?.reasoning,\n\t\tcacheRetention: options?.cacheRetention,\n\t\tsessionId: options?.sessionId,\n\t\tapiKey: options?.apiKey,\n\t\theaders: options?.headers,\n\t\tmetadata: options?.metadata,\n\t\ttransport: options?.transport,\n\t\tthinkingBudgets: options?.thinkingBudgets,\n\t\ttimeoutMs: options?.timeoutMs,\n\t\twebsocketConnectTimeoutMs: options?.websocketConnectTimeoutMs,\n\t\tmaxRetries: options?.maxRetries,\n\t\tmaxRetryDelayMs: options?.maxRetryDelayMs,\n\t};\n}\n\nexport interface PiServerCompactOptions extends SimpleStreamOptions {\n\tcustomInstructions?: string;\n\tsettings?: unknown;\n\tsessionTree?: PiServerTreeSnapshot;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\nexport async function compactPiServer(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions?: PiServerCompactOptions,\n): Promise<PiServerCompactionResult> {\n\tconst sessionId = options?.sessionId ?? \"default\";\n\tconst request = createPiServerRequest(options?.signal);\n\n\tawait ensureSessionInit(sessionId, context, request);\n\tconst tree = options?.sessionTree ?? getLinearTreeFromMessages(context.messages as Message[]);\n\tawait syncPiServerTreeWithRequest(sessionId, context, tree, request, options?.onHistoryReconciled);\n\n\tconst makeBody = () => ({\n\t\tsessionId,\n\t\tmodel,\n\t\toptions: serializeOptions(options),\n\t\tsettings: options?.settings,\n\t\tcustomInstructions: options?.customInstructions,\n\t});\n\tlet response = await request.postJson(\"/api/session/compact\", makeBody());\n\tif (!response.ok) {\n\t\tlet failure = await readPiServerFailure(response);\n\t\tif (!options?.signal?.aborted && isRecoverableMissingServerState(response, failure.matchText)) {\n\t\t\tresetSessionTracking(sessionId);\n\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\tawait syncPiServerTreeWithRequest(sessionId, context, tree, request, options?.onHistoryReconciled);\n\t\t\tresponse = await request.postJson(\"/api/session/compact\", makeBody());\n\t\t\tif (response.ok) {\n\t\t\t\tfailure = { details: \"\", matchText: \"\" };\n\t\t\t} else {\n\t\t\t\tfailure = await readPiServerFailure(response);\n\t\t\t}\n\t\t}\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Server compaction failed (${failure.details})`);\n\t\t}\n\t}\n\tconst result = await readPiServerJson<Partial<PiServerCompactionResult>>(response, \"Server compaction failed\");\n\tif (!result.compaction) {\n\t\tthrow new Error(\"Server compaction response did not include a compaction result\");\n\t}\n\tif (!result.compactionEntry || !result.entries || result.leafId === undefined || !result.messages) {\n\t\tthrow new Error(\"Server compaction response did not include the updated session tree\");\n\t}\n\tmarkTreeSynced(sessionId, { entries: result.entries, leafId: result.leafId });\n\treturn {\n\t\tcompaction: result.compaction,\n\t\tcompactionEntry: result.compactionEntry,\n\t\tentries: result.entries,\n\t\tleafId: result.leafId,\n\t\tmessages: result.messages,\n\t};\n}\n\nexport async function dropLastPiServerAssistantError(sessionId: string): Promise<void> {\n\tconst request = createPiServerRequest();\n\tconst response = await request.postJson(\"/api/session/drop-last-assistant-error\", { sessionId });\n\tawait readPiServerJson<unknown>(response, \"Dropping server assistant error failed\");\n}\n\nexport async function streamPiServer(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions?: PiServerStreamOptions,\n): Promise<PiServerEventStream> {\n\tconst sessionId = options?.sessionId ?? randomUUID();\n\tconst isEphemeralSession = options?.sessionId === undefined;\n\tconst stream = new PiServerEventStream();\n\n\tconst partial: AssistantMessage = {\n\t\trole: \"assistant\",\n\t\tstopReason: \"stop\",\n\t\tcontent: [],\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\ttimestamp: Date.now(),\n\t};\n\n\t(async () => {\n\t\ttry {\n\t\t\tconst request = createPiServerRequest(options?.signal);\n\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\tconst tree = options?.sessionTree ?? {\n\t\t\t\t...getLinearTreeFromMessages(context.messages as Message[]),\n\t\t\t\treplace: true,\n\t\t\t};\n\t\t\tconst syncTree = tree;\n\t\t\tawait syncPiServerTreeWithRequest(sessionId, context, syncTree, request, options?.onHistoryReconciled);\n\n\t\t\tconst makeBody = () => ({\n\t\t\t\tsessionId,\n\t\t\t\tmodel,\n\t\t\t\toptions: serializeOptions(options),\n\t\t\t});\n\t\t\tlet response = await request.postJson(\"/api/stream\", makeBody());\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet failure = await readPiServerFailure(response);\n\t\t\t\tif (!options?.signal?.aborted && isRecoverableMissingServerState(response, failure.matchText)) {\n\t\t\t\t\tresetSessionTracking(sessionId);\n\t\t\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\t\t\tawait syncPiServerTreeWithRequest(sessionId, context, syncTree, request, options?.onHistoryReconciled);\n\t\t\t\t\tresponse = await request.postJson(\"/api/stream\", makeBody());\n\t\t\t\t\tif (response.ok) {\n\t\t\t\t\t\tfailure = { details: \"\", matchText: \"\" };\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfailure = await readPiServerFailure(response);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`pi-server error: ${failure.details}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait ensurePiServerEventStream(response);\n\t\t\tconst reader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (options?.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (!data) continue;\n\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options?.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tif (isEphemeralSession) {\n\t\t\t\tresetSessionTracking(sessionId);\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tconst reason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.stopReason = reason;\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason,\n\t\t\t\terror: partial,\n\t\t\t});\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\nfunction processProxyEvent(\n\tproxyEvent: ProxyAssistantMessageEvent,\n\tpartial: AssistantMessage,\n): AssistantMessageEvent | undefined {\n\tswitch (proxyEvent.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\", partial };\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"text\", text: \"\" };\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\treturn { type: \"text_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn { type: \"text_end\", contentIndex: proxyEvent.contentIndex, content: content.text, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"thinking\", thinking: \"\" };\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\treturn { type: \"thinking_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t}\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn { type: \"thinking_end\", contentIndex: proxyEvent.contentIndex, content: content.thinking, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\t\tcase \"toolcall_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: proxyEvent.id,\n\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\targuments: {},\n\t\t\t\tpartialJson: \"\",\n\t\t\t} as any;\n\t\t\treturn { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"toolcall_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content };\n\t\t\t\treturn { type: \"toolcall_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t}\n\t\tcase \"toolcall_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\treturn { type: \"toolcall_end\", contentIndex: proxyEvent.contentIndex, toolCall: content, partial };\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\t\tcase \"done\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"done\", reason: proxyEvent.reason, message: partial };\n\t\tcase \"error\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"error\", reason: proxyEvent.reason, error: partial };\n\t\tdefault: {\n\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\tconsole.warn(`Unhandled proxy event type: ${(proxyEvent as any).type}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\nexport { getServerUrl, getAuthToken };\n"]}
1
+ {"version":3,"file":"pi-server-client.d.ts","sourceRoot":"","sources":["../../src/core/pi-server-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport type {\n\tCompactionPreparationOptions,\n\tCompactResult,\n\tProxyAssistantMessageEvent,\n\tSessionTreeEntry,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tEventStream,\n\ttype Message,\n\ttype Model,\n\tparseStreamingJson,\n\ttype SimpleStreamOptions,\n} from \"@earendil-works/pi-ai\";\nimport { ChunkRequest } from \"./pi-server-request.ts\";\n\nclass PiServerEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {\n\tconstructor() {\n\t\tsuper(\n\t\t\t(event) => event.type === \"done\" || event.type === \"error\",\n\t\t\t(event) => {\n\t\t\t\tif (event.type === \"done\") return event.message;\n\t\t\t\tif (event.type === \"error\") return event.error;\n\t\t\t\tthrow new Error(\"Unexpected event type\");\n\t\t\t},\n\t\t);\n\t}\n}\n\nfunction getServerUrl(): string {\n\treturn process.env.PI_SERVER_URL ?? \"http://127.0.0.1:4217\";\n}\n\nfunction getAuthToken(): string {\n\treturn process.env.PI_SERVER_AUTH_TOKEN ?? \"\";\n}\n\nfunction createPiServerRequest(signal?: AbortSignal): ChunkRequest {\n\treturn new ChunkRequest({\n\t\tserverUrl: getServerUrl(),\n\t\tauthToken: getAuthToken(),\n\t\tsignal,\n\t});\n}\n\nconst sessionStaticContextHashes = new Map<string, string>();\nconst sessionSyncedEntryIds = new Map<string, Set<string>>();\nconst sessionTreeHashes = new Map<string, string>();\nconst sessionTreeEntryCounts = new Map<string, number>();\nconst sessionTreeLeafIds = new Map<string, string | null>();\nconst sessionHasTemporaryTree = new Set<string>();\nconst RESPONSE_BODY_EXCERPT_CHARS = 500;\n\nexport function hashStaticContext(ctx: Context): string {\n\tconst parts: string[] = [];\n\tif (ctx.systemPrompt !== undefined) parts.push(`sp:${ctx.systemPrompt}`);\n\tif (ctx.tools) {\n\t\tparts.push(\n\t\t\t`t:${JSON.stringify(ctx.tools.map((t) => ({ name: t.name, description: t.description, parameters: t.parameters })))}`,\n\t\t);\n\t}\n\treturn parts.join(\"|\");\n}\n\nfunction hashEntries(entries: SessionTreeEntry[]): string {\n\treturn createHash(\"sha256\").update(JSON.stringify(entries)).digest(\"hex\");\n}\n\nfunction getLinearTreeFromMessages(messages: Message[]): { entries: SessionTreeEntry[]; leafId: string | null } {\n\tlet parentId: string | null = null;\n\tconst entries = messages.map((message, index): SessionTreeEntry => {\n\t\tconst id = `message-${index}`;\n\t\tconst entry: SessionTreeEntry = {\n\t\t\ttype: \"message\",\n\t\t\tid,\n\t\t\tparentId,\n\t\t\ttimestamp: new Date(message.timestamp).toISOString(),\n\t\t\tmessage,\n\t\t};\n\t\tparentId = id;\n\t\treturn entry;\n\t});\n\treturn { entries, leafId: parentId };\n}\n\nexport function resetSessionTracking(sessionId: string): void {\n\tsessionStaticContextHashes.delete(sessionId);\n\tsessionSyncedEntryIds.delete(sessionId);\n\tsessionTreeHashes.delete(sessionId);\n\tsessionTreeEntryCounts.delete(sessionId);\n\tsessionTreeLeafIds.delete(sessionId);\n\tsessionHasTemporaryTree.delete(sessionId);\n}\n\nexport function resetAllSessionTracking(): void {\n\tsessionStaticContextHashes.clear();\n\tsessionSyncedEntryIds.clear();\n\tsessionTreeHashes.clear();\n\tsessionTreeEntryCounts.clear();\n\tsessionTreeLeafIds.clear();\n\tsessionHasTemporaryTree.clear();\n}\n\ninterface SessionInitResponse {\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash?: string;\n\tmessageCount: number;\n\tentryCount?: number;\n\tleafId?: string | null;\n\trevision?: number;\n\tfromCache?: boolean;\n}\n\nexport interface PiServerTreeSnapshot {\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\treplace?: boolean;\n}\n\nexport interface PiServerCompactionResult {\n\tcompaction: CompactResult;\n\tcompactionEntry: SessionTreeEntry;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tmessages: Message[];\n}\n\nexport interface PiServerHistorySnapshot {\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tmessages: Message[];\n}\n\nexport interface PiServerStreamOptions extends SimpleStreamOptions {\n\tsessionTree?: PiServerTreeSnapshot;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\ninterface PiServerResponseFailure {\n\tdetails: string;\n\tmatchText: string;\n}\n\ninterface PiServerHistoryResponse extends Partial<PiServerHistorySnapshot> {\n\tsessionId: string;\n\ttreeHash?: string;\n\tentryCount?: number;\n\tleafId?: string | null;\n}\n\ninterface PiServerSyncOptions {\n\tsignal?: AbortSignal;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction getResponseStatus(response: Response): string {\n\tconst status = response.statusText ? `${response.status} ${response.statusText}` : String(response.status);\n\treturn response.status >= 500 ? `${status} (server error)` : status;\n}\n\nfunction getResponseContentType(response: Response): string {\n\treturn response.headers.get(\"content-type\") ?? \"unknown\";\n}\n\nfunction getBodyExcerpt(text: string): string {\n\tconst trimmed = text.trim();\n\tif (!trimmed) return \"<empty>\";\n\tconst excerpt = trimmed.slice(0, RESPONSE_BODY_EXCERPT_CHARS);\n\treturn excerpt.length < trimmed.length ? `${excerpt}...` : excerpt;\n}\n\nfunction getJsonErrorText(text: string): string | undefined {\n\tif (!text) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(text) as unknown;\n\t\tif (!isObject(parsed)) return undefined;\n\t\tconst error = parsed.error;\n\t\treturn typeof error === \"string\" && error.length > 0 ? error : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction formatResponseDetails(response: Response, bodyText: string): string {\n\tconst body = getJsonErrorText(bodyText) ?? getBodyExcerpt(bodyText);\n\treturn `${getResponseStatus(response)}; content-type: ${getResponseContentType(response)}; body excerpt: ${body}`;\n}\n\nasync function readPiServerFailure(response: Response): Promise<PiServerResponseFailure> {\n\tconst bodyText = await response.text();\n\treturn {\n\t\tdetails: formatResponseDetails(response, bodyText),\n\t\tmatchText: getJsonErrorText(bodyText) ?? bodyText,\n\t};\n}\n\nasync function readPiServerJson<T>(response: Response, errorPrefix: string): Promise<T> {\n\tconst bodyText = await response.text();\n\tif (!response.ok) {\n\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)})`);\n\t}\n\ttry {\n\t\treturn JSON.parse(bodyText) as T;\n\t} catch {\n\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)}; expected JSON)`);\n\t}\n}\n\nasync function ensurePiServerEventStream(response: Response): Promise<void> {\n\tconst contentType = getResponseContentType(response);\n\tif (contentType.toLowerCase().split(\";\")[0]?.trim() === \"text/event-stream\") {\n\t\treturn;\n\t}\n\tconst bodyText = await response.text();\n\tthrow new Error(`pi-server error: ${formatResponseDetails(response, bodyText)}; expected text/event-stream`);\n}\n\nasync function ensureSessionInit(\n\tsessionId: string,\n\tcontext: Context,\n\trequest: ChunkRequest,\n): Promise<SessionInitResponse> {\n\tconst currentHash = hashStaticContext(context);\n\tconst previousHash = sessionStaticContextHashes.get(sessionId);\n\n\tif (previousHash === currentHash) {\n\t\treturn {\n\t\t\tsessionId,\n\t\t\tstaticContextHash: previousHash,\n\t\t\tmessageCount: 0,\n\t\t\tfromCache: true,\n\t\t};\n\t}\n\n\tconst staticContext = {\n\t\tsystemPrompt: context.systemPrompt,\n\t\ttools: context.tools,\n\t};\n\n\tconst endpoint = previousHash === undefined ? \"/api/session/init\" : \"/api/session/update\";\n\n\tconst response = await request.postJson(endpoint, { sessionId, staticContext });\n\n\tconst result = await readPiServerJson<SessionInitResponse>(response, \"Session init failed\");\n\tsessionStaticContextHashes.set(sessionId, currentHash);\n\tif (result.treeHash !== undefined) {\n\t\tsessionTreeHashes.set(sessionId, result.treeHash);\n\t\tsessionTreeLeafIds.set(sessionId, result.leafId ?? null);\n\t\tif (result.entryCount !== undefined) {\n\t\t\tsessionTreeEntryCounts.set(sessionId, result.entryCount);\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction getStaticContext(context: Context) {\n\treturn {\n\t\tsystemPrompt: context.systemPrompt,\n\t\ttools: context.tools,\n\t};\n}\n\nfunction markTreeSynced(sessionId: string, tree: PiServerTreeSnapshot): void {\n\tsessionSyncedEntryIds.set(sessionId, new Set(tree.entries.map((entry) => entry.id)));\n\tsessionTreeHashes.set(sessionId, hashEntries(tree.entries));\n\tsessionTreeEntryCounts.set(sessionId, tree.entries.length);\n\tsessionTreeLeafIds.set(sessionId, tree.leafId);\n\tif (tree.replace) {\n\t\tsessionHasTemporaryTree.add(sessionId);\n\t} else {\n\t\tsessionHasTemporaryTree.delete(sessionId);\n\t}\n}\n\nasync function postTreeJson(\n\trequest: ChunkRequest,\n\tendpoint: string,\n\tbody: unknown,\n\terrorPrefix: string,\n): Promise<void> {\n\tconst response = await request.postJson(endpoint, body);\n\tawait readPiServerJson<unknown>(response, errorPrefix);\n}\n\nasync function fetchPiServerHistory(\n\tsessionId: string,\n\trequest: ChunkRequest,\n): Promise<PiServerHistorySnapshot | undefined> {\n\tconst response = await request.getJson(`/api/session/${encodeURIComponent(sessionId)}/history`);\n\tif (response.status === 404) {\n\t\treturn undefined;\n\t}\n\tconst history = await readPiServerJson<PiServerHistoryResponse>(response, \"Session history reconciliation failed\");\n\tif (!history.entries || history.leafId === undefined || !history.messages) {\n\t\tthrow new Error(\"Session history reconciliation failed (response did not include the session tree)\");\n\t}\n\treturn {\n\t\tentries: history.entries,\n\t\tleafId: history.leafId,\n\t\tmessages: history.messages,\n\t};\n}\n\nasync function applyPiServerHistory(\n\tsessionId: string,\n\tsnapshot: PiServerHistorySnapshot,\n\tonHistoryReconciled: ((snapshot: PiServerHistorySnapshot) => void | Promise<void>) | undefined,\n): Promise<void> {\n\tmarkTreeSynced(sessionId, snapshot);\n\tawait onHistoryReconciled?.(snapshot);\n}\n\nfunction getKnownServerPrefixIds(sessionId: string, entries: SessionTreeEntry[]): Set<string> | undefined {\n\tconst entryCount = sessionTreeEntryCounts.get(sessionId);\n\tconst treeHash = sessionTreeHashes.get(sessionId);\n\tif (entryCount === undefined || treeHash === undefined || entryCount > entries.length) {\n\t\treturn undefined;\n\t}\n\tconst prefix = entries.slice(0, entryCount);\n\tif (hashEntries(prefix) !== treeHash) {\n\t\treturn undefined;\n\t}\n\tconst ids = new Set(prefix.map((entry) => entry.id));\n\tsessionSyncedEntryIds.set(sessionId, ids);\n\treturn ids;\n}\n\nfunction isRecoverableTreeDivergenceError(error: unknown): boolean {\n\tconst message = error instanceof Error ? error.message : String(error);\n\treturn /parent entry .* does not exist|leafId .* does not exist|entry .* already exists/i.test(message);\n}\n\nfunction isRecoverableMissingServerState(response: Response, errorBody: string): boolean {\n\tif (response.status !== 400 && response.status !== 404) return false;\n\treturn /Session has no static context|session not found|parent entry .* does not exist|leafId .* does not exist|entry .* already exists/i.test(\n\t\terrorBody,\n\t);\n}\n\nasync function syncFullPiServerTree(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n): Promise<void> {\n\tawait postTreeJson(\n\t\trequest,\n\t\t\"/api/session/tree/sync\",\n\t\t{\n\t\t\tsessionId,\n\t\t\tentries: tree.entries,\n\t\t\tleafId: tree.leafId,\n\t\t\tstaticContext: getStaticContext(context),\n\t\t},\n\t\t\"Session tree sync failed\",\n\t);\n\tmarkTreeSynced(sessionId, tree);\n}\n\nfunction shouldUseServerHistory(sessionId: string, tree: PiServerTreeSnapshot): boolean {\n\treturn !tree.replace && !sessionHasTemporaryTree.has(sessionId) && (sessionTreeEntryCounts.get(sessionId) ?? 0) > 0;\n}\n\nasync function recoverPiServerTreeDivergence(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>,\n): Promise<void> {\n\tif (shouldUseServerHistory(sessionId, tree)) {\n\t\tconst snapshot = await fetchPiServerHistory(sessionId, request);\n\t\tif (snapshot && snapshot.entries.length > 0) {\n\t\t\tawait applyPiServerHistory(sessionId, snapshot, onHistoryReconciled);\n\t\t\tthrow new Error(\n\t\t\t\t\"pi-server history differed from local history; local session was reconciled to server history\",\n\t\t\t);\n\t\t}\n\t}\n\n\tawait syncFullPiServerTree(sessionId, context, tree, request);\n}\n\nexport async function syncPiServerTree(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\toptions?: PiServerSyncOptions,\n): Promise<void> {\n\tconst request = createPiServerRequest(options?.signal);\n\tawait ensureSessionInit(sessionId, context, request);\n\tawait syncPiServerTreeWithRequest(sessionId, context, tree, request, options?.onHistoryReconciled);\n}\n\nasync function syncPiServerTreeWithRequest(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>,\n): Promise<void> {\n\tconst syncTree = tree;\n\tconst currentHash = hashEntries(syncTree.entries);\n\tconst previousHash = sessionTreeHashes.get(sessionId);\n\tconst previousLeafId = sessionTreeLeafIds.get(sessionId);\n\n\tif (!tree.replace && previousHash === currentHash) {\n\t\tif (previousLeafId !== syncTree.leafId) {\n\t\t\ttry {\n\t\t\t\tawait postTreeJson(\n\t\t\t\t\trequest,\n\t\t\t\t\t\"/api/session/tree/switch\",\n\t\t\t\t\t{ sessionId, leafId: syncTree.leafId },\n\t\t\t\t\t\"Session tree switch failed\",\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (!isRecoverableTreeDivergenceError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tawait recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsessionTreeLeafIds.set(sessionId, syncTree.leafId);\n\t\t}\n\t\tmarkTreeSynced(sessionId, syncTree);\n\t\treturn;\n\t}\n\n\tconst syncedIds =\n\t\t!tree.replace && !sessionHasTemporaryTree.has(sessionId)\n\t\t\t? (sessionSyncedEntryIds.get(sessionId) ?? getKnownServerPrefixIds(sessionId, syncTree.entries))\n\t\t\t: undefined;\n\tif (syncedIds) {\n\t\tconst deltaEntries = syncTree.entries.filter((entry) => !syncedIds.has(entry.id));\n\t\tif (deltaEntries.length > 0) {\n\t\t\ttry {\n\t\t\t\tawait postTreeJson(\n\t\t\t\t\trequest,\n\t\t\t\t\t\"/api/session/tree/append\",\n\t\t\t\t\t{\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\tentries: deltaEntries,\n\t\t\t\t\t\tleafId: syncTree.leafId,\n\t\t\t\t\t\tstaticContext: getStaticContext(context),\n\t\t\t\t\t},\n\t\t\t\t\t\"Session tree append failed\",\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (!isRecoverableTreeDivergenceError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tawait recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmarkTreeSynced(sessionId, syncTree);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tawait recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n}\n\nfunction serializeOptions(options: SimpleStreamOptions | undefined): SimpleStreamOptions {\n\treturn {\n\t\ttemperature: options?.temperature,\n\t\tmaxTokens: options?.maxTokens,\n\t\treasoning: options?.reasoning,\n\t\tcacheRetention: options?.cacheRetention,\n\t\tsessionId: options?.sessionId,\n\t\tapiKey: options?.apiKey,\n\t\theaders: options?.headers,\n\t\tmetadata: options?.metadata,\n\t\ttransport: options?.transport,\n\t\tthinkingBudgets: options?.thinkingBudgets,\n\t\ttimeoutMs: options?.timeoutMs,\n\t\twebsocketConnectTimeoutMs: options?.websocketConnectTimeoutMs,\n\t\tmaxRetries: options?.maxRetries,\n\t\tmaxRetryDelayMs: options?.maxRetryDelayMs,\n\t};\n}\n\nexport interface PiServerCompactOptions extends SimpleStreamOptions {\n\tcustomInstructions?: string;\n\tsettings?: unknown;\n\tpreparation?: CompactionPreparationOptions;\n\tsessionTree?: PiServerTreeSnapshot;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\nexport async function compactPiServer(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions?: PiServerCompactOptions,\n): Promise<PiServerCompactionResult> {\n\tconst sessionId = options?.sessionId ?? \"default\";\n\tconst request = createPiServerRequest(options?.signal);\n\n\tawait ensureSessionInit(sessionId, context, request);\n\tconst tree = options?.sessionTree ?? getLinearTreeFromMessages(context.messages as Message[]);\n\tawait syncPiServerTreeWithRequest(sessionId, context, tree, request, options?.onHistoryReconciled);\n\n\tconst makeBody = () => ({\n\t\tsessionId,\n\t\tmodel,\n\t\toptions: serializeOptions(options),\n\t\tsettings: options?.settings,\n\t\tpreparation: options?.preparation,\n\t\tcustomInstructions: options?.customInstructions,\n\t});\n\tlet response = await request.postJson(\"/api/session/compact\", makeBody());\n\tif (!response.ok) {\n\t\tlet failure = await readPiServerFailure(response);\n\t\tif (!options?.signal?.aborted && isRecoverableMissingServerState(response, failure.matchText)) {\n\t\t\tresetSessionTracking(sessionId);\n\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\tawait syncPiServerTreeWithRequest(sessionId, context, tree, request, options?.onHistoryReconciled);\n\t\t\tresponse = await request.postJson(\"/api/session/compact\", makeBody());\n\t\t\tif (response.ok) {\n\t\t\t\tfailure = { details: \"\", matchText: \"\" };\n\t\t\t} else {\n\t\t\t\tfailure = await readPiServerFailure(response);\n\t\t\t}\n\t\t}\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Server compaction failed (${failure.details})`);\n\t\t}\n\t}\n\tconst result = await readPiServerJson<Partial<PiServerCompactionResult>>(response, \"Server compaction failed\");\n\tif (!result.compaction) {\n\t\tthrow new Error(\"Server compaction response did not include a compaction result\");\n\t}\n\tif (!result.compactionEntry || !result.entries || result.leafId === undefined || !result.messages) {\n\t\tthrow new Error(\"Server compaction response did not include the updated session tree\");\n\t}\n\tmarkTreeSynced(sessionId, { entries: result.entries, leafId: result.leafId });\n\treturn {\n\t\tcompaction: result.compaction,\n\t\tcompactionEntry: result.compactionEntry,\n\t\tentries: result.entries,\n\t\tleafId: result.leafId,\n\t\tmessages: result.messages,\n\t};\n}\n\nexport async function dropLastPiServerAssistantError(sessionId: string): Promise<void> {\n\tconst request = createPiServerRequest();\n\tconst response = await request.postJson(\"/api/session/drop-last-assistant-error\", { sessionId });\n\tawait readPiServerJson<unknown>(response, \"Dropping server assistant error failed\");\n}\n\nexport async function streamPiServer(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions?: PiServerStreamOptions,\n): Promise<PiServerEventStream> {\n\tconst sessionId = options?.sessionId ?? randomUUID();\n\tconst isEphemeralSession = options?.sessionId === undefined;\n\tconst stream = new PiServerEventStream();\n\n\tconst partial: AssistantMessage = {\n\t\trole: \"assistant\",\n\t\tstopReason: \"stop\",\n\t\tcontent: [],\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\ttimestamp: Date.now(),\n\t};\n\n\t(async () => {\n\t\ttry {\n\t\t\tconst request = createPiServerRequest(options?.signal);\n\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\tconst tree = options?.sessionTree ?? {\n\t\t\t\t...getLinearTreeFromMessages(context.messages as Message[]),\n\t\t\t\treplace: true,\n\t\t\t};\n\t\t\tconst syncTree = tree;\n\t\t\tawait syncPiServerTreeWithRequest(sessionId, context, syncTree, request, options?.onHistoryReconciled);\n\n\t\t\tconst makeBody = () => ({\n\t\t\t\tsessionId,\n\t\t\t\tmodel,\n\t\t\t\toptions: serializeOptions(options),\n\t\t\t});\n\t\t\tlet response = await request.postJson(\"/api/stream\", makeBody());\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet failure = await readPiServerFailure(response);\n\t\t\t\tif (!options?.signal?.aborted && isRecoverableMissingServerState(response, failure.matchText)) {\n\t\t\t\t\tresetSessionTracking(sessionId);\n\t\t\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\t\t\tawait syncPiServerTreeWithRequest(sessionId, context, syncTree, request, options?.onHistoryReconciled);\n\t\t\t\t\tresponse = await request.postJson(\"/api/stream\", makeBody());\n\t\t\t\t\tif (response.ok) {\n\t\t\t\t\t\tfailure = { details: \"\", matchText: \"\" };\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfailure = await readPiServerFailure(response);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`pi-server error: ${failure.details}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait ensurePiServerEventStream(response);\n\t\t\tconst reader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (options?.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (!data) continue;\n\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options?.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tif (isEphemeralSession) {\n\t\t\t\tresetSessionTracking(sessionId);\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tconst reason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.stopReason = reason;\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason,\n\t\t\t\terror: partial,\n\t\t\t});\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\nfunction processProxyEvent(\n\tproxyEvent: ProxyAssistantMessageEvent,\n\tpartial: AssistantMessage,\n): AssistantMessageEvent | undefined {\n\tswitch (proxyEvent.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\", partial };\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"text\", text: \"\" };\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\treturn { type: \"text_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn { type: \"text_end\", contentIndex: proxyEvent.contentIndex, content: content.text, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"thinking\", thinking: \"\" };\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\treturn { type: \"thinking_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t}\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn { type: \"thinking_end\", contentIndex: proxyEvent.contentIndex, content: content.thinking, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\t\tcase \"toolcall_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: proxyEvent.id,\n\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\targuments: {},\n\t\t\t\tpartialJson: \"\",\n\t\t\t} as any;\n\t\t\treturn { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"toolcall_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content };\n\t\t\t\treturn { type: \"toolcall_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t}\n\t\tcase \"toolcall_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\treturn { type: \"toolcall_end\", contentIndex: proxyEvent.contentIndex, toolCall: content, partial };\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\t\tcase \"done\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"done\", reason: proxyEvent.reason, message: partial };\n\t\tcase \"error\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"error\", reason: proxyEvent.reason, error: partial };\n\t\tdefault: {\n\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\tconsole.warn(`Unhandled proxy event type: ${(proxyEvent as any).type}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\nexport { getServerUrl, getAuthToken };\n"]}
@@ -335,6 +335,7 @@ export async function compactPiServer(model, context, options) {
335
335
  model,
336
336
  options: serializeOptions(options),
337
337
  settings: options?.settings,
338
+ preparation: options?.preparation,
338
339
  customInstructions: options?.customInstructions,
339
340
  });
340
341
  let response = await request.postJson("/api/session/compact", makeBody());
@@ -1 +1 @@
1
- {"version":3,"file":"pi-server-client.js","sourceRoot":"","sources":["../../src/core/pi-server-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport type { CompactResult, ProxyAssistantMessageEvent, SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tEventStream,\n\ttype Message,\n\ttype Model,\n\tparseStreamingJson,\n\ttype SimpleStreamOptions,\n} from \"@earendil-works/pi-ai\";\nimport { ChunkRequest } from \"./pi-server-request.ts\";\n\nclass PiServerEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {\n\tconstructor() {\n\t\tsuper(\n\t\t\t(event) => event.type === \"done\" || event.type === \"error\",\n\t\t\t(event) => {\n\t\t\t\tif (event.type === \"done\") return event.message;\n\t\t\t\tif (event.type === \"error\") return event.error;\n\t\t\t\tthrow new Error(\"Unexpected event type\");\n\t\t\t},\n\t\t);\n\t}\n}\n\nfunction getServerUrl(): string {\n\treturn process.env.PI_SERVER_URL ?? \"http://127.0.0.1:4217\";\n}\n\nfunction getAuthToken(): string {\n\treturn process.env.PI_SERVER_AUTH_TOKEN ?? \"\";\n}\n\nfunction createPiServerRequest(signal?: AbortSignal): ChunkRequest {\n\treturn new ChunkRequest({\n\t\tserverUrl: getServerUrl(),\n\t\tauthToken: getAuthToken(),\n\t\tsignal,\n\t});\n}\n\nconst sessionStaticContextHashes = new Map<string, string>();\nconst sessionSyncedEntryIds = new Map<string, Set<string>>();\nconst sessionTreeHashes = new Map<string, string>();\nconst sessionTreeEntryCounts = new Map<string, number>();\nconst sessionTreeLeafIds = new Map<string, string | null>();\nconst sessionHasTemporaryTree = new Set<string>();\nconst RESPONSE_BODY_EXCERPT_CHARS = 500;\n\nexport function hashStaticContext(ctx: Context): string {\n\tconst parts: string[] = [];\n\tif (ctx.systemPrompt !== undefined) parts.push(`sp:${ctx.systemPrompt}`);\n\tif (ctx.tools) {\n\t\tparts.push(\n\t\t\t`t:${JSON.stringify(ctx.tools.map((t) => ({ name: t.name, description: t.description, parameters: t.parameters })))}`,\n\t\t);\n\t}\n\treturn parts.join(\"|\");\n}\n\nfunction hashEntries(entries: SessionTreeEntry[]): string {\n\treturn createHash(\"sha256\").update(JSON.stringify(entries)).digest(\"hex\");\n}\n\nfunction getLinearTreeFromMessages(messages: Message[]): { entries: SessionTreeEntry[]; leafId: string | null } {\n\tlet parentId: string | null = null;\n\tconst entries = messages.map((message, index): SessionTreeEntry => {\n\t\tconst id = `message-${index}`;\n\t\tconst entry: SessionTreeEntry = {\n\t\t\ttype: \"message\",\n\t\t\tid,\n\t\t\tparentId,\n\t\t\ttimestamp: new Date(message.timestamp).toISOString(),\n\t\t\tmessage,\n\t\t};\n\t\tparentId = id;\n\t\treturn entry;\n\t});\n\treturn { entries, leafId: parentId };\n}\n\nexport function resetSessionTracking(sessionId: string): void {\n\tsessionStaticContextHashes.delete(sessionId);\n\tsessionSyncedEntryIds.delete(sessionId);\n\tsessionTreeHashes.delete(sessionId);\n\tsessionTreeEntryCounts.delete(sessionId);\n\tsessionTreeLeafIds.delete(sessionId);\n\tsessionHasTemporaryTree.delete(sessionId);\n}\n\nexport function resetAllSessionTracking(): void {\n\tsessionStaticContextHashes.clear();\n\tsessionSyncedEntryIds.clear();\n\tsessionTreeHashes.clear();\n\tsessionTreeEntryCounts.clear();\n\tsessionTreeLeafIds.clear();\n\tsessionHasTemporaryTree.clear();\n}\n\ninterface SessionInitResponse {\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash?: string;\n\tmessageCount: number;\n\tentryCount?: number;\n\tleafId?: string | null;\n\trevision?: number;\n\tfromCache?: boolean;\n}\n\nexport interface PiServerTreeSnapshot {\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\treplace?: boolean;\n}\n\nexport interface PiServerCompactionResult {\n\tcompaction: CompactResult;\n\tcompactionEntry: SessionTreeEntry;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tmessages: Message[];\n}\n\nexport interface PiServerHistorySnapshot {\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tmessages: Message[];\n}\n\nexport interface PiServerStreamOptions extends SimpleStreamOptions {\n\tsessionTree?: PiServerTreeSnapshot;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\ninterface PiServerResponseFailure {\n\tdetails: string;\n\tmatchText: string;\n}\n\ninterface PiServerHistoryResponse extends Partial<PiServerHistorySnapshot> {\n\tsessionId: string;\n\ttreeHash?: string;\n\tentryCount?: number;\n\tleafId?: string | null;\n}\n\ninterface PiServerSyncOptions {\n\tsignal?: AbortSignal;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction getResponseStatus(response: Response): string {\n\tconst status = response.statusText ? `${response.status} ${response.statusText}` : String(response.status);\n\treturn response.status >= 500 ? `${status} (server error)` : status;\n}\n\nfunction getResponseContentType(response: Response): string {\n\treturn response.headers.get(\"content-type\") ?? \"unknown\";\n}\n\nfunction getBodyExcerpt(text: string): string {\n\tconst trimmed = text.trim();\n\tif (!trimmed) return \"<empty>\";\n\tconst excerpt = trimmed.slice(0, RESPONSE_BODY_EXCERPT_CHARS);\n\treturn excerpt.length < trimmed.length ? `${excerpt}...` : excerpt;\n}\n\nfunction getJsonErrorText(text: string): string | undefined {\n\tif (!text) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(text) as unknown;\n\t\tif (!isObject(parsed)) return undefined;\n\t\tconst error = parsed.error;\n\t\treturn typeof error === \"string\" && error.length > 0 ? error : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction formatResponseDetails(response: Response, bodyText: string): string {\n\tconst body = getJsonErrorText(bodyText) ?? getBodyExcerpt(bodyText);\n\treturn `${getResponseStatus(response)}; content-type: ${getResponseContentType(response)}; body excerpt: ${body}`;\n}\n\nasync function readPiServerFailure(response: Response): Promise<PiServerResponseFailure> {\n\tconst bodyText = await response.text();\n\treturn {\n\t\tdetails: formatResponseDetails(response, bodyText),\n\t\tmatchText: getJsonErrorText(bodyText) ?? bodyText,\n\t};\n}\n\nasync function readPiServerJson<T>(response: Response, errorPrefix: string): Promise<T> {\n\tconst bodyText = await response.text();\n\tif (!response.ok) {\n\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)})`);\n\t}\n\ttry {\n\t\treturn JSON.parse(bodyText) as T;\n\t} catch {\n\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)}; expected JSON)`);\n\t}\n}\n\nasync function ensurePiServerEventStream(response: Response): Promise<void> {\n\tconst contentType = getResponseContentType(response);\n\tif (contentType.toLowerCase().split(\";\")[0]?.trim() === \"text/event-stream\") {\n\t\treturn;\n\t}\n\tconst bodyText = await response.text();\n\tthrow new Error(`pi-server error: ${formatResponseDetails(response, bodyText)}; expected text/event-stream`);\n}\n\nasync function ensureSessionInit(\n\tsessionId: string,\n\tcontext: Context,\n\trequest: ChunkRequest,\n): Promise<SessionInitResponse> {\n\tconst currentHash = hashStaticContext(context);\n\tconst previousHash = sessionStaticContextHashes.get(sessionId);\n\n\tif (previousHash === currentHash) {\n\t\treturn {\n\t\t\tsessionId,\n\t\t\tstaticContextHash: previousHash,\n\t\t\tmessageCount: 0,\n\t\t\tfromCache: true,\n\t\t};\n\t}\n\n\tconst staticContext = {\n\t\tsystemPrompt: context.systemPrompt,\n\t\ttools: context.tools,\n\t};\n\n\tconst endpoint = previousHash === undefined ? \"/api/session/init\" : \"/api/session/update\";\n\n\tconst response = await request.postJson(endpoint, { sessionId, staticContext });\n\n\tconst result = await readPiServerJson<SessionInitResponse>(response, \"Session init failed\");\n\tsessionStaticContextHashes.set(sessionId, currentHash);\n\tif (result.treeHash !== undefined) {\n\t\tsessionTreeHashes.set(sessionId, result.treeHash);\n\t\tsessionTreeLeafIds.set(sessionId, result.leafId ?? null);\n\t\tif (result.entryCount !== undefined) {\n\t\t\tsessionTreeEntryCounts.set(sessionId, result.entryCount);\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction getStaticContext(context: Context) {\n\treturn {\n\t\tsystemPrompt: context.systemPrompt,\n\t\ttools: context.tools,\n\t};\n}\n\nfunction markTreeSynced(sessionId: string, tree: PiServerTreeSnapshot): void {\n\tsessionSyncedEntryIds.set(sessionId, new Set(tree.entries.map((entry) => entry.id)));\n\tsessionTreeHashes.set(sessionId, hashEntries(tree.entries));\n\tsessionTreeEntryCounts.set(sessionId, tree.entries.length);\n\tsessionTreeLeafIds.set(sessionId, tree.leafId);\n\tif (tree.replace) {\n\t\tsessionHasTemporaryTree.add(sessionId);\n\t} else {\n\t\tsessionHasTemporaryTree.delete(sessionId);\n\t}\n}\n\nasync function postTreeJson(\n\trequest: ChunkRequest,\n\tendpoint: string,\n\tbody: unknown,\n\terrorPrefix: string,\n): Promise<void> {\n\tconst response = await request.postJson(endpoint, body);\n\tawait readPiServerJson<unknown>(response, errorPrefix);\n}\n\nasync function fetchPiServerHistory(\n\tsessionId: string,\n\trequest: ChunkRequest,\n): Promise<PiServerHistorySnapshot | undefined> {\n\tconst response = await request.getJson(`/api/session/${encodeURIComponent(sessionId)}/history`);\n\tif (response.status === 404) {\n\t\treturn undefined;\n\t}\n\tconst history = await readPiServerJson<PiServerHistoryResponse>(response, \"Session history reconciliation failed\");\n\tif (!history.entries || history.leafId === undefined || !history.messages) {\n\t\tthrow new Error(\"Session history reconciliation failed (response did not include the session tree)\");\n\t}\n\treturn {\n\t\tentries: history.entries,\n\t\tleafId: history.leafId,\n\t\tmessages: history.messages,\n\t};\n}\n\nasync function applyPiServerHistory(\n\tsessionId: string,\n\tsnapshot: PiServerHistorySnapshot,\n\tonHistoryReconciled: ((snapshot: PiServerHistorySnapshot) => void | Promise<void>) | undefined,\n): Promise<void> {\n\tmarkTreeSynced(sessionId, snapshot);\n\tawait onHistoryReconciled?.(snapshot);\n}\n\nfunction getKnownServerPrefixIds(sessionId: string, entries: SessionTreeEntry[]): Set<string> | undefined {\n\tconst entryCount = sessionTreeEntryCounts.get(sessionId);\n\tconst treeHash = sessionTreeHashes.get(sessionId);\n\tif (entryCount === undefined || treeHash === undefined || entryCount > entries.length) {\n\t\treturn undefined;\n\t}\n\tconst prefix = entries.slice(0, entryCount);\n\tif (hashEntries(prefix) !== treeHash) {\n\t\treturn undefined;\n\t}\n\tconst ids = new Set(prefix.map((entry) => entry.id));\n\tsessionSyncedEntryIds.set(sessionId, ids);\n\treturn ids;\n}\n\nfunction isRecoverableTreeDivergenceError(error: unknown): boolean {\n\tconst message = error instanceof Error ? error.message : String(error);\n\treturn /parent entry .* does not exist|leafId .* does not exist|entry .* already exists/i.test(message);\n}\n\nfunction isRecoverableMissingServerState(response: Response, errorBody: string): boolean {\n\tif (response.status !== 400 && response.status !== 404) return false;\n\treturn /Session has no static context|session not found|parent entry .* does not exist|leafId .* does not exist|entry .* already exists/i.test(\n\t\terrorBody,\n\t);\n}\n\nasync function syncFullPiServerTree(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n): Promise<void> {\n\tawait postTreeJson(\n\t\trequest,\n\t\t\"/api/session/tree/sync\",\n\t\t{\n\t\t\tsessionId,\n\t\t\tentries: tree.entries,\n\t\t\tleafId: tree.leafId,\n\t\t\tstaticContext: getStaticContext(context),\n\t\t},\n\t\t\"Session tree sync failed\",\n\t);\n\tmarkTreeSynced(sessionId, tree);\n}\n\nfunction shouldUseServerHistory(sessionId: string, tree: PiServerTreeSnapshot): boolean {\n\treturn !tree.replace && !sessionHasTemporaryTree.has(sessionId) && (sessionTreeEntryCounts.get(sessionId) ?? 0) > 0;\n}\n\nasync function recoverPiServerTreeDivergence(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>,\n): Promise<void> {\n\tif (shouldUseServerHistory(sessionId, tree)) {\n\t\tconst snapshot = await fetchPiServerHistory(sessionId, request);\n\t\tif (snapshot && snapshot.entries.length > 0) {\n\t\t\tawait applyPiServerHistory(sessionId, snapshot, onHistoryReconciled);\n\t\t\tthrow new Error(\n\t\t\t\t\"pi-server history differed from local history; local session was reconciled to server history\",\n\t\t\t);\n\t\t}\n\t}\n\n\tawait syncFullPiServerTree(sessionId, context, tree, request);\n}\n\nexport async function syncPiServerTree(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\toptions?: PiServerSyncOptions,\n): Promise<void> {\n\tconst request = createPiServerRequest(options?.signal);\n\tawait ensureSessionInit(sessionId, context, request);\n\tawait syncPiServerTreeWithRequest(sessionId, context, tree, request, options?.onHistoryReconciled);\n}\n\nasync function syncPiServerTreeWithRequest(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>,\n): Promise<void> {\n\tconst syncTree = tree;\n\tconst currentHash = hashEntries(syncTree.entries);\n\tconst previousHash = sessionTreeHashes.get(sessionId);\n\tconst previousLeafId = sessionTreeLeafIds.get(sessionId);\n\n\tif (!tree.replace && previousHash === currentHash) {\n\t\tif (previousLeafId !== syncTree.leafId) {\n\t\t\ttry {\n\t\t\t\tawait postTreeJson(\n\t\t\t\t\trequest,\n\t\t\t\t\t\"/api/session/tree/switch\",\n\t\t\t\t\t{ sessionId, leafId: syncTree.leafId },\n\t\t\t\t\t\"Session tree switch failed\",\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (!isRecoverableTreeDivergenceError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tawait recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsessionTreeLeafIds.set(sessionId, syncTree.leafId);\n\t\t}\n\t\tmarkTreeSynced(sessionId, syncTree);\n\t\treturn;\n\t}\n\n\tconst syncedIds =\n\t\t!tree.replace && !sessionHasTemporaryTree.has(sessionId)\n\t\t\t? (sessionSyncedEntryIds.get(sessionId) ?? getKnownServerPrefixIds(sessionId, syncTree.entries))\n\t\t\t: undefined;\n\tif (syncedIds) {\n\t\tconst deltaEntries = syncTree.entries.filter((entry) => !syncedIds.has(entry.id));\n\t\tif (deltaEntries.length > 0) {\n\t\t\ttry {\n\t\t\t\tawait postTreeJson(\n\t\t\t\t\trequest,\n\t\t\t\t\t\"/api/session/tree/append\",\n\t\t\t\t\t{\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\tentries: deltaEntries,\n\t\t\t\t\t\tleafId: syncTree.leafId,\n\t\t\t\t\t\tstaticContext: getStaticContext(context),\n\t\t\t\t\t},\n\t\t\t\t\t\"Session tree append failed\",\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (!isRecoverableTreeDivergenceError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tawait recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmarkTreeSynced(sessionId, syncTree);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tawait recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n}\n\nfunction serializeOptions(options: SimpleStreamOptions | undefined): SimpleStreamOptions {\n\treturn {\n\t\ttemperature: options?.temperature,\n\t\tmaxTokens: options?.maxTokens,\n\t\treasoning: options?.reasoning,\n\t\tcacheRetention: options?.cacheRetention,\n\t\tsessionId: options?.sessionId,\n\t\tapiKey: options?.apiKey,\n\t\theaders: options?.headers,\n\t\tmetadata: options?.metadata,\n\t\ttransport: options?.transport,\n\t\tthinkingBudgets: options?.thinkingBudgets,\n\t\ttimeoutMs: options?.timeoutMs,\n\t\twebsocketConnectTimeoutMs: options?.websocketConnectTimeoutMs,\n\t\tmaxRetries: options?.maxRetries,\n\t\tmaxRetryDelayMs: options?.maxRetryDelayMs,\n\t};\n}\n\nexport interface PiServerCompactOptions extends SimpleStreamOptions {\n\tcustomInstructions?: string;\n\tsettings?: unknown;\n\tsessionTree?: PiServerTreeSnapshot;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\nexport async function compactPiServer(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions?: PiServerCompactOptions,\n): Promise<PiServerCompactionResult> {\n\tconst sessionId = options?.sessionId ?? \"default\";\n\tconst request = createPiServerRequest(options?.signal);\n\n\tawait ensureSessionInit(sessionId, context, request);\n\tconst tree = options?.sessionTree ?? getLinearTreeFromMessages(context.messages as Message[]);\n\tawait syncPiServerTreeWithRequest(sessionId, context, tree, request, options?.onHistoryReconciled);\n\n\tconst makeBody = () => ({\n\t\tsessionId,\n\t\tmodel,\n\t\toptions: serializeOptions(options),\n\t\tsettings: options?.settings,\n\t\tcustomInstructions: options?.customInstructions,\n\t});\n\tlet response = await request.postJson(\"/api/session/compact\", makeBody());\n\tif (!response.ok) {\n\t\tlet failure = await readPiServerFailure(response);\n\t\tif (!options?.signal?.aborted && isRecoverableMissingServerState(response, failure.matchText)) {\n\t\t\tresetSessionTracking(sessionId);\n\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\tawait syncPiServerTreeWithRequest(sessionId, context, tree, request, options?.onHistoryReconciled);\n\t\t\tresponse = await request.postJson(\"/api/session/compact\", makeBody());\n\t\t\tif (response.ok) {\n\t\t\t\tfailure = { details: \"\", matchText: \"\" };\n\t\t\t} else {\n\t\t\t\tfailure = await readPiServerFailure(response);\n\t\t\t}\n\t\t}\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Server compaction failed (${failure.details})`);\n\t\t}\n\t}\n\tconst result = await readPiServerJson<Partial<PiServerCompactionResult>>(response, \"Server compaction failed\");\n\tif (!result.compaction) {\n\t\tthrow new Error(\"Server compaction response did not include a compaction result\");\n\t}\n\tif (!result.compactionEntry || !result.entries || result.leafId === undefined || !result.messages) {\n\t\tthrow new Error(\"Server compaction response did not include the updated session tree\");\n\t}\n\tmarkTreeSynced(sessionId, { entries: result.entries, leafId: result.leafId });\n\treturn {\n\t\tcompaction: result.compaction,\n\t\tcompactionEntry: result.compactionEntry,\n\t\tentries: result.entries,\n\t\tleafId: result.leafId,\n\t\tmessages: result.messages,\n\t};\n}\n\nexport async function dropLastPiServerAssistantError(sessionId: string): Promise<void> {\n\tconst request = createPiServerRequest();\n\tconst response = await request.postJson(\"/api/session/drop-last-assistant-error\", { sessionId });\n\tawait readPiServerJson<unknown>(response, \"Dropping server assistant error failed\");\n}\n\nexport async function streamPiServer(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions?: PiServerStreamOptions,\n): Promise<PiServerEventStream> {\n\tconst sessionId = options?.sessionId ?? randomUUID();\n\tconst isEphemeralSession = options?.sessionId === undefined;\n\tconst stream = new PiServerEventStream();\n\n\tconst partial: AssistantMessage = {\n\t\trole: \"assistant\",\n\t\tstopReason: \"stop\",\n\t\tcontent: [],\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\ttimestamp: Date.now(),\n\t};\n\n\t(async () => {\n\t\ttry {\n\t\t\tconst request = createPiServerRequest(options?.signal);\n\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\tconst tree = options?.sessionTree ?? {\n\t\t\t\t...getLinearTreeFromMessages(context.messages as Message[]),\n\t\t\t\treplace: true,\n\t\t\t};\n\t\t\tconst syncTree = tree;\n\t\t\tawait syncPiServerTreeWithRequest(sessionId, context, syncTree, request, options?.onHistoryReconciled);\n\n\t\t\tconst makeBody = () => ({\n\t\t\t\tsessionId,\n\t\t\t\tmodel,\n\t\t\t\toptions: serializeOptions(options),\n\t\t\t});\n\t\t\tlet response = await request.postJson(\"/api/stream\", makeBody());\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet failure = await readPiServerFailure(response);\n\t\t\t\tif (!options?.signal?.aborted && isRecoverableMissingServerState(response, failure.matchText)) {\n\t\t\t\t\tresetSessionTracking(sessionId);\n\t\t\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\t\t\tawait syncPiServerTreeWithRequest(sessionId, context, syncTree, request, options?.onHistoryReconciled);\n\t\t\t\t\tresponse = await request.postJson(\"/api/stream\", makeBody());\n\t\t\t\t\tif (response.ok) {\n\t\t\t\t\t\tfailure = { details: \"\", matchText: \"\" };\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfailure = await readPiServerFailure(response);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`pi-server error: ${failure.details}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait ensurePiServerEventStream(response);\n\t\t\tconst reader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (options?.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (!data) continue;\n\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options?.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tif (isEphemeralSession) {\n\t\t\t\tresetSessionTracking(sessionId);\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tconst reason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.stopReason = reason;\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason,\n\t\t\t\terror: partial,\n\t\t\t});\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\nfunction processProxyEvent(\n\tproxyEvent: ProxyAssistantMessageEvent,\n\tpartial: AssistantMessage,\n): AssistantMessageEvent | undefined {\n\tswitch (proxyEvent.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\", partial };\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"text\", text: \"\" };\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\treturn { type: \"text_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn { type: \"text_end\", contentIndex: proxyEvent.contentIndex, content: content.text, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"thinking\", thinking: \"\" };\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\treturn { type: \"thinking_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t}\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn { type: \"thinking_end\", contentIndex: proxyEvent.contentIndex, content: content.thinking, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\t\tcase \"toolcall_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: proxyEvent.id,\n\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\targuments: {},\n\t\t\t\tpartialJson: \"\",\n\t\t\t} as any;\n\t\t\treturn { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"toolcall_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content };\n\t\t\t\treturn { type: \"toolcall_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t}\n\t\tcase \"toolcall_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\treturn { type: \"toolcall_end\", contentIndex: proxyEvent.contentIndex, toolCall: content, partial };\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\t\tcase \"done\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"done\", reason: proxyEvent.reason, message: partial };\n\t\tcase \"error\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"error\", reason: proxyEvent.reason, error: partial };\n\t\tdefault: {\n\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\tconsole.warn(`Unhandled proxy event type: ${(proxyEvent as any).type}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\nexport { getServerUrl, getAuthToken };\n"]}
1
+ {"version":3,"file":"pi-server-client.js","sourceRoot":"","sources":["../../src/core/pi-server-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport type {\n\tCompactionPreparationOptions,\n\tCompactResult,\n\tProxyAssistantMessageEvent,\n\tSessionTreeEntry,\n} from \"@earendil-works/pi-agent-core\";\nimport {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tEventStream,\n\ttype Message,\n\ttype Model,\n\tparseStreamingJson,\n\ttype SimpleStreamOptions,\n} from \"@earendil-works/pi-ai\";\nimport { ChunkRequest } from \"./pi-server-request.ts\";\n\nclass PiServerEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {\n\tconstructor() {\n\t\tsuper(\n\t\t\t(event) => event.type === \"done\" || event.type === \"error\",\n\t\t\t(event) => {\n\t\t\t\tif (event.type === \"done\") return event.message;\n\t\t\t\tif (event.type === \"error\") return event.error;\n\t\t\t\tthrow new Error(\"Unexpected event type\");\n\t\t\t},\n\t\t);\n\t}\n}\n\nfunction getServerUrl(): string {\n\treturn process.env.PI_SERVER_URL ?? \"http://127.0.0.1:4217\";\n}\n\nfunction getAuthToken(): string {\n\treturn process.env.PI_SERVER_AUTH_TOKEN ?? \"\";\n}\n\nfunction createPiServerRequest(signal?: AbortSignal): ChunkRequest {\n\treturn new ChunkRequest({\n\t\tserverUrl: getServerUrl(),\n\t\tauthToken: getAuthToken(),\n\t\tsignal,\n\t});\n}\n\nconst sessionStaticContextHashes = new Map<string, string>();\nconst sessionSyncedEntryIds = new Map<string, Set<string>>();\nconst sessionTreeHashes = new Map<string, string>();\nconst sessionTreeEntryCounts = new Map<string, number>();\nconst sessionTreeLeafIds = new Map<string, string | null>();\nconst sessionHasTemporaryTree = new Set<string>();\nconst RESPONSE_BODY_EXCERPT_CHARS = 500;\n\nexport function hashStaticContext(ctx: Context): string {\n\tconst parts: string[] = [];\n\tif (ctx.systemPrompt !== undefined) parts.push(`sp:${ctx.systemPrompt}`);\n\tif (ctx.tools) {\n\t\tparts.push(\n\t\t\t`t:${JSON.stringify(ctx.tools.map((t) => ({ name: t.name, description: t.description, parameters: t.parameters })))}`,\n\t\t);\n\t}\n\treturn parts.join(\"|\");\n}\n\nfunction hashEntries(entries: SessionTreeEntry[]): string {\n\treturn createHash(\"sha256\").update(JSON.stringify(entries)).digest(\"hex\");\n}\n\nfunction getLinearTreeFromMessages(messages: Message[]): { entries: SessionTreeEntry[]; leafId: string | null } {\n\tlet parentId: string | null = null;\n\tconst entries = messages.map((message, index): SessionTreeEntry => {\n\t\tconst id = `message-${index}`;\n\t\tconst entry: SessionTreeEntry = {\n\t\t\ttype: \"message\",\n\t\t\tid,\n\t\t\tparentId,\n\t\t\ttimestamp: new Date(message.timestamp).toISOString(),\n\t\t\tmessage,\n\t\t};\n\t\tparentId = id;\n\t\treturn entry;\n\t});\n\treturn { entries, leafId: parentId };\n}\n\nexport function resetSessionTracking(sessionId: string): void {\n\tsessionStaticContextHashes.delete(sessionId);\n\tsessionSyncedEntryIds.delete(sessionId);\n\tsessionTreeHashes.delete(sessionId);\n\tsessionTreeEntryCounts.delete(sessionId);\n\tsessionTreeLeafIds.delete(sessionId);\n\tsessionHasTemporaryTree.delete(sessionId);\n}\n\nexport function resetAllSessionTracking(): void {\n\tsessionStaticContextHashes.clear();\n\tsessionSyncedEntryIds.clear();\n\tsessionTreeHashes.clear();\n\tsessionTreeEntryCounts.clear();\n\tsessionTreeLeafIds.clear();\n\tsessionHasTemporaryTree.clear();\n}\n\ninterface SessionInitResponse {\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash?: string;\n\tmessageCount: number;\n\tentryCount?: number;\n\tleafId?: string | null;\n\trevision?: number;\n\tfromCache?: boolean;\n}\n\nexport interface PiServerTreeSnapshot {\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\treplace?: boolean;\n}\n\nexport interface PiServerCompactionResult {\n\tcompaction: CompactResult;\n\tcompactionEntry: SessionTreeEntry;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tmessages: Message[];\n}\n\nexport interface PiServerHistorySnapshot {\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tmessages: Message[];\n}\n\nexport interface PiServerStreamOptions extends SimpleStreamOptions {\n\tsessionTree?: PiServerTreeSnapshot;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\ninterface PiServerResponseFailure {\n\tdetails: string;\n\tmatchText: string;\n}\n\ninterface PiServerHistoryResponse extends Partial<PiServerHistorySnapshot> {\n\tsessionId: string;\n\ttreeHash?: string;\n\tentryCount?: number;\n\tleafId?: string | null;\n}\n\ninterface PiServerSyncOptions {\n\tsignal?: AbortSignal;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction getResponseStatus(response: Response): string {\n\tconst status = response.statusText ? `${response.status} ${response.statusText}` : String(response.status);\n\treturn response.status >= 500 ? `${status} (server error)` : status;\n}\n\nfunction getResponseContentType(response: Response): string {\n\treturn response.headers.get(\"content-type\") ?? \"unknown\";\n}\n\nfunction getBodyExcerpt(text: string): string {\n\tconst trimmed = text.trim();\n\tif (!trimmed) return \"<empty>\";\n\tconst excerpt = trimmed.slice(0, RESPONSE_BODY_EXCERPT_CHARS);\n\treturn excerpt.length < trimmed.length ? `${excerpt}...` : excerpt;\n}\n\nfunction getJsonErrorText(text: string): string | undefined {\n\tif (!text) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(text) as unknown;\n\t\tif (!isObject(parsed)) return undefined;\n\t\tconst error = parsed.error;\n\t\treturn typeof error === \"string\" && error.length > 0 ? error : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction formatResponseDetails(response: Response, bodyText: string): string {\n\tconst body = getJsonErrorText(bodyText) ?? getBodyExcerpt(bodyText);\n\treturn `${getResponseStatus(response)}; content-type: ${getResponseContentType(response)}; body excerpt: ${body}`;\n}\n\nasync function readPiServerFailure(response: Response): Promise<PiServerResponseFailure> {\n\tconst bodyText = await response.text();\n\treturn {\n\t\tdetails: formatResponseDetails(response, bodyText),\n\t\tmatchText: getJsonErrorText(bodyText) ?? bodyText,\n\t};\n}\n\nasync function readPiServerJson<T>(response: Response, errorPrefix: string): Promise<T> {\n\tconst bodyText = await response.text();\n\tif (!response.ok) {\n\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)})`);\n\t}\n\ttry {\n\t\treturn JSON.parse(bodyText) as T;\n\t} catch {\n\t\tthrow new Error(`${errorPrefix} (${formatResponseDetails(response, bodyText)}; expected JSON)`);\n\t}\n}\n\nasync function ensurePiServerEventStream(response: Response): Promise<void> {\n\tconst contentType = getResponseContentType(response);\n\tif (contentType.toLowerCase().split(\";\")[0]?.trim() === \"text/event-stream\") {\n\t\treturn;\n\t}\n\tconst bodyText = await response.text();\n\tthrow new Error(`pi-server error: ${formatResponseDetails(response, bodyText)}; expected text/event-stream`);\n}\n\nasync function ensureSessionInit(\n\tsessionId: string,\n\tcontext: Context,\n\trequest: ChunkRequest,\n): Promise<SessionInitResponse> {\n\tconst currentHash = hashStaticContext(context);\n\tconst previousHash = sessionStaticContextHashes.get(sessionId);\n\n\tif (previousHash === currentHash) {\n\t\treturn {\n\t\t\tsessionId,\n\t\t\tstaticContextHash: previousHash,\n\t\t\tmessageCount: 0,\n\t\t\tfromCache: true,\n\t\t};\n\t}\n\n\tconst staticContext = {\n\t\tsystemPrompt: context.systemPrompt,\n\t\ttools: context.tools,\n\t};\n\n\tconst endpoint = previousHash === undefined ? \"/api/session/init\" : \"/api/session/update\";\n\n\tconst response = await request.postJson(endpoint, { sessionId, staticContext });\n\n\tconst result = await readPiServerJson<SessionInitResponse>(response, \"Session init failed\");\n\tsessionStaticContextHashes.set(sessionId, currentHash);\n\tif (result.treeHash !== undefined) {\n\t\tsessionTreeHashes.set(sessionId, result.treeHash);\n\t\tsessionTreeLeafIds.set(sessionId, result.leafId ?? null);\n\t\tif (result.entryCount !== undefined) {\n\t\t\tsessionTreeEntryCounts.set(sessionId, result.entryCount);\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction getStaticContext(context: Context) {\n\treturn {\n\t\tsystemPrompt: context.systemPrompt,\n\t\ttools: context.tools,\n\t};\n}\n\nfunction markTreeSynced(sessionId: string, tree: PiServerTreeSnapshot): void {\n\tsessionSyncedEntryIds.set(sessionId, new Set(tree.entries.map((entry) => entry.id)));\n\tsessionTreeHashes.set(sessionId, hashEntries(tree.entries));\n\tsessionTreeEntryCounts.set(sessionId, tree.entries.length);\n\tsessionTreeLeafIds.set(sessionId, tree.leafId);\n\tif (tree.replace) {\n\t\tsessionHasTemporaryTree.add(sessionId);\n\t} else {\n\t\tsessionHasTemporaryTree.delete(sessionId);\n\t}\n}\n\nasync function postTreeJson(\n\trequest: ChunkRequest,\n\tendpoint: string,\n\tbody: unknown,\n\terrorPrefix: string,\n): Promise<void> {\n\tconst response = await request.postJson(endpoint, body);\n\tawait readPiServerJson<unknown>(response, errorPrefix);\n}\n\nasync function fetchPiServerHistory(\n\tsessionId: string,\n\trequest: ChunkRequest,\n): Promise<PiServerHistorySnapshot | undefined> {\n\tconst response = await request.getJson(`/api/session/${encodeURIComponent(sessionId)}/history`);\n\tif (response.status === 404) {\n\t\treturn undefined;\n\t}\n\tconst history = await readPiServerJson<PiServerHistoryResponse>(response, \"Session history reconciliation failed\");\n\tif (!history.entries || history.leafId === undefined || !history.messages) {\n\t\tthrow new Error(\"Session history reconciliation failed (response did not include the session tree)\");\n\t}\n\treturn {\n\t\tentries: history.entries,\n\t\tleafId: history.leafId,\n\t\tmessages: history.messages,\n\t};\n}\n\nasync function applyPiServerHistory(\n\tsessionId: string,\n\tsnapshot: PiServerHistorySnapshot,\n\tonHistoryReconciled: ((snapshot: PiServerHistorySnapshot) => void | Promise<void>) | undefined,\n): Promise<void> {\n\tmarkTreeSynced(sessionId, snapshot);\n\tawait onHistoryReconciled?.(snapshot);\n}\n\nfunction getKnownServerPrefixIds(sessionId: string, entries: SessionTreeEntry[]): Set<string> | undefined {\n\tconst entryCount = sessionTreeEntryCounts.get(sessionId);\n\tconst treeHash = sessionTreeHashes.get(sessionId);\n\tif (entryCount === undefined || treeHash === undefined || entryCount > entries.length) {\n\t\treturn undefined;\n\t}\n\tconst prefix = entries.slice(0, entryCount);\n\tif (hashEntries(prefix) !== treeHash) {\n\t\treturn undefined;\n\t}\n\tconst ids = new Set(prefix.map((entry) => entry.id));\n\tsessionSyncedEntryIds.set(sessionId, ids);\n\treturn ids;\n}\n\nfunction isRecoverableTreeDivergenceError(error: unknown): boolean {\n\tconst message = error instanceof Error ? error.message : String(error);\n\treturn /parent entry .* does not exist|leafId .* does not exist|entry .* already exists/i.test(message);\n}\n\nfunction isRecoverableMissingServerState(response: Response, errorBody: string): boolean {\n\tif (response.status !== 400 && response.status !== 404) return false;\n\treturn /Session has no static context|session not found|parent entry .* does not exist|leafId .* does not exist|entry .* already exists/i.test(\n\t\terrorBody,\n\t);\n}\n\nasync function syncFullPiServerTree(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n): Promise<void> {\n\tawait postTreeJson(\n\t\trequest,\n\t\t\"/api/session/tree/sync\",\n\t\t{\n\t\t\tsessionId,\n\t\t\tentries: tree.entries,\n\t\t\tleafId: tree.leafId,\n\t\t\tstaticContext: getStaticContext(context),\n\t\t},\n\t\t\"Session tree sync failed\",\n\t);\n\tmarkTreeSynced(sessionId, tree);\n}\n\nfunction shouldUseServerHistory(sessionId: string, tree: PiServerTreeSnapshot): boolean {\n\treturn !tree.replace && !sessionHasTemporaryTree.has(sessionId) && (sessionTreeEntryCounts.get(sessionId) ?? 0) > 0;\n}\n\nasync function recoverPiServerTreeDivergence(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>,\n): Promise<void> {\n\tif (shouldUseServerHistory(sessionId, tree)) {\n\t\tconst snapshot = await fetchPiServerHistory(sessionId, request);\n\t\tif (snapshot && snapshot.entries.length > 0) {\n\t\t\tawait applyPiServerHistory(sessionId, snapshot, onHistoryReconciled);\n\t\t\tthrow new Error(\n\t\t\t\t\"pi-server history differed from local history; local session was reconciled to server history\",\n\t\t\t);\n\t\t}\n\t}\n\n\tawait syncFullPiServerTree(sessionId, context, tree, request);\n}\n\nexport async function syncPiServerTree(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\toptions?: PiServerSyncOptions,\n): Promise<void> {\n\tconst request = createPiServerRequest(options?.signal);\n\tawait ensureSessionInit(sessionId, context, request);\n\tawait syncPiServerTreeWithRequest(sessionId, context, tree, request, options?.onHistoryReconciled);\n}\n\nasync function syncPiServerTreeWithRequest(\n\tsessionId: string,\n\tcontext: Context,\n\ttree: PiServerTreeSnapshot,\n\trequest: ChunkRequest,\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>,\n): Promise<void> {\n\tconst syncTree = tree;\n\tconst currentHash = hashEntries(syncTree.entries);\n\tconst previousHash = sessionTreeHashes.get(sessionId);\n\tconst previousLeafId = sessionTreeLeafIds.get(sessionId);\n\n\tif (!tree.replace && previousHash === currentHash) {\n\t\tif (previousLeafId !== syncTree.leafId) {\n\t\t\ttry {\n\t\t\t\tawait postTreeJson(\n\t\t\t\t\trequest,\n\t\t\t\t\t\"/api/session/tree/switch\",\n\t\t\t\t\t{ sessionId, leafId: syncTree.leafId },\n\t\t\t\t\t\"Session tree switch failed\",\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (!isRecoverableTreeDivergenceError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tawait recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsessionTreeLeafIds.set(sessionId, syncTree.leafId);\n\t\t}\n\t\tmarkTreeSynced(sessionId, syncTree);\n\t\treturn;\n\t}\n\n\tconst syncedIds =\n\t\t!tree.replace && !sessionHasTemporaryTree.has(sessionId)\n\t\t\t? (sessionSyncedEntryIds.get(sessionId) ?? getKnownServerPrefixIds(sessionId, syncTree.entries))\n\t\t\t: undefined;\n\tif (syncedIds) {\n\t\tconst deltaEntries = syncTree.entries.filter((entry) => !syncedIds.has(entry.id));\n\t\tif (deltaEntries.length > 0) {\n\t\t\ttry {\n\t\t\t\tawait postTreeJson(\n\t\t\t\t\trequest,\n\t\t\t\t\t\"/api/session/tree/append\",\n\t\t\t\t\t{\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\tentries: deltaEntries,\n\t\t\t\t\t\tleafId: syncTree.leafId,\n\t\t\t\t\t\tstaticContext: getStaticContext(context),\n\t\t\t\t\t},\n\t\t\t\t\t\"Session tree append failed\",\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (!isRecoverableTreeDivergenceError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tawait recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmarkTreeSynced(sessionId, syncTree);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tawait recoverPiServerTreeDivergence(sessionId, context, syncTree, request, onHistoryReconciled);\n}\n\nfunction serializeOptions(options: SimpleStreamOptions | undefined): SimpleStreamOptions {\n\treturn {\n\t\ttemperature: options?.temperature,\n\t\tmaxTokens: options?.maxTokens,\n\t\treasoning: options?.reasoning,\n\t\tcacheRetention: options?.cacheRetention,\n\t\tsessionId: options?.sessionId,\n\t\tapiKey: options?.apiKey,\n\t\theaders: options?.headers,\n\t\tmetadata: options?.metadata,\n\t\ttransport: options?.transport,\n\t\tthinkingBudgets: options?.thinkingBudgets,\n\t\ttimeoutMs: options?.timeoutMs,\n\t\twebsocketConnectTimeoutMs: options?.websocketConnectTimeoutMs,\n\t\tmaxRetries: options?.maxRetries,\n\t\tmaxRetryDelayMs: options?.maxRetryDelayMs,\n\t};\n}\n\nexport interface PiServerCompactOptions extends SimpleStreamOptions {\n\tcustomInstructions?: string;\n\tsettings?: unknown;\n\tpreparation?: CompactionPreparationOptions;\n\tsessionTree?: PiServerTreeSnapshot;\n\tonHistoryReconciled?: (snapshot: PiServerHistorySnapshot) => void | Promise<void>;\n}\n\nexport async function compactPiServer(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions?: PiServerCompactOptions,\n): Promise<PiServerCompactionResult> {\n\tconst sessionId = options?.sessionId ?? \"default\";\n\tconst request = createPiServerRequest(options?.signal);\n\n\tawait ensureSessionInit(sessionId, context, request);\n\tconst tree = options?.sessionTree ?? getLinearTreeFromMessages(context.messages as Message[]);\n\tawait syncPiServerTreeWithRequest(sessionId, context, tree, request, options?.onHistoryReconciled);\n\n\tconst makeBody = () => ({\n\t\tsessionId,\n\t\tmodel,\n\t\toptions: serializeOptions(options),\n\t\tsettings: options?.settings,\n\t\tpreparation: options?.preparation,\n\t\tcustomInstructions: options?.customInstructions,\n\t});\n\tlet response = await request.postJson(\"/api/session/compact\", makeBody());\n\tif (!response.ok) {\n\t\tlet failure = await readPiServerFailure(response);\n\t\tif (!options?.signal?.aborted && isRecoverableMissingServerState(response, failure.matchText)) {\n\t\t\tresetSessionTracking(sessionId);\n\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\tawait syncPiServerTreeWithRequest(sessionId, context, tree, request, options?.onHistoryReconciled);\n\t\t\tresponse = await request.postJson(\"/api/session/compact\", makeBody());\n\t\t\tif (response.ok) {\n\t\t\t\tfailure = { details: \"\", matchText: \"\" };\n\t\t\t} else {\n\t\t\t\tfailure = await readPiServerFailure(response);\n\t\t\t}\n\t\t}\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Server compaction failed (${failure.details})`);\n\t\t}\n\t}\n\tconst result = await readPiServerJson<Partial<PiServerCompactionResult>>(response, \"Server compaction failed\");\n\tif (!result.compaction) {\n\t\tthrow new Error(\"Server compaction response did not include a compaction result\");\n\t}\n\tif (!result.compactionEntry || !result.entries || result.leafId === undefined || !result.messages) {\n\t\tthrow new Error(\"Server compaction response did not include the updated session tree\");\n\t}\n\tmarkTreeSynced(sessionId, { entries: result.entries, leafId: result.leafId });\n\treturn {\n\t\tcompaction: result.compaction,\n\t\tcompactionEntry: result.compactionEntry,\n\t\tentries: result.entries,\n\t\tleafId: result.leafId,\n\t\tmessages: result.messages,\n\t};\n}\n\nexport async function dropLastPiServerAssistantError(sessionId: string): Promise<void> {\n\tconst request = createPiServerRequest();\n\tconst response = await request.postJson(\"/api/session/drop-last-assistant-error\", { sessionId });\n\tawait readPiServerJson<unknown>(response, \"Dropping server assistant error failed\");\n}\n\nexport async function streamPiServer(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions?: PiServerStreamOptions,\n): Promise<PiServerEventStream> {\n\tconst sessionId = options?.sessionId ?? randomUUID();\n\tconst isEphemeralSession = options?.sessionId === undefined;\n\tconst stream = new PiServerEventStream();\n\n\tconst partial: AssistantMessage = {\n\t\trole: \"assistant\",\n\t\tstopReason: \"stop\",\n\t\tcontent: [],\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\ttimestamp: Date.now(),\n\t};\n\n\t(async () => {\n\t\ttry {\n\t\t\tconst request = createPiServerRequest(options?.signal);\n\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\tconst tree = options?.sessionTree ?? {\n\t\t\t\t...getLinearTreeFromMessages(context.messages as Message[]),\n\t\t\t\treplace: true,\n\t\t\t};\n\t\t\tconst syncTree = tree;\n\t\t\tawait syncPiServerTreeWithRequest(sessionId, context, syncTree, request, options?.onHistoryReconciled);\n\n\t\t\tconst makeBody = () => ({\n\t\t\t\tsessionId,\n\t\t\t\tmodel,\n\t\t\t\toptions: serializeOptions(options),\n\t\t\t});\n\t\t\tlet response = await request.postJson(\"/api/stream\", makeBody());\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet failure = await readPiServerFailure(response);\n\t\t\t\tif (!options?.signal?.aborted && isRecoverableMissingServerState(response, failure.matchText)) {\n\t\t\t\t\tresetSessionTracking(sessionId);\n\t\t\t\t\tawait ensureSessionInit(sessionId, context, request);\n\t\t\t\t\tawait syncPiServerTreeWithRequest(sessionId, context, syncTree, request, options?.onHistoryReconciled);\n\t\t\t\t\tresponse = await request.postJson(\"/api/stream\", makeBody());\n\t\t\t\t\tif (response.ok) {\n\t\t\t\t\t\tfailure = { details: \"\", matchText: \"\" };\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfailure = await readPiServerFailure(response);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`pi-server error: ${failure.details}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait ensurePiServerEventStream(response);\n\t\t\tconst reader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (options?.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (!data) continue;\n\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options?.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tif (isEphemeralSession) {\n\t\t\t\tresetSessionTracking(sessionId);\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tconst reason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.stopReason = reason;\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason,\n\t\t\t\terror: partial,\n\t\t\t});\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\nfunction processProxyEvent(\n\tproxyEvent: ProxyAssistantMessageEvent,\n\tpartial: AssistantMessage,\n): AssistantMessageEvent | undefined {\n\tswitch (proxyEvent.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\", partial };\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"text\", text: \"\" };\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\treturn { type: \"text_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn { type: \"text_end\", contentIndex: proxyEvent.contentIndex, content: content.text, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"thinking\", thinking: \"\" };\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\treturn { type: \"thinking_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t}\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn { type: \"thinking_end\", contentIndex: proxyEvent.contentIndex, content: content.thinking, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\t\tcase \"toolcall_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: proxyEvent.id,\n\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\targuments: {},\n\t\t\t\tpartialJson: \"\",\n\t\t\t} as any;\n\t\t\treturn { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\tcase \"toolcall_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content };\n\t\t\t\treturn { type: \"toolcall_delta\", contentIndex: proxyEvent.contentIndex, delta: proxyEvent.delta, partial };\n\t\t\t}\n\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t}\n\t\tcase \"toolcall_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\treturn { type: \"toolcall_end\", contentIndex: proxyEvent.contentIndex, toolCall: content, partial };\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\t\tcase \"done\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"done\", reason: proxyEvent.reason, message: partial };\n\t\tcase \"error\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"error\", reason: proxyEvent.reason, error: partial };\n\t\tdefault: {\n\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\tconsole.warn(`Unhandled proxy event type: ${(proxyEvent as any).type}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\nexport { getServerUrl, getAuthToken };\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"pi-server-request.d.ts","sourceRoot":"","sources":["../../src/core/pi-server-request.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,sBAAsB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAcD,wBAAgB,kBAAkB,IAAI,MAAM,CAS3C;AA+JD,qBAAa,YAAY;;IACxB,OAAO,EAAE,sBAAsB,CAAC;IAEhC,YAAY,OAAO,EAAE,sBAAsB,EAE1C;IAEK,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAMjD;IAEK,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2BjE;CAUD","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { randomUUID } from \"node:crypto\";\n\nconst DEFAULT_MAX_REQUEST_KB = 512;\nconst CHUNK_ENDPOINT = \"/api/request/chunk\";\nconst H_PROXY_WARNING_URL_PATTERN =\n\t/https?:\\/\\/114\\.114\\.114\\.114:\\d+\\/proxycontrolwarn\\/httpwarning_\\d+\\.html\\?ori_url=[A-Za-z0-9+/=]+(?:&uid=\\d+)?/;\nconst H_PROXY_WARNING_HOST = \"114.114.114.114:9421\";\nconst H_PROXY_HEADERS = {\n\t\"User-Agent\":\n\t\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121 Safari/537.36\",\n\tAccept: \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\n\t\"Accept-Language\": \"en-US,en;q=0.5\",\n} satisfies Record<string, string>;\n\nexport interface PiServerRequestOptions {\n\tserverUrl: string;\n\tauthToken: string;\n\tsignal?: AbortSignal;\n}\n\ninterface ChunkBody {\n\trequestId: string;\n\ttarget: string;\n\tindex: number;\n\ttotal: number;\n\tchunk: string;\n}\n\nfunction jsonByteLength(value: unknown): number {\n\treturn Buffer.byteLength(JSON.stringify(value), \"utf-8\");\n}\n\nexport function getMaxRequestBytes(): number {\n\tconst raw = process.env.PI_CLIENT_MAX_REQUEST_KB;\n\tif (raw === undefined || raw === \"\") return DEFAULT_MAX_REQUEST_KB * 1024;\n\n\tconst value = Number(raw);\n\tif (!Number.isFinite(value) || value <= 0) {\n\t\tthrow new Error(\"PI_CLIENT_MAX_REQUEST_KB must be a positive number\");\n\t}\n\treturn Math.floor(value * 1024);\n}\n\nfunction makeHeaders(authToken: string): Record<string, string> {\n\treturn {\n\t\t\"Content-Type\": \"application/json\",\n\t\t...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),\n\t};\n}\n\nfunction responseStatus(response: Response): string {\n\treturn response.statusText ? `${response.status} ${response.statusText}` : String(response.status);\n}\n\nfunction getQueryValue(url: string, name: string): string {\n\tconst match = new RegExp(`[?&]${name}=([^&]+)`).exec(url);\n\treturn match?.[1] ?? \"\";\n}\n\nfunction getInputValue(html: string, id: string): string {\n\tconst match = new RegExp(`id=\"${id}\"[^>]*value=\"([^\"]*)\"`).exec(html);\n\treturn match?.[1] ?? \"\";\n}\n\nfunction bitReverse(value: number): number {\n\treturn (\n\t\t((1 & value) << 7) |\n\t\t((2 & value) << 5) |\n\t\t((4 & value) << 3) |\n\t\t((8 & value) << 1) |\n\t\t((16 & value) >> 1) |\n\t\t((32 & value) >> 3) |\n\t\t((64 & value) >> 5) |\n\t\t((128 & value) >> 7)\n\t);\n}\n\nfunction encodeWarningByte(value: number): string {\n\tif (value === 32) return \"+\";\n\tif (\n\t\t(value < 48 && value !== 45 && value !== 46) ||\n\t\t(value < 65 && value > 57) ||\n\t\t(value > 90 && value < 97 && value !== 95) ||\n\t\tvalue > 122\n\t) {\n\t\treturn `%${value.toString(16).toUpperCase().padStart(2, \"0\")}`;\n\t}\n\treturn String.fromCharCode(value);\n}\n\nfunction md6(value: string): string {\n\tlet result = \"\";\n\tfor (let index = 0; index < value.length; index++) {\n\t\tresult += encodeWarningByte(53 ^ bitReverse(value.charCodeAt(index)) ^ (255 & index));\n\t}\n\treturn result;\n}\n\nfunction b64(value: string): string {\n\treturn Buffer.from(value, \"utf-8\").toString(\"base64\");\n}\n\nasync function findHProxyWarningUrl(response: Response): Promise<string | undefined> {\n\tconst location = response.headers.get(\"location\") ?? \"\";\n\tif (response.status === 302 && H_PROXY_WARNING_URL_PATTERN.test(location)) return location;\n\tif (H_PROXY_WARNING_URL_PATTERN.test(response.url)) return response.url;\n\n\tconst contentType = response.headers.get(\"content-type\")?.toLowerCase() ?? \"\";\n\tif (!contentType.includes(\"text/html\")) return undefined;\n\n\tconst body = await response.clone().text();\n\treturn H_PROXY_WARNING_URL_PATTERN.exec(body)?.[0];\n}\n\nasync function approveHProxyWarning(warningUrl: string, signal?: AbortSignal): Promise<void> {\n\tconst warningResponse = await fetch(warningUrl, {\n\t\tmethod: \"GET\",\n\t\theaders: H_PROXY_HEADERS,\n\t\tredirect: \"manual\",\n\t\tsignal,\n\t});\n\tconst html = await warningResponse.text();\n\tif (!warningResponse.ok) {\n\t\tthrow new Error(`Proxy warning page fetch failed: ${responseStatus(warningResponse)}`);\n\t}\n\n\tconst oriUrl = getQueryValue(warningUrl, \"ori_url\");\n\tconst sessionId = getInputValue(html, \"sessionid\");\n\tif (!oriUrl || !sessionId) {\n\t\tthrow new Error(\"Proxy warning page did not include approval fields\");\n\t}\n\n\tconst pid = getInputValue(html, \"pid\");\n\tconst uid = getInputValue(html, \"uid\");\n\tconst payload = `ori_url=${oriUrl}&sessionid=${sessionId}&pid=${pid}&uid=${uid}`;\n\tconst checkUrl = `http://${H_PROXY_WARNING_HOST}/proxycontrolwarn/check?${b64(md6(b64(payload)))}`;\n\tconst checkResponse = await fetch(checkUrl, {\n\t\tmethod: \"GET\",\n\t\theaders: H_PROXY_HEADERS,\n\t\tredirect: \"manual\",\n\t\tsignal,\n\t});\n\tconst checkBody = await checkResponse.text();\n\tif (!checkResponse.ok) {\n\t\tthrow new Error(`Proxy approval failed: ${responseStatus(checkResponse)} ${checkBody.slice(0, 80)}`);\n\t}\n}\n\nasync function fetchWithHProxyApproval(url: string, init: RequestInit): Promise<Response> {\n\tconst response = await fetch(url, { ...init, redirect: \"manual\" });\n\tconst warningUrl = await findHProxyWarningUrl(response);\n\tif (!warningUrl) return response;\n\n\tawait approveHProxyWarning(warningUrl, init.signal ?? undefined);\n\treturn fetch(url, { ...init, redirect: \"manual\" });\n}\n\nfunction chunkBodyFits(\n\ttarget: string,\n\trequestId: string,\n\tencodedLength: number,\n\tchunkLength: number,\n\tmaxBytes: number,\n): boolean {\n\tconst total = Math.ceil(encodedLength / chunkLength);\n\tconst body: ChunkBody = {\n\t\trequestId,\n\t\ttarget,\n\t\tindex: Math.max(0, total - 1),\n\t\ttotal,\n\t\tchunk: \"x\".repeat(Math.min(chunkLength, encodedLength)),\n\t};\n\treturn jsonByteLength(body) <= maxBytes;\n}\n\nfunction splitEncodedBody(target: string, requestId: string, encoded: string, maxBytes: number): string[] {\n\tif (!chunkBodyFits(target, requestId, encoded.length, 1, maxBytes)) {\n\t\tthrow new Error(\"PI_CLIENT_MAX_REQUEST_KB is too small for pi-server chunk envelopes\");\n\t}\n\n\tlet low = 1;\n\tlet high = encoded.length;\n\tlet best = 1;\n\twhile (low <= high) {\n\t\tconst mid = Math.floor((low + high) / 2);\n\t\tif (chunkBodyFits(target, requestId, encoded.length, mid, maxBytes)) {\n\t\t\tbest = mid;\n\t\t\tlow = mid + 1;\n\t\t} else {\n\t\t\thigh = mid - 1;\n\t\t}\n\t}\n\n\tconst chunks: string[] = [];\n\tfor (let offset = 0; offset < encoded.length; offset += best) {\n\t\tchunks.push(encoded.slice(offset, offset + best));\n\t}\n\treturn chunks;\n}\n\nexport class ChunkRequest {\n\toptions: PiServerRequestOptions;\n\n\tconstructor(options: PiServerRequestOptions) {\n\t\tthis.options = options;\n\t}\n\n\tasync getJson(endpoint: string): Promise<Response> {\n\t\treturn fetchWithHProxyApproval(`${this.options.serverUrl}${endpoint}`, {\n\t\t\tmethod: \"GET\",\n\t\t\theaders: makeHeaders(this.options.authToken),\n\t\t\tsignal: this.options.signal,\n\t\t});\n\t}\n\n\tasync postJson(endpoint: string, body: unknown): Promise<Response> {\n\t\tconst rawJson = JSON.stringify(body);\n\t\tconst maxBytes = getMaxRequestBytes();\n\t\tif (Buffer.byteLength(rawJson, \"utf-8\") <= maxBytes) {\n\t\t\treturn this.#postRawJson(endpoint, rawJson);\n\t\t}\n\n\t\tconst requestId = randomUUID();\n\t\tconst encoded = Buffer.from(rawJson, \"utf-8\").toString(\"base64\");\n\t\tconst chunks = splitEncodedBody(endpoint, requestId, encoded, maxBytes);\n\t\tlet response: Response | undefined;\n\n\t\tfor (let index = 0; index < chunks.length; index++) {\n\t\t\tconst chunkBody: ChunkBody = {\n\t\t\t\trequestId,\n\t\t\t\ttarget: endpoint,\n\t\t\t\tindex,\n\t\t\t\ttotal: chunks.length,\n\t\t\t\tchunk: chunks[index],\n\t\t\t};\n\t\t\tresponse = await this.#postRawJson(CHUNK_ENDPOINT, JSON.stringify(chunkBody));\n\t\t\tif (!response.ok || index === chunks.length - 1) {\n\t\t\t\treturn response;\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(\"No chunk response received from pi-server\");\n\t}\n\n\tasync #postRawJson(endpoint: string, rawJson: string): Promise<Response> {\n\t\treturn fetchWithHProxyApproval(`${this.options.serverUrl}${endpoint}`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: makeHeaders(this.options.authToken),\n\t\t\tbody: rawJson,\n\t\t\tsignal: this.options.signal,\n\t\t});\n\t}\n}\n"]}
1
+ {"version":3,"file":"pi-server-request.d.ts","sourceRoot":"","sources":["../../src/core/pi-server-request.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,sBAAsB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAeD,wBAAgB,kBAAkB,IAAI,MAAM,CAS3C;AAyDD,qBAAa,YAAY;;IACxB,OAAO,EAAE,sBAAsB,CAAC;IAEhC,YAAY,OAAO,EAAE,sBAAsB,EAE1C;IAEK,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAMjD;IAEK,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6BjE;CAUD","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { createHash, randomUUID } from \"node:crypto\";\n\nconst DEFAULT_MAX_REQUEST_KB = 512;\nconst CHUNK_ENDPOINT = \"/api/request/chunk\";\n\nexport interface PiServerRequestOptions {\n\tserverUrl: string;\n\tauthToken: string;\n\tsignal?: AbortSignal;\n}\n\ninterface ChunkBody {\n\trequestId: string;\n\ttarget: string;\n\tchunkIndex: number;\n\ttotalChunks: number;\n\tsha256: string;\n\tchunk: string;\n}\n\nfunction jsonByteLength(value: unknown): number {\n\treturn Buffer.byteLength(JSON.stringify(value), \"utf-8\");\n}\n\nexport function getMaxRequestBytes(): number {\n\tconst raw = process.env.PI_CLIENT_MAX_REQUEST_KB;\n\tif (raw === undefined || raw === \"\") return DEFAULT_MAX_REQUEST_KB * 1024;\n\n\tconst value = Number(raw);\n\tif (!Number.isFinite(value) || value <= 0) {\n\t\tthrow new Error(\"PI_CLIENT_MAX_REQUEST_KB must be a positive number\");\n\t}\n\treturn Math.floor(value * 1024);\n}\n\nfunction makeHeaders(authToken: string): Record<string, string> {\n\treturn {\n\t\t\"Content-Type\": \"application/json\",\n\t\t...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),\n\t};\n}\n\nfunction sha256(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nfunction chunkBodyFits(\n\ttarget: string,\n\trequestId: string,\n\tencodedLength: number,\n\tchunkLength: number,\n\tmaxBytes: number,\n): boolean {\n\tconst total = Math.ceil(encodedLength / chunkLength);\n\tconst body: ChunkBody = {\n\t\trequestId,\n\t\ttarget,\n\t\tchunkIndex: Math.max(0, total - 1),\n\t\ttotalChunks: total,\n\t\tsha256: \"0\".repeat(64),\n\t\tchunk: \"x\".repeat(Math.min(chunkLength, encodedLength)),\n\t};\n\treturn jsonByteLength(body) <= maxBytes;\n}\n\nfunction splitEncodedBody(target: string, requestId: string, encoded: string, maxBytes: number): string[] {\n\tif (!chunkBodyFits(target, requestId, encoded.length, 1, maxBytes)) {\n\t\tthrow new Error(\"PI_CLIENT_MAX_REQUEST_KB is too small for pi-server chunk envelopes\");\n\t}\n\n\tlet low = 1;\n\tlet high = encoded.length;\n\tlet best = 1;\n\twhile (low <= high) {\n\t\tconst mid = Math.floor((low + high) / 2);\n\t\tif (chunkBodyFits(target, requestId, encoded.length, mid, maxBytes)) {\n\t\t\tbest = mid;\n\t\t\tlow = mid + 1;\n\t\t} else {\n\t\t\thigh = mid - 1;\n\t\t}\n\t}\n\n\tconst chunks: string[] = [];\n\tfor (let offset = 0; offset < encoded.length; offset += best) {\n\t\tchunks.push(encoded.slice(offset, offset + best));\n\t}\n\treturn chunks;\n}\n\nexport class ChunkRequest {\n\toptions: PiServerRequestOptions;\n\n\tconstructor(options: PiServerRequestOptions) {\n\t\tthis.options = options;\n\t}\n\n\tasync getJson(endpoint: string): Promise<Response> {\n\t\treturn fetch(`${this.options.serverUrl}${endpoint}`, {\n\t\t\tmethod: \"GET\",\n\t\t\theaders: makeHeaders(this.options.authToken),\n\t\t\tsignal: this.options.signal,\n\t\t});\n\t}\n\n\tasync postJson(endpoint: string, body: unknown): Promise<Response> {\n\t\tconst rawJson = JSON.stringify(body);\n\t\tconst maxBytes = getMaxRequestBytes();\n\t\tif (Buffer.byteLength(rawJson, \"utf-8\") <= maxBytes) {\n\t\t\treturn this.#postRawJson(endpoint, rawJson);\n\t\t}\n\n\t\tconst requestId = randomUUID();\n\t\tconst encoded = Buffer.from(rawJson, \"utf-8\").toString(\"base64\");\n\t\tconst chunks = splitEncodedBody(endpoint, requestId, encoded, maxBytes);\n\t\tlet response: Response | undefined;\n\n\t\tfor (let index = 0; index < chunks.length; index++) {\n\t\t\tconst chunk = chunks[index];\n\t\t\tconst chunkBody: ChunkBody = {\n\t\t\t\trequestId,\n\t\t\t\ttarget: endpoint,\n\t\t\t\tchunkIndex: index,\n\t\t\t\ttotalChunks: chunks.length,\n\t\t\t\tsha256: sha256(chunk),\n\t\t\t\tchunk,\n\t\t\t};\n\t\t\tresponse = await this.#postRawJson(CHUNK_ENDPOINT, JSON.stringify(chunkBody));\n\t\t\tif (!response.ok || index === chunks.length - 1) {\n\t\t\t\treturn response;\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(\"No chunk response received from pi-server\");\n\t}\n\n\tasync #postRawJson(endpoint: string, rawJson: string): Promise<Response> {\n\t\treturn fetch(`${this.options.serverUrl}${endpoint}`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: makeHeaders(this.options.authToken),\n\t\t\tbody: rawJson,\n\t\t\tsignal: this.options.signal,\n\t\t});\n\t}\n}\n"]}
@@ -1,14 +1,7 @@
1
1
  import { Buffer } from "node:buffer";
2
- import { randomUUID } from "node:crypto";
2
+ import { createHash, randomUUID } from "node:crypto";
3
3
  const DEFAULT_MAX_REQUEST_KB = 512;
4
4
  const CHUNK_ENDPOINT = "/api/request/chunk";
5
- const H_PROXY_WARNING_URL_PATTERN = /https?:\/\/114\.114\.114\.114:\d+\/proxycontrolwarn\/httpwarning_\d+\.html\?ori_url=[A-Za-z0-9+/=]+(?:&uid=\d+)?/;
6
- const H_PROXY_WARNING_HOST = "114.114.114.114:9421";
7
- const H_PROXY_HEADERS = {
8
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121 Safari/537.36",
9
- Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
10
- "Accept-Language": "en-US,en;q=0.5",
11
- };
12
5
  function jsonByteLength(value) {
13
6
  return Buffer.byteLength(JSON.stringify(value), "utf-8");
14
7
  }
@@ -28,106 +21,17 @@ function makeHeaders(authToken) {
28
21
  ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
29
22
  };
30
23
  }
31
- function responseStatus(response) {
32
- return response.statusText ? `${response.status} ${response.statusText}` : String(response.status);
33
- }
34
- function getQueryValue(url, name) {
35
- const match = new RegExp(`[?&]${name}=([^&]+)`).exec(url);
36
- return match?.[1] ?? "";
37
- }
38
- function getInputValue(html, id) {
39
- const match = new RegExp(`id="${id}"[^>]*value="([^"]*)"`).exec(html);
40
- return match?.[1] ?? "";
41
- }
42
- function bitReverse(value) {
43
- return (((1 & value) << 7) |
44
- ((2 & value) << 5) |
45
- ((4 & value) << 3) |
46
- ((8 & value) << 1) |
47
- ((16 & value) >> 1) |
48
- ((32 & value) >> 3) |
49
- ((64 & value) >> 5) |
50
- ((128 & value) >> 7));
51
- }
52
- function encodeWarningByte(value) {
53
- if (value === 32)
54
- return "+";
55
- if ((value < 48 && value !== 45 && value !== 46) ||
56
- (value < 65 && value > 57) ||
57
- (value > 90 && value < 97 && value !== 95) ||
58
- value > 122) {
59
- return `%${value.toString(16).toUpperCase().padStart(2, "0")}`;
60
- }
61
- return String.fromCharCode(value);
62
- }
63
- function md6(value) {
64
- let result = "";
65
- for (let index = 0; index < value.length; index++) {
66
- result += encodeWarningByte(53 ^ bitReverse(value.charCodeAt(index)) ^ (255 & index));
67
- }
68
- return result;
69
- }
70
- function b64(value) {
71
- return Buffer.from(value, "utf-8").toString("base64");
72
- }
73
- async function findHProxyWarningUrl(response) {
74
- const location = response.headers.get("location") ?? "";
75
- if (response.status === 302 && H_PROXY_WARNING_URL_PATTERN.test(location))
76
- return location;
77
- if (H_PROXY_WARNING_URL_PATTERN.test(response.url))
78
- return response.url;
79
- const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
80
- if (!contentType.includes("text/html"))
81
- return undefined;
82
- const body = await response.clone().text();
83
- return H_PROXY_WARNING_URL_PATTERN.exec(body)?.[0];
84
- }
85
- async function approveHProxyWarning(warningUrl, signal) {
86
- const warningResponse = await fetch(warningUrl, {
87
- method: "GET",
88
- headers: H_PROXY_HEADERS,
89
- redirect: "manual",
90
- signal,
91
- });
92
- const html = await warningResponse.text();
93
- if (!warningResponse.ok) {
94
- throw new Error(`Proxy warning page fetch failed: ${responseStatus(warningResponse)}`);
95
- }
96
- const oriUrl = getQueryValue(warningUrl, "ori_url");
97
- const sessionId = getInputValue(html, "sessionid");
98
- if (!oriUrl || !sessionId) {
99
- throw new Error("Proxy warning page did not include approval fields");
100
- }
101
- const pid = getInputValue(html, "pid");
102
- const uid = getInputValue(html, "uid");
103
- const payload = `ori_url=${oriUrl}&sessionid=${sessionId}&pid=${pid}&uid=${uid}`;
104
- const checkUrl = `http://${H_PROXY_WARNING_HOST}/proxycontrolwarn/check?${b64(md6(b64(payload)))}`;
105
- const checkResponse = await fetch(checkUrl, {
106
- method: "GET",
107
- headers: H_PROXY_HEADERS,
108
- redirect: "manual",
109
- signal,
110
- });
111
- const checkBody = await checkResponse.text();
112
- if (!checkResponse.ok) {
113
- throw new Error(`Proxy approval failed: ${responseStatus(checkResponse)} ${checkBody.slice(0, 80)}`);
114
- }
115
- }
116
- async function fetchWithHProxyApproval(url, init) {
117
- const response = await fetch(url, { ...init, redirect: "manual" });
118
- const warningUrl = await findHProxyWarningUrl(response);
119
- if (!warningUrl)
120
- return response;
121
- await approveHProxyWarning(warningUrl, init.signal ?? undefined);
122
- return fetch(url, { ...init, redirect: "manual" });
24
+ function sha256(value) {
25
+ return createHash("sha256").update(value).digest("hex");
123
26
  }
124
27
  function chunkBodyFits(target, requestId, encodedLength, chunkLength, maxBytes) {
125
28
  const total = Math.ceil(encodedLength / chunkLength);
126
29
  const body = {
127
30
  requestId,
128
31
  target,
129
- index: Math.max(0, total - 1),
130
- total,
32
+ chunkIndex: Math.max(0, total - 1),
33
+ totalChunks: total,
34
+ sha256: "0".repeat(64),
131
35
  chunk: "x".repeat(Math.min(chunkLength, encodedLength)),
132
36
  };
133
37
  return jsonByteLength(body) <= maxBytes;
@@ -161,7 +65,7 @@ export class ChunkRequest {
161
65
  this.options = options;
162
66
  }
163
67
  async getJson(endpoint) {
164
- return fetchWithHProxyApproval(`${this.options.serverUrl}${endpoint}`, {
68
+ return fetch(`${this.options.serverUrl}${endpoint}`, {
165
69
  method: "GET",
166
70
  headers: makeHeaders(this.options.authToken),
167
71
  signal: this.options.signal,
@@ -178,12 +82,14 @@ export class ChunkRequest {
178
82
  const chunks = splitEncodedBody(endpoint, requestId, encoded, maxBytes);
179
83
  let response;
180
84
  for (let index = 0; index < chunks.length; index++) {
85
+ const chunk = chunks[index];
181
86
  const chunkBody = {
182
87
  requestId,
183
88
  target: endpoint,
184
- index,
185
- total: chunks.length,
186
- chunk: chunks[index],
89
+ chunkIndex: index,
90
+ totalChunks: chunks.length,
91
+ sha256: sha256(chunk),
92
+ chunk,
187
93
  };
188
94
  response = await this.#postRawJson(CHUNK_ENDPOINT, JSON.stringify(chunkBody));
189
95
  if (!response.ok || index === chunks.length - 1) {
@@ -193,7 +99,7 @@ export class ChunkRequest {
193
99
  throw new Error("No chunk response received from pi-server");
194
100
  }
195
101
  async #postRawJson(endpoint, rawJson) {
196
- return fetchWithHProxyApproval(`${this.options.serverUrl}${endpoint}`, {
102
+ return fetch(`${this.options.serverUrl}${endpoint}`, {
197
103
  method: "POST",
198
104
  headers: makeHeaders(this.options.authToken),
199
105
  body: rawJson,