@averyyy/pi-server 0.80.3-piclient.5 → 0.80.3-piclient.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -1
- package/bin/update.js +6 -170
- package/dist/pi-server-protocol.d.ts +13 -0
- package/dist/pi-server-protocol.d.ts.map +1 -0
- package/dist/pi-server-protocol.js +32 -0
- package/dist/pi-server-protocol.js.map +1 -0
- package/dist/request-chunks.d.ts +15 -5
- package/dist/request-chunks.d.ts.map +1 -1
- package/dist/request-chunks.js +123 -27
- package/dist/request-chunks.js.map +1 -1
- package/dist/server.d.ts +6 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +178 -24
- package/dist/server.js.map +1 -1
- package/dist/session-persistence.d.ts.map +1 -1
- package/dist/session-persistence.js +104 -3
- package/dist/session-persistence.js.map +1 -1
- package/dist/session-store.d.ts +10 -0
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +46 -12
- package/dist/session-store.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { buildSessionContext, convertToLlm, type SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport type { Message, Tool } from \"@earendil-works/pi-ai\";\n\nexport interface SessionStaticContext {\n\tsystemPrompt?: string;\n\ttools?: Tool[];\n}\n\nexport interface SessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tstaticContextHash: string;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tmessages: Message[];\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport interface SessionSummary {\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash: string;\n\tmessageCount: number;\n\tentryCount: number;\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport interface PersistedSessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nconst sessions = new Map<string, SessionState>();\n\nfunction hashStaticContext(ctx: SessionStaticContext | undefined): string {\n\tif (!ctx) return \"\";\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\nexport function getOrCreateSession(sessionId: string): SessionState {\n\tlet session = sessions.get(sessionId);\n\tif (!session) {\n\t\tconst now = Date.now();\n\t\tsession = {\n\t\t\tsessionId,\n\t\t\tstaticContext: undefined,\n\t\t\tstaticContextHash: \"\",\n\t\t\tentries: [],\n\t\t\tleafId: null,\n\t\t\tmessages: [],\n\t\t\trevision: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t};\n\t\tsessions.set(sessionId, session);\n\t}\n\treturn session;\n}\n\nexport function getSession(sessionId: string): SessionState | undefined {\n\treturn sessions.get(sessionId);\n}\n\nexport function hashSessionEntries(entries: SessionTreeEntry[]): string {\n\treturn createHash(\"sha256\").update(JSON.stringify(entries)).digest(\"hex\");\n}\n\nexport function listSessions(): SessionSummary[] {\n\treturn Array.from(sessions.values())\n\t\t.map((session) => ({\n\t\t\tsessionId: session.sessionId,\n\t\t\tstaticContextHash: session.staticContextHash,\n\t\t\ttreeHash: hashSessionEntries(session.entries),\n\t\t\tmessageCount: session.messages.length,\n\t\t\tentryCount: session.entries.length,\n\t\t\tleafId: session.leafId,\n\t\t\trevision: session.revision,\n\t\t\tcreatedAt: session.createdAt,\n\t\t\tupdatedAt: session.updatedAt,\n\t\t}))\n\t\t.sort((a, b) => b.updatedAt - a.updatedAt || a.sessionId.localeCompare(b.sessionId));\n}\n\nexport function exportSessionState(session: SessionState): PersistedSessionState {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tentries: session.entries.map((entry) => ({ ...entry })),\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tcreatedAt: session.createdAt,\n\t\tupdatedAt: session.updatedAt,\n\t};\n}\n\nexport function restoreSessionState(persisted: PersistedSessionState): SessionState {\n\tassertValidLeaf(persisted.entries, persisted.leafId);\n\tconst session: SessionState = {\n\t\tsessionId: persisted.sessionId,\n\t\tstaticContext: persisted.staticContext,\n\t\tstaticContextHash: hashStaticContext(persisted.staticContext),\n\t\tentries: persisted.entries.map((entry) => ({ ...entry })),\n\t\tleafId: persisted.leafId,\n\t\tmessages: [],\n\t\trevision: persisted.revision,\n\t\tcreatedAt: persisted.createdAt,\n\t\tupdatedAt: persisted.updatedAt,\n\t};\n\trefreshActiveMessages(session);\n\tsessions.set(session.sessionId, session);\n\treturn session;\n}\n\nexport function setStaticContext(sessionId: string, context: SessionStaticContext): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst newHash = hashStaticContext(context);\n\tsession.staticContext = context;\n\tsession.staticContextHash = newHash;\n\tsession.updatedAt = Date.now();\n\treturn session;\n}\n\nfunction entryToMessageEntry(message: Message, parentId: string | null): SessionTreeEntry {\n\treturn {\n\t\ttype: \"message\",\n\t\tid: randomUUID(),\n\t\tparentId,\n\t\ttimestamp: new Date(message.timestamp).toISOString(),\n\t\tmessage,\n\t};\n}\n\nexport function getSessionBranch(session: SessionState): SessionTreeEntry[] {\n\tconst byId = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst branch: SessionTreeEntry[] = [];\n\tlet current = session.leafId ? byId.get(session.leafId) : undefined;\n\tconst seen = new Set<string>();\n\twhile (current) {\n\t\tif (seen.has(current.id)) {\n\t\t\tthrow new Error(`session tree contains a parent cycle at entry ${current.id}`);\n\t\t}\n\t\tseen.add(current.id);\n\t\tbranch.unshift(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\treturn branch;\n}\n\nfunction deriveActiveMessages(session: SessionState): Message[] {\n\tconst branch = getSessionBranch(session);\n\treturn convertToLlm(buildSessionContext(branch).messages);\n}\n\nfunction refreshActiveMessages(session: SessionState): void {\n\tsession.messages = deriveActiveMessages(session);\n}\n\nfunction assertValidLeaf(entries: SessionTreeEntry[], leafId: string | null): void {\n\tif (leafId === null) return;\n\tif (!entries.some((entry) => entry.id === leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n}\n\nexport function replaceSessionTree(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tassertValidLeaf(entries, leafId);\n\tconst session = getOrCreateSession(sessionId);\n\tsession.entries = entries.map((entry) => ({ ...entry }));\n\tsession.leafId = leafId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\treturn session;\n}\n\nexport function appendSessionEntries(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst knownEntries = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst entriesToAppend: SessionTreeEntry[] = [];\n\tfor (const entry of entries) {\n\t\tconst knownEntry = knownEntries.get(entry.id);\n\t\tif (knownEntry) {\n\t\t\tif (!isDeepStrictEqual(knownEntry, entry)) {\n\t\t\t\tthrow new Error(`entry ${entry.id} already exists`);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.parentId !== null && !knownEntries.has(entry.parentId)) {\n\t\t\tthrow new Error(`parent entry ${entry.parentId} does not exist`);\n\t\t}\n\t\tconst entryToAppend = { ...entry };\n\t\tentriesToAppend.push(entryToAppend);\n\t\tknownEntries.set(entryToAppend.id, entryToAppend);\n\t}\n\tif (leafId !== null && !knownEntries.has(leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n\tif (entriesToAppend.length === 0 && session.leafId === leafId) {\n\t\treturn session;\n\t}\n\tsession.entries.push(...entriesToAppend);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\treturn session;\n}\n\nexport function switchSessionLeaf(sessionId: string, leafId: string | null): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\treturn session;\n}\n\nexport function appendCompactionEntry(\n\tsessionId: string,\n\tcompaction: { summary: string; firstKeptEntryId: string; tokensBefore: number; details?: unknown },\n): { session: SessionState; entry: SessionTreeEntry } {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entry: SessionTreeEntry = {\n\t\ttype: \"compaction\",\n\t\tid: randomUUID(),\n\t\tparentId: session.leafId,\n\t\ttimestamp: new Date().toISOString(),\n\t\tsummary: compaction.summary,\n\t\tfirstKeptEntryId: compaction.firstKeptEntryId,\n\t\ttokensBefore: compaction.tokensBefore,\n\t\tdetails: compaction.details,\n\t};\n\tsession.entries.push(entry);\n\tsession.leafId = entry.id;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\treturn { session, entry };\n}\n\nexport function getActiveMessages(sessionId: string): Message[] {\n\tconst session = getSession(sessionId);\n\treturn session ? [...session.messages] : [];\n}\n\nexport function appendMessages(sessionId: string, delta: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entries = delta.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, session.leafId);\n\t\tsession.leafId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.entries.push(...entries);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\treturn session;\n}\n\nexport function appendAssistantResponse(sessionId: string, message: Message): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tsession.messages.push(message);\n\tsession.updatedAt = Date.now();\n\treturn session;\n}\n\nexport function replaceMessages(sessionId: string, messages: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tlet parentId: string | null = null;\n\tsession.entries = messages.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, parentId);\n\t\tparentId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.leafId = parentId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\treturn session;\n}\n\nexport function dropLastAssistantError(sessionId: string): boolean {\n\tconst session = getSession(sessionId);\n\tif (!session) return false;\n\tconst leaf = session.leafId ? session.entries.find((entry) => entry.id === session.leafId) : undefined;\n\tif (leaf?.type !== \"message\" || leaf.message.role !== \"assistant\" || leaf.message.stopReason !== \"error\") {\n\t\treturn false;\n\t}\n\tsession.entries = session.entries.filter((entry) => entry.id !== leaf.id);\n\tsession.leafId = leaf.parentId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\treturn true;\n}\n\nexport function deleteSession(sessionId: string): boolean {\n\treturn sessions.delete(sessionId);\n}\n\nexport function clearAllSessions(): void {\n\tsessions.clear();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { buildSessionContext, convertToLlm, type SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport type { Message, Tool } from \"@earendil-works/pi-ai\";\nimport {\n\tappendPiServerTreeHash,\n\tbuildPiServerTreePrefixHashes,\n\thashPiServerSessionEntries,\n\thashPiServerStaticContext,\n\thashPiServerTreeEntry,\n} from \"./pi-server-protocol.ts\";\n\nexport interface SessionStaticContext {\n\tsystemPrompt?: string;\n\ttools?: Tool[];\n}\n\nexport interface SessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tstaticContextHash: string;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\ttreeHash: string;\n\tprefixHashes: string[];\n\tmessages: Message[];\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n\tpersistenceChange: SessionPersistenceChange | undefined;\n}\n\nexport interface SessionSummary {\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash: string;\n\tmessageCount: number;\n\tentryCount: number;\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport interface PersistedSessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport type SessionPersistenceChange = { kind: \"snapshot\" } | { kind: \"wal\"; entries: SessionTreeEntry[] };\n\nconst sessions = new Map<string, SessionState>();\n\nfunction hashStaticContext(ctx: SessionStaticContext | undefined): string {\n\treturn hashPiServerStaticContext(ctx);\n}\n\nexport function getOrCreateSession(sessionId: string): SessionState {\n\tlet session = sessions.get(sessionId);\n\tif (!session) {\n\t\tconst now = Date.now();\n\t\tsession = {\n\t\t\tsessionId,\n\t\t\tstaticContext: undefined,\n\t\t\tstaticContextHash: \"\",\n\t\t\tentries: [],\n\t\t\tleafId: null,\n\t\t\ttreeHash: hashPiServerSessionEntries([]),\n\t\t\tprefixHashes: buildPiServerTreePrefixHashes([]),\n\t\t\tmessages: [],\n\t\t\trevision: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t\tpersistenceChange: { kind: \"snapshot\" },\n\t\t};\n\t\tsessions.set(sessionId, session);\n\t}\n\treturn session;\n}\n\nexport function getSession(sessionId: string): SessionState | undefined {\n\treturn sessions.get(sessionId);\n}\n\nexport function hashSessionEntries(entries: SessionTreeEntry[]): string {\n\treturn hashPiServerSessionEntries(entries);\n}\n\nexport function listSessions(): SessionSummary[] {\n\treturn Array.from(sessions.values())\n\t\t.map((session) => ({\n\t\t\tsessionId: session.sessionId,\n\t\t\tstaticContextHash: session.staticContextHash,\n\t\t\ttreeHash: session.treeHash,\n\t\t\tmessageCount: session.messages.length,\n\t\t\tentryCount: session.entries.length,\n\t\t\tleafId: session.leafId,\n\t\t\trevision: session.revision,\n\t\t\tcreatedAt: session.createdAt,\n\t\t\tupdatedAt: session.updatedAt,\n\t\t}))\n\t\t.sort((a, b) => b.updatedAt - a.updatedAt || a.sessionId.localeCompare(b.sessionId));\n}\n\nexport function exportSessionState(session: SessionState): PersistedSessionState {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tentries: session.entries.map((entry) => ({ ...entry })),\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tcreatedAt: session.createdAt,\n\t\tupdatedAt: session.updatedAt,\n\t};\n}\n\nexport function restoreSessionState(persisted: PersistedSessionState): SessionState {\n\tassertValidLeaf(persisted.entries, persisted.leafId);\n\tconst session: SessionState = {\n\t\tsessionId: persisted.sessionId,\n\t\tstaticContext: persisted.staticContext,\n\t\tstaticContextHash: hashStaticContext(persisted.staticContext),\n\t\tentries: persisted.entries.map((entry) => ({ ...entry })),\n\t\tleafId: persisted.leafId,\n\t\ttreeHash: hashSessionEntries(persisted.entries),\n\t\tprefixHashes: buildPiServerTreePrefixHashes(persisted.entries),\n\t\tmessages: [],\n\t\trevision: persisted.revision,\n\t\tcreatedAt: persisted.createdAt,\n\t\tupdatedAt: persisted.updatedAt,\n\t\tpersistenceChange: undefined,\n\t};\n\trefreshActiveMessages(session);\n\tsessions.set(session.sessionId, session);\n\treturn session;\n}\n\nexport function setStaticContext(sessionId: string, context: SessionStaticContext): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst newHash = hashStaticContext(context);\n\tsession.staticContext = context;\n\tsession.staticContextHash = newHash;\n\tsession.updatedAt = Date.now();\n\tmarkWalPersistenceChange(session, []);\n\treturn session;\n}\n\nfunction entryToMessageEntry(message: Message, parentId: string | null): SessionTreeEntry {\n\treturn {\n\t\ttype: \"message\",\n\t\tid: randomUUID(),\n\t\tparentId,\n\t\ttimestamp: new Date(message.timestamp).toISOString(),\n\t\tmessage,\n\t};\n}\n\nexport function getSessionBranch(session: SessionState): SessionTreeEntry[] {\n\tconst byId = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst branch: SessionTreeEntry[] = [];\n\tlet current = session.leafId ? byId.get(session.leafId) : undefined;\n\tconst seen = new Set<string>();\n\twhile (current) {\n\t\tif (seen.has(current.id)) {\n\t\t\tthrow new Error(`session tree contains a parent cycle at entry ${current.id}`);\n\t\t}\n\t\tseen.add(current.id);\n\t\tbranch.unshift(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\treturn branch;\n}\n\nfunction deriveActiveMessages(session: SessionState): Message[] {\n\tconst branch = getSessionBranch(session);\n\treturn convertToLlm(buildSessionContext(branch).messages);\n}\n\nfunction refreshActiveMessages(session: SessionState): void {\n\tsession.messages = deriveActiveMessages(session);\n}\n\nfunction refreshTreeHashes(session: SessionState): void {\n\tsession.prefixHashes = buildPiServerTreePrefixHashes(session.entries);\n\tsession.treeHash = session.prefixHashes[session.prefixHashes.length - 1];\n}\n\nfunction appendTreeHashes(session: SessionState, entries: SessionTreeEntry[]): void {\n\tfor (const entry of entries) {\n\t\tsession.treeHash = appendPiServerTreeHash(session.treeHash, hashPiServerTreeEntry(entry));\n\t\tsession.prefixHashes.push(session.treeHash);\n\t}\n}\n\nfunction assertValidLeaf(entries: SessionTreeEntry[], leafId: string | null): void {\n\tif (leafId === null) return;\n\tif (!entries.some((entry) => entry.id === leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n}\n\nexport function replaceSessionTree(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tassertValidLeaf(entries, leafId);\n\tconst session = getOrCreateSession(sessionId);\n\tsession.entries = entries.map((entry) => ({ ...entry }));\n\tsession.leafId = leafId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn session;\n}\n\nexport function appendSessionEntries(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst knownEntries = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst entriesToAppend: SessionTreeEntry[] = [];\n\tfor (const entry of entries) {\n\t\tconst knownEntry = knownEntries.get(entry.id);\n\t\tif (knownEntry) {\n\t\t\tif (!isDeepStrictEqual(knownEntry, entry)) {\n\t\t\t\tthrow new Error(`entry ${entry.id} already exists`);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.parentId !== null && !knownEntries.has(entry.parentId)) {\n\t\t\tthrow new Error(`parent entry ${entry.parentId} does not exist`);\n\t\t}\n\t\tconst entryToAppend = { ...entry };\n\t\tentriesToAppend.push(entryToAppend);\n\t\tknownEntries.set(entryToAppend.id, entryToAppend);\n\t}\n\tif (leafId !== null && !knownEntries.has(leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n\tif (entriesToAppend.length === 0 && session.leafId === leafId) {\n\t\treturn session;\n\t}\n\tsession.entries.push(...entriesToAppend);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tappendTreeHashes(session, entriesToAppend);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, entriesToAppend);\n\treturn session;\n}\n\nexport function switchSessionLeaf(sessionId: string, leafId: string | null): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, []);\n\treturn session;\n}\n\nexport function appendCompactionEntry(\n\tsessionId: string,\n\tcompaction: { summary: string; firstKeptEntryId: string; tokensBefore: number; details?: unknown },\n): { session: SessionState; entry: SessionTreeEntry } {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entry: SessionTreeEntry = {\n\t\ttype: \"compaction\",\n\t\tid: randomUUID(),\n\t\tparentId: session.leafId,\n\t\ttimestamp: new Date().toISOString(),\n\t\tsummary: compaction.summary,\n\t\tfirstKeptEntryId: compaction.firstKeptEntryId,\n\t\ttokensBefore: compaction.tokensBefore,\n\t\tdetails: compaction.details,\n\t};\n\tsession.entries.push(entry);\n\tsession.leafId = entry.id;\n\tappendTreeHashes(session, [entry]);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, [entry]);\n\treturn { session, entry };\n}\n\nexport function getActiveMessages(sessionId: string): Message[] {\n\tconst session = getSession(sessionId);\n\treturn session ? [...session.messages] : [];\n}\n\nexport function appendMessages(sessionId: string, delta: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entries = delta.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, session.leafId);\n\t\tsession.leafId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.entries.push(...entries);\n\tappendTreeHashes(session, entries);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, entries);\n\treturn session;\n}\n\nexport function appendAssistantResponse(sessionId: string, message: Message): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tsession.messages.push(message);\n\tsession.updatedAt = Date.now();\n\treturn session;\n}\n\nexport function replaceMessages(sessionId: string, messages: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tlet parentId: string | null = null;\n\tsession.entries = messages.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, parentId);\n\t\tparentId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.leafId = parentId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn session;\n}\n\nexport function dropLastAssistantError(sessionId: string): boolean {\n\tconst session = getSession(sessionId);\n\tif (!session) return false;\n\tconst leaf = session.leafId ? session.entries.find((entry) => entry.id === session.leafId) : undefined;\n\tif (leaf?.type !== \"message\" || leaf.message.role !== \"assistant\" || leaf.message.stopReason !== \"error\") {\n\t\treturn false;\n\t}\n\tsession.entries = session.entries.filter((entry) => entry.id !== leaf.id);\n\tsession.leafId = leaf.parentId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn true;\n}\n\nexport function deleteSession(sessionId: string): boolean {\n\treturn sessions.delete(sessionId);\n}\n\nexport function clearAllSessions(): void {\n\tsessions.clear();\n}\n\nfunction markWalPersistenceChange(session: SessionState, entries: SessionTreeEntry[]): void {\n\tif (session.persistenceChange?.kind === \"snapshot\") return;\n\tsession.persistenceChange = {\n\t\tkind: \"wal\",\n\t\tentries: [...(session.persistenceChange?.entries ?? []), ...entries.map((entry) => ({ ...entry }))],\n\t};\n}\n\nexport function markSessionPersisted(session: SessionState): void {\n\tsession.persistenceChange = undefined;\n}\n"]}
|
package/dist/session-store.js
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { isDeepStrictEqual } from "node:util";
|
|
3
3
|
import { buildSessionContext, convertToLlm } from "@earendil-works/pi-agent-core";
|
|
4
|
+
import { appendPiServerTreeHash, buildPiServerTreePrefixHashes, hashPiServerSessionEntries, hashPiServerStaticContext, hashPiServerTreeEntry, } from "./pi-server-protocol.js";
|
|
4
5
|
const sessions = new Map();
|
|
5
6
|
function hashStaticContext(ctx) {
|
|
6
|
-
|
|
7
|
-
return "";
|
|
8
|
-
const parts = [];
|
|
9
|
-
if (ctx.systemPrompt !== undefined)
|
|
10
|
-
parts.push(`sp:${ctx.systemPrompt}`);
|
|
11
|
-
if (ctx.tools) {
|
|
12
|
-
parts.push(`t:${JSON.stringify(ctx.tools.map((t) => ({ name: t.name, description: t.description, parameters: t.parameters })))}`);
|
|
13
|
-
}
|
|
14
|
-
return parts.join("|");
|
|
7
|
+
return hashPiServerStaticContext(ctx);
|
|
15
8
|
}
|
|
16
9
|
export function getOrCreateSession(sessionId) {
|
|
17
10
|
let session = sessions.get(sessionId);
|
|
@@ -23,10 +16,13 @@ export function getOrCreateSession(sessionId) {
|
|
|
23
16
|
staticContextHash: "",
|
|
24
17
|
entries: [],
|
|
25
18
|
leafId: null,
|
|
19
|
+
treeHash: hashPiServerSessionEntries([]),
|
|
20
|
+
prefixHashes: buildPiServerTreePrefixHashes([]),
|
|
26
21
|
messages: [],
|
|
27
22
|
revision: 0,
|
|
28
23
|
createdAt: now,
|
|
29
24
|
updatedAt: now,
|
|
25
|
+
persistenceChange: { kind: "snapshot" },
|
|
30
26
|
};
|
|
31
27
|
sessions.set(sessionId, session);
|
|
32
28
|
}
|
|
@@ -36,14 +32,14 @@ export function getSession(sessionId) {
|
|
|
36
32
|
return sessions.get(sessionId);
|
|
37
33
|
}
|
|
38
34
|
export function hashSessionEntries(entries) {
|
|
39
|
-
return
|
|
35
|
+
return hashPiServerSessionEntries(entries);
|
|
40
36
|
}
|
|
41
37
|
export function listSessions() {
|
|
42
38
|
return Array.from(sessions.values())
|
|
43
39
|
.map((session) => ({
|
|
44
40
|
sessionId: session.sessionId,
|
|
45
41
|
staticContextHash: session.staticContextHash,
|
|
46
|
-
treeHash:
|
|
42
|
+
treeHash: session.treeHash,
|
|
47
43
|
messageCount: session.messages.length,
|
|
48
44
|
entryCount: session.entries.length,
|
|
49
45
|
leafId: session.leafId,
|
|
@@ -72,10 +68,13 @@ export function restoreSessionState(persisted) {
|
|
|
72
68
|
staticContextHash: hashStaticContext(persisted.staticContext),
|
|
73
69
|
entries: persisted.entries.map((entry) => ({ ...entry })),
|
|
74
70
|
leafId: persisted.leafId,
|
|
71
|
+
treeHash: hashSessionEntries(persisted.entries),
|
|
72
|
+
prefixHashes: buildPiServerTreePrefixHashes(persisted.entries),
|
|
75
73
|
messages: [],
|
|
76
74
|
revision: persisted.revision,
|
|
77
75
|
createdAt: persisted.createdAt,
|
|
78
76
|
updatedAt: persisted.updatedAt,
|
|
77
|
+
persistenceChange: undefined,
|
|
79
78
|
};
|
|
80
79
|
refreshActiveMessages(session);
|
|
81
80
|
sessions.set(session.sessionId, session);
|
|
@@ -87,6 +86,7 @@ export function setStaticContext(sessionId, context) {
|
|
|
87
86
|
session.staticContext = context;
|
|
88
87
|
session.staticContextHash = newHash;
|
|
89
88
|
session.updatedAt = Date.now();
|
|
89
|
+
markWalPersistenceChange(session, []);
|
|
90
90
|
return session;
|
|
91
91
|
}
|
|
92
92
|
function entryToMessageEntry(message, parentId) {
|
|
@@ -120,6 +120,16 @@ function deriveActiveMessages(session) {
|
|
|
120
120
|
function refreshActiveMessages(session) {
|
|
121
121
|
session.messages = deriveActiveMessages(session);
|
|
122
122
|
}
|
|
123
|
+
function refreshTreeHashes(session) {
|
|
124
|
+
session.prefixHashes = buildPiServerTreePrefixHashes(session.entries);
|
|
125
|
+
session.treeHash = session.prefixHashes[session.prefixHashes.length - 1];
|
|
126
|
+
}
|
|
127
|
+
function appendTreeHashes(session, entries) {
|
|
128
|
+
for (const entry of entries) {
|
|
129
|
+
session.treeHash = appendPiServerTreeHash(session.treeHash, hashPiServerTreeEntry(entry));
|
|
130
|
+
session.prefixHashes.push(session.treeHash);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
123
133
|
function assertValidLeaf(entries, leafId) {
|
|
124
134
|
if (leafId === null)
|
|
125
135
|
return;
|
|
@@ -132,9 +142,11 @@ export function replaceSessionTree(sessionId, entries, leafId) {
|
|
|
132
142
|
const session = getOrCreateSession(sessionId);
|
|
133
143
|
session.entries = entries.map((entry) => ({ ...entry }));
|
|
134
144
|
session.leafId = leafId;
|
|
145
|
+
refreshTreeHashes(session);
|
|
135
146
|
session.revision++;
|
|
136
147
|
session.updatedAt = Date.now();
|
|
137
148
|
refreshActiveMessages(session);
|
|
149
|
+
session.persistenceChange = { kind: "snapshot" };
|
|
138
150
|
return session;
|
|
139
151
|
}
|
|
140
152
|
export function appendSessionEntries(sessionId, entries, leafId) {
|
|
@@ -165,9 +177,11 @@ export function appendSessionEntries(sessionId, entries, leafId) {
|
|
|
165
177
|
session.entries.push(...entriesToAppend);
|
|
166
178
|
assertValidLeaf(session.entries, leafId);
|
|
167
179
|
session.leafId = leafId;
|
|
180
|
+
appendTreeHashes(session, entriesToAppend);
|
|
168
181
|
session.revision++;
|
|
169
182
|
session.updatedAt = Date.now();
|
|
170
183
|
refreshActiveMessages(session);
|
|
184
|
+
markWalPersistenceChange(session, entriesToAppend);
|
|
171
185
|
return session;
|
|
172
186
|
}
|
|
173
187
|
export function switchSessionLeaf(sessionId, leafId) {
|
|
@@ -177,6 +191,7 @@ export function switchSessionLeaf(sessionId, leafId) {
|
|
|
177
191
|
session.revision++;
|
|
178
192
|
session.updatedAt = Date.now();
|
|
179
193
|
refreshActiveMessages(session);
|
|
194
|
+
markWalPersistenceChange(session, []);
|
|
180
195
|
return session;
|
|
181
196
|
}
|
|
182
197
|
export function appendCompactionEntry(sessionId, compaction) {
|
|
@@ -193,9 +208,11 @@ export function appendCompactionEntry(sessionId, compaction) {
|
|
|
193
208
|
};
|
|
194
209
|
session.entries.push(entry);
|
|
195
210
|
session.leafId = entry.id;
|
|
211
|
+
appendTreeHashes(session, [entry]);
|
|
196
212
|
session.revision++;
|
|
197
213
|
session.updatedAt = Date.now();
|
|
198
214
|
refreshActiveMessages(session);
|
|
215
|
+
markWalPersistenceChange(session, [entry]);
|
|
199
216
|
return { session, entry };
|
|
200
217
|
}
|
|
201
218
|
export function getActiveMessages(sessionId) {
|
|
@@ -210,9 +227,11 @@ export function appendMessages(sessionId, delta) {
|
|
|
210
227
|
return entry;
|
|
211
228
|
});
|
|
212
229
|
session.entries.push(...entries);
|
|
230
|
+
appendTreeHashes(session, entries);
|
|
213
231
|
session.revision++;
|
|
214
232
|
session.updatedAt = Date.now();
|
|
215
233
|
refreshActiveMessages(session);
|
|
234
|
+
markWalPersistenceChange(session, entries);
|
|
216
235
|
return session;
|
|
217
236
|
}
|
|
218
237
|
export function appendAssistantResponse(sessionId, message) {
|
|
@@ -230,9 +249,11 @@ export function replaceMessages(sessionId, messages) {
|
|
|
230
249
|
return entry;
|
|
231
250
|
});
|
|
232
251
|
session.leafId = parentId;
|
|
252
|
+
refreshTreeHashes(session);
|
|
233
253
|
session.revision++;
|
|
234
254
|
session.updatedAt = Date.now();
|
|
235
255
|
refreshActiveMessages(session);
|
|
256
|
+
session.persistenceChange = { kind: "snapshot" };
|
|
236
257
|
return session;
|
|
237
258
|
}
|
|
238
259
|
export function dropLastAssistantError(sessionId) {
|
|
@@ -245,9 +266,11 @@ export function dropLastAssistantError(sessionId) {
|
|
|
245
266
|
}
|
|
246
267
|
session.entries = session.entries.filter((entry) => entry.id !== leaf.id);
|
|
247
268
|
session.leafId = leaf.parentId;
|
|
269
|
+
refreshTreeHashes(session);
|
|
248
270
|
session.revision++;
|
|
249
271
|
session.updatedAt = Date.now();
|
|
250
272
|
refreshActiveMessages(session);
|
|
273
|
+
session.persistenceChange = { kind: "snapshot" };
|
|
251
274
|
return true;
|
|
252
275
|
}
|
|
253
276
|
export function deleteSession(sessionId) {
|
|
@@ -256,4 +279,15 @@ export function deleteSession(sessionId) {
|
|
|
256
279
|
export function clearAllSessions() {
|
|
257
280
|
sessions.clear();
|
|
258
281
|
}
|
|
282
|
+
function markWalPersistenceChange(session, entries) {
|
|
283
|
+
if (session.persistenceChange?.kind === "snapshot")
|
|
284
|
+
return;
|
|
285
|
+
session.persistenceChange = {
|
|
286
|
+
kind: "wal",
|
|
287
|
+
entries: [...(session.persistenceChange?.entries ?? []), ...entries.map((entry) => ({ ...entry }))],
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
export function markSessionPersisted(session) {
|
|
291
|
+
session.persistenceChange = undefined;
|
|
292
|
+
}
|
|
259
293
|
//# sourceMappingURL=session-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-store.js","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { buildSessionContext, convertToLlm, type SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport type { Message, Tool } from \"@earendil-works/pi-ai\";\n\nexport interface SessionStaticContext {\n\tsystemPrompt?: string;\n\ttools?: Tool[];\n}\n\nexport interface SessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tstaticContextHash: string;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\tmessages: Message[];\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport interface SessionSummary {\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash: string;\n\tmessageCount: number;\n\tentryCount: number;\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport interface PersistedSessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nconst sessions = new Map<string, SessionState>();\n\nfunction hashStaticContext(ctx: SessionStaticContext | undefined): string {\n\tif (!ctx) return \"\";\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\nexport function getOrCreateSession(sessionId: string): SessionState {\n\tlet session = sessions.get(sessionId);\n\tif (!session) {\n\t\tconst now = Date.now();\n\t\tsession = {\n\t\t\tsessionId,\n\t\t\tstaticContext: undefined,\n\t\t\tstaticContextHash: \"\",\n\t\t\tentries: [],\n\t\t\tleafId: null,\n\t\t\tmessages: [],\n\t\t\trevision: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t};\n\t\tsessions.set(sessionId, session);\n\t}\n\treturn session;\n}\n\nexport function getSession(sessionId: string): SessionState | undefined {\n\treturn sessions.get(sessionId);\n}\n\nexport function hashSessionEntries(entries: SessionTreeEntry[]): string {\n\treturn createHash(\"sha256\").update(JSON.stringify(entries)).digest(\"hex\");\n}\n\nexport function listSessions(): SessionSummary[] {\n\treturn Array.from(sessions.values())\n\t\t.map((session) => ({\n\t\t\tsessionId: session.sessionId,\n\t\t\tstaticContextHash: session.staticContextHash,\n\t\t\ttreeHash: hashSessionEntries(session.entries),\n\t\t\tmessageCount: session.messages.length,\n\t\t\tentryCount: session.entries.length,\n\t\t\tleafId: session.leafId,\n\t\t\trevision: session.revision,\n\t\t\tcreatedAt: session.createdAt,\n\t\t\tupdatedAt: session.updatedAt,\n\t\t}))\n\t\t.sort((a, b) => b.updatedAt - a.updatedAt || a.sessionId.localeCompare(b.sessionId));\n}\n\nexport function exportSessionState(session: SessionState): PersistedSessionState {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tentries: session.entries.map((entry) => ({ ...entry })),\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tcreatedAt: session.createdAt,\n\t\tupdatedAt: session.updatedAt,\n\t};\n}\n\nexport function restoreSessionState(persisted: PersistedSessionState): SessionState {\n\tassertValidLeaf(persisted.entries, persisted.leafId);\n\tconst session: SessionState = {\n\t\tsessionId: persisted.sessionId,\n\t\tstaticContext: persisted.staticContext,\n\t\tstaticContextHash: hashStaticContext(persisted.staticContext),\n\t\tentries: persisted.entries.map((entry) => ({ ...entry })),\n\t\tleafId: persisted.leafId,\n\t\tmessages: [],\n\t\trevision: persisted.revision,\n\t\tcreatedAt: persisted.createdAt,\n\t\tupdatedAt: persisted.updatedAt,\n\t};\n\trefreshActiveMessages(session);\n\tsessions.set(session.sessionId, session);\n\treturn session;\n}\n\nexport function setStaticContext(sessionId: string, context: SessionStaticContext): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst newHash = hashStaticContext(context);\n\tsession.staticContext = context;\n\tsession.staticContextHash = newHash;\n\tsession.updatedAt = Date.now();\n\treturn session;\n}\n\nfunction entryToMessageEntry(message: Message, parentId: string | null): SessionTreeEntry {\n\treturn {\n\t\ttype: \"message\",\n\t\tid: randomUUID(),\n\t\tparentId,\n\t\ttimestamp: new Date(message.timestamp).toISOString(),\n\t\tmessage,\n\t};\n}\n\nexport function getSessionBranch(session: SessionState): SessionTreeEntry[] {\n\tconst byId = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst branch: SessionTreeEntry[] = [];\n\tlet current = session.leafId ? byId.get(session.leafId) : undefined;\n\tconst seen = new Set<string>();\n\twhile (current) {\n\t\tif (seen.has(current.id)) {\n\t\t\tthrow new Error(`session tree contains a parent cycle at entry ${current.id}`);\n\t\t}\n\t\tseen.add(current.id);\n\t\tbranch.unshift(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\treturn branch;\n}\n\nfunction deriveActiveMessages(session: SessionState): Message[] {\n\tconst branch = getSessionBranch(session);\n\treturn convertToLlm(buildSessionContext(branch).messages);\n}\n\nfunction refreshActiveMessages(session: SessionState): void {\n\tsession.messages = deriveActiveMessages(session);\n}\n\nfunction assertValidLeaf(entries: SessionTreeEntry[], leafId: string | null): void {\n\tif (leafId === null) return;\n\tif (!entries.some((entry) => entry.id === leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n}\n\nexport function replaceSessionTree(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tassertValidLeaf(entries, leafId);\n\tconst session = getOrCreateSession(sessionId);\n\tsession.entries = entries.map((entry) => ({ ...entry }));\n\tsession.leafId = leafId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\treturn session;\n}\n\nexport function appendSessionEntries(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst knownEntries = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst entriesToAppend: SessionTreeEntry[] = [];\n\tfor (const entry of entries) {\n\t\tconst knownEntry = knownEntries.get(entry.id);\n\t\tif (knownEntry) {\n\t\t\tif (!isDeepStrictEqual(knownEntry, entry)) {\n\t\t\t\tthrow new Error(`entry ${entry.id} already exists`);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.parentId !== null && !knownEntries.has(entry.parentId)) {\n\t\t\tthrow new Error(`parent entry ${entry.parentId} does not exist`);\n\t\t}\n\t\tconst entryToAppend = { ...entry };\n\t\tentriesToAppend.push(entryToAppend);\n\t\tknownEntries.set(entryToAppend.id, entryToAppend);\n\t}\n\tif (leafId !== null && !knownEntries.has(leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n\tif (entriesToAppend.length === 0 && session.leafId === leafId) {\n\t\treturn session;\n\t}\n\tsession.entries.push(...entriesToAppend);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\treturn session;\n}\n\nexport function switchSessionLeaf(sessionId: string, leafId: string | null): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\treturn session;\n}\n\nexport function appendCompactionEntry(\n\tsessionId: string,\n\tcompaction: { summary: string; firstKeptEntryId: string; tokensBefore: number; details?: unknown },\n): { session: SessionState; entry: SessionTreeEntry } {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entry: SessionTreeEntry = {\n\t\ttype: \"compaction\",\n\t\tid: randomUUID(),\n\t\tparentId: session.leafId,\n\t\ttimestamp: new Date().toISOString(),\n\t\tsummary: compaction.summary,\n\t\tfirstKeptEntryId: compaction.firstKeptEntryId,\n\t\ttokensBefore: compaction.tokensBefore,\n\t\tdetails: compaction.details,\n\t};\n\tsession.entries.push(entry);\n\tsession.leafId = entry.id;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\treturn { session, entry };\n}\n\nexport function getActiveMessages(sessionId: string): Message[] {\n\tconst session = getSession(sessionId);\n\treturn session ? [...session.messages] : [];\n}\n\nexport function appendMessages(sessionId: string, delta: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entries = delta.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, session.leafId);\n\t\tsession.leafId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.entries.push(...entries);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\treturn session;\n}\n\nexport function appendAssistantResponse(sessionId: string, message: Message): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tsession.messages.push(message);\n\tsession.updatedAt = Date.now();\n\treturn session;\n}\n\nexport function replaceMessages(sessionId: string, messages: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tlet parentId: string | null = null;\n\tsession.entries = messages.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, parentId);\n\t\tparentId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.leafId = parentId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\treturn session;\n}\n\nexport function dropLastAssistantError(sessionId: string): boolean {\n\tconst session = getSession(sessionId);\n\tif (!session) return false;\n\tconst leaf = session.leafId ? session.entries.find((entry) => entry.id === session.leafId) : undefined;\n\tif (leaf?.type !== \"message\" || leaf.message.role !== \"assistant\" || leaf.message.stopReason !== \"error\") {\n\t\treturn false;\n\t}\n\tsession.entries = session.entries.filter((entry) => entry.id !== leaf.id);\n\tsession.leafId = leaf.parentId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\treturn true;\n}\n\nexport function deleteSession(sessionId: string): boolean {\n\treturn sessions.delete(sessionId);\n}\n\nexport function clearAllSessions(): void {\n\tsessions.clear();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-store.js","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { buildSessionContext, convertToLlm, type SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport type { Message, Tool } from \"@earendil-works/pi-ai\";\nimport {\n\tappendPiServerTreeHash,\n\tbuildPiServerTreePrefixHashes,\n\thashPiServerSessionEntries,\n\thashPiServerStaticContext,\n\thashPiServerTreeEntry,\n} from \"./pi-server-protocol.ts\";\n\nexport interface SessionStaticContext {\n\tsystemPrompt?: string;\n\ttools?: Tool[];\n}\n\nexport interface SessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tstaticContextHash: string;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\ttreeHash: string;\n\tprefixHashes: string[];\n\tmessages: Message[];\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n\tpersistenceChange: SessionPersistenceChange | undefined;\n}\n\nexport interface SessionSummary {\n\tsessionId: string;\n\tstaticContextHash: string;\n\ttreeHash: string;\n\tmessageCount: number;\n\tentryCount: number;\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport interface PersistedSessionState {\n\tsessionId: string;\n\tstaticContext: SessionStaticContext | undefined;\n\tentries: SessionTreeEntry[];\n\tleafId: string | null;\n\trevision: number;\n\tcreatedAt: number;\n\tupdatedAt: number;\n}\n\nexport type SessionPersistenceChange = { kind: \"snapshot\" } | { kind: \"wal\"; entries: SessionTreeEntry[] };\n\nconst sessions = new Map<string, SessionState>();\n\nfunction hashStaticContext(ctx: SessionStaticContext | undefined): string {\n\treturn hashPiServerStaticContext(ctx);\n}\n\nexport function getOrCreateSession(sessionId: string): SessionState {\n\tlet session = sessions.get(sessionId);\n\tif (!session) {\n\t\tconst now = Date.now();\n\t\tsession = {\n\t\t\tsessionId,\n\t\t\tstaticContext: undefined,\n\t\t\tstaticContextHash: \"\",\n\t\t\tentries: [],\n\t\t\tleafId: null,\n\t\t\ttreeHash: hashPiServerSessionEntries([]),\n\t\t\tprefixHashes: buildPiServerTreePrefixHashes([]),\n\t\t\tmessages: [],\n\t\t\trevision: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t\tpersistenceChange: { kind: \"snapshot\" },\n\t\t};\n\t\tsessions.set(sessionId, session);\n\t}\n\treturn session;\n}\n\nexport function getSession(sessionId: string): SessionState | undefined {\n\treturn sessions.get(sessionId);\n}\n\nexport function hashSessionEntries(entries: SessionTreeEntry[]): string {\n\treturn hashPiServerSessionEntries(entries);\n}\n\nexport function listSessions(): SessionSummary[] {\n\treturn Array.from(sessions.values())\n\t\t.map((session) => ({\n\t\t\tsessionId: session.sessionId,\n\t\t\tstaticContextHash: session.staticContextHash,\n\t\t\ttreeHash: session.treeHash,\n\t\t\tmessageCount: session.messages.length,\n\t\t\tentryCount: session.entries.length,\n\t\t\tleafId: session.leafId,\n\t\t\trevision: session.revision,\n\t\t\tcreatedAt: session.createdAt,\n\t\t\tupdatedAt: session.updatedAt,\n\t\t}))\n\t\t.sort((a, b) => b.updatedAt - a.updatedAt || a.sessionId.localeCompare(b.sessionId));\n}\n\nexport function exportSessionState(session: SessionState): PersistedSessionState {\n\treturn {\n\t\tsessionId: session.sessionId,\n\t\tstaticContext: session.staticContext,\n\t\tentries: session.entries.map((entry) => ({ ...entry })),\n\t\tleafId: session.leafId,\n\t\trevision: session.revision,\n\t\tcreatedAt: session.createdAt,\n\t\tupdatedAt: session.updatedAt,\n\t};\n}\n\nexport function restoreSessionState(persisted: PersistedSessionState): SessionState {\n\tassertValidLeaf(persisted.entries, persisted.leafId);\n\tconst session: SessionState = {\n\t\tsessionId: persisted.sessionId,\n\t\tstaticContext: persisted.staticContext,\n\t\tstaticContextHash: hashStaticContext(persisted.staticContext),\n\t\tentries: persisted.entries.map((entry) => ({ ...entry })),\n\t\tleafId: persisted.leafId,\n\t\ttreeHash: hashSessionEntries(persisted.entries),\n\t\tprefixHashes: buildPiServerTreePrefixHashes(persisted.entries),\n\t\tmessages: [],\n\t\trevision: persisted.revision,\n\t\tcreatedAt: persisted.createdAt,\n\t\tupdatedAt: persisted.updatedAt,\n\t\tpersistenceChange: undefined,\n\t};\n\trefreshActiveMessages(session);\n\tsessions.set(session.sessionId, session);\n\treturn session;\n}\n\nexport function setStaticContext(sessionId: string, context: SessionStaticContext): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst newHash = hashStaticContext(context);\n\tsession.staticContext = context;\n\tsession.staticContextHash = newHash;\n\tsession.updatedAt = Date.now();\n\tmarkWalPersistenceChange(session, []);\n\treturn session;\n}\n\nfunction entryToMessageEntry(message: Message, parentId: string | null): SessionTreeEntry {\n\treturn {\n\t\ttype: \"message\",\n\t\tid: randomUUID(),\n\t\tparentId,\n\t\ttimestamp: new Date(message.timestamp).toISOString(),\n\t\tmessage,\n\t};\n}\n\nexport function getSessionBranch(session: SessionState): SessionTreeEntry[] {\n\tconst byId = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst branch: SessionTreeEntry[] = [];\n\tlet current = session.leafId ? byId.get(session.leafId) : undefined;\n\tconst seen = new Set<string>();\n\twhile (current) {\n\t\tif (seen.has(current.id)) {\n\t\t\tthrow new Error(`session tree contains a parent cycle at entry ${current.id}`);\n\t\t}\n\t\tseen.add(current.id);\n\t\tbranch.unshift(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\treturn branch;\n}\n\nfunction deriveActiveMessages(session: SessionState): Message[] {\n\tconst branch = getSessionBranch(session);\n\treturn convertToLlm(buildSessionContext(branch).messages);\n}\n\nfunction refreshActiveMessages(session: SessionState): void {\n\tsession.messages = deriveActiveMessages(session);\n}\n\nfunction refreshTreeHashes(session: SessionState): void {\n\tsession.prefixHashes = buildPiServerTreePrefixHashes(session.entries);\n\tsession.treeHash = session.prefixHashes[session.prefixHashes.length - 1];\n}\n\nfunction appendTreeHashes(session: SessionState, entries: SessionTreeEntry[]): void {\n\tfor (const entry of entries) {\n\t\tsession.treeHash = appendPiServerTreeHash(session.treeHash, hashPiServerTreeEntry(entry));\n\t\tsession.prefixHashes.push(session.treeHash);\n\t}\n}\n\nfunction assertValidLeaf(entries: SessionTreeEntry[], leafId: string | null): void {\n\tif (leafId === null) return;\n\tif (!entries.some((entry) => entry.id === leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n}\n\nexport function replaceSessionTree(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tassertValidLeaf(entries, leafId);\n\tconst session = getOrCreateSession(sessionId);\n\tsession.entries = entries.map((entry) => ({ ...entry }));\n\tsession.leafId = leafId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn session;\n}\n\nexport function appendSessionEntries(\n\tsessionId: string,\n\tentries: SessionTreeEntry[],\n\tleafId: string | null,\n): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst knownEntries = new Map(session.entries.map((entry) => [entry.id, entry]));\n\tconst entriesToAppend: SessionTreeEntry[] = [];\n\tfor (const entry of entries) {\n\t\tconst knownEntry = knownEntries.get(entry.id);\n\t\tif (knownEntry) {\n\t\t\tif (!isDeepStrictEqual(knownEntry, entry)) {\n\t\t\t\tthrow new Error(`entry ${entry.id} already exists`);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.parentId !== null && !knownEntries.has(entry.parentId)) {\n\t\t\tthrow new Error(`parent entry ${entry.parentId} does not exist`);\n\t\t}\n\t\tconst entryToAppend = { ...entry };\n\t\tentriesToAppend.push(entryToAppend);\n\t\tknownEntries.set(entryToAppend.id, entryToAppend);\n\t}\n\tif (leafId !== null && !knownEntries.has(leafId)) {\n\t\tthrow new Error(`leafId ${leafId} does not exist in session tree`);\n\t}\n\tif (entriesToAppend.length === 0 && session.leafId === leafId) {\n\t\treturn session;\n\t}\n\tsession.entries.push(...entriesToAppend);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tappendTreeHashes(session, entriesToAppend);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, entriesToAppend);\n\treturn session;\n}\n\nexport function switchSessionLeaf(sessionId: string, leafId: string | null): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tassertValidLeaf(session.entries, leafId);\n\tsession.leafId = leafId;\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, []);\n\treturn session;\n}\n\nexport function appendCompactionEntry(\n\tsessionId: string,\n\tcompaction: { summary: string; firstKeptEntryId: string; tokensBefore: number; details?: unknown },\n): { session: SessionState; entry: SessionTreeEntry } {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entry: SessionTreeEntry = {\n\t\ttype: \"compaction\",\n\t\tid: randomUUID(),\n\t\tparentId: session.leafId,\n\t\ttimestamp: new Date().toISOString(),\n\t\tsummary: compaction.summary,\n\t\tfirstKeptEntryId: compaction.firstKeptEntryId,\n\t\ttokensBefore: compaction.tokensBefore,\n\t\tdetails: compaction.details,\n\t};\n\tsession.entries.push(entry);\n\tsession.leafId = entry.id;\n\tappendTreeHashes(session, [entry]);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, [entry]);\n\treturn { session, entry };\n}\n\nexport function getActiveMessages(sessionId: string): Message[] {\n\tconst session = getSession(sessionId);\n\treturn session ? [...session.messages] : [];\n}\n\nexport function appendMessages(sessionId: string, delta: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tconst entries = delta.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, session.leafId);\n\t\tsession.leafId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.entries.push(...entries);\n\tappendTreeHashes(session, entries);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tmarkWalPersistenceChange(session, entries);\n\treturn session;\n}\n\nexport function appendAssistantResponse(sessionId: string, message: Message): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tsession.messages.push(message);\n\tsession.updatedAt = Date.now();\n\treturn session;\n}\n\nexport function replaceMessages(sessionId: string, messages: Message[]): SessionState {\n\tconst session = getOrCreateSession(sessionId);\n\tlet parentId: string | null = null;\n\tsession.entries = messages.map((message) => {\n\t\tconst entry = entryToMessageEntry(message, parentId);\n\t\tparentId = entry.id;\n\t\treturn entry;\n\t});\n\tsession.leafId = parentId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn session;\n}\n\nexport function dropLastAssistantError(sessionId: string): boolean {\n\tconst session = getSession(sessionId);\n\tif (!session) return false;\n\tconst leaf = session.leafId ? session.entries.find((entry) => entry.id === session.leafId) : undefined;\n\tif (leaf?.type !== \"message\" || leaf.message.role !== \"assistant\" || leaf.message.stopReason !== \"error\") {\n\t\treturn false;\n\t}\n\tsession.entries = session.entries.filter((entry) => entry.id !== leaf.id);\n\tsession.leafId = leaf.parentId;\n\trefreshTreeHashes(session);\n\tsession.revision++;\n\tsession.updatedAt = Date.now();\n\trefreshActiveMessages(session);\n\tsession.persistenceChange = { kind: \"snapshot\" };\n\treturn true;\n}\n\nexport function deleteSession(sessionId: string): boolean {\n\treturn sessions.delete(sessionId);\n}\n\nexport function clearAllSessions(): void {\n\tsessions.clear();\n}\n\nfunction markWalPersistenceChange(session: SessionState, entries: SessionTreeEntry[]): void {\n\tif (session.persistenceChange?.kind === \"snapshot\") return;\n\tsession.persistenceChange = {\n\t\tkind: \"wal\",\n\t\tentries: [...(session.persistenceChange?.entries ?? []), ...entries.map((entry) => ({ ...entry }))],\n\t};\n}\n\nexport function markSessionPersisted(session: SessionState): void {\n\tsession.persistenceChange = undefined;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@averyyy/pi-server",
|
|
3
|
-
"version": "0.80.3-piclient.
|
|
3
|
+
"version": "0.80.3-piclient.7",
|
|
4
4
|
"description": "Proxy server for pi-client that stores session state and forwards incremental requests to upstream LLM providers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"prepublishOnly": "npm run clean && npm run build"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@earendil-works/pi-ai": "npm:@averyyy/pi-ai@0.80.3-piclient.
|
|
31
|
-
"@earendil-works/pi-agent-core": "npm:@averyyy/pi-agent-core@0.80.3-piclient.
|
|
30
|
+
"@earendil-works/pi-ai": "npm:@averyyy/pi-ai@0.80.3-piclient.7",
|
|
31
|
+
"@earendil-works/pi-agent-core": "npm:@averyyy/pi-agent-core@0.80.3-piclient.7"
|
|
32
32
|
},
|
|
33
33
|
"keywords": [
|
|
34
34
|
"pi-server",
|