@averyyy/pi-server 0.86.1-piclient.2 → 0.87.0-piclient.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { type SessionTreeEntry } from "@earendil-works/pi-agent-core";
2
- import type { Message, Tool } from "@earendil-works/pi-ai";
2
+ import { type Message, type Tool } from "@earendil-works/pi-ai";
3
3
  export interface SessionStaticContext {
4
4
  systemPrompt?: string;
5
5
  tools?: Tool[];
@@ -1 +1 @@
1
- {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { buildLegacySessionContext, convertToLlm, type SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport type { Message, Tool } from \"@earendil-works/pi-ai\";\nimport { PiServerError, PiServerErrorCode } from \"./error-codes.ts\";\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\tif (session.staticContextHash === newHash) return session;\n\tsession.staticContext = context;\n\tsession.staticContextHash = newHash;\n\tsession.revision++;\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.push(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\tbranch.reverse();\n\treturn branch;\n}\n\nfunction deriveActiveMessages(session: SessionState): Message[] {\n\tconst branch = getSessionBranch(session);\n\treturn convertToLlm(buildLegacySessionContext(branch).messages);\n}\n\nfunction refreshActiveMessages(session: SessionState): void {\n\tsession.messages = deriveActiveMessages(session);\n}\n\nfunction refreshTreeHashes(session: SessionState): void {\n\tsession.prefixHashes = buildPiServerTreePrefixHashes(session.entries);\n\tsession.treeHash = session.prefixHashes[session.prefixHashes.length - 1];\n}\n\nfunction appendTreeHashes(session: SessionState, entries: SessionTreeEntry[]): void {\n\tfor (const entry of entries) {\n\t\tsession.treeHash = appendPiServerTreeHash(session.treeHash, hashPiServerTreeEntry(entry));\n\t\tsession.prefixHashes.push(session.treeHash);\n\t}\n}\n\nfunction assertValidLeaf(entries: SessionTreeEntry[], leafId: string | null): void {\n\tif (leafId === null) return;\n\tif (!entries.some((entry) => entry.id === leafId)) {\n\t\tthrow new PiServerError(`leafId ${leafId} does not exist in session tree`, PiServerErrorCode.LEAF_ID_NOT_FOUND, {\n\t\t\tleafId,\n\t\t});\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 PiServerError(`entry ${entry.id} already exists`, PiServerErrorCode.ENTRY_ALREADY_EXISTS, {\n\t\t\t\t\tentryId: entry.id,\n\t\t\t\t});\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 PiServerError(\n\t\t\t\t`parent entry ${entry.parentId} does not exist`,\n\t\t\t\tPiServerErrorCode.PARENT_ENTRY_NOT_FOUND,\n\t\t\t\t{ parentId: entry.parentId },\n\t\t\t);\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 PiServerError(`leafId ${leafId} does not exist in session tree`, PiServerErrorCode.LEAF_ID_NOT_FOUND, {\n\t\t\tleafId,\n\t\t});\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 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 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 deleteSession(sessionId: string): boolean {\n\treturn sessions.delete(sessionId);\n}\n\nexport function clearAllSessions(): void {\n\tsessions.clear();\n}\n\nfunction markWalPersistenceChange(session: SessionState, entries: SessionTreeEntry[]): void {\n\tif (session.persistenceChange?.kind === \"snapshot\") return;\n\tsession.persistenceChange = {\n\t\tkind: \"wal\",\n\t\tentries: [...(session.persistenceChange?.entries ?? []), ...entries.map((entry) => ({ ...entry }))],\n\t};\n}\n\nexport function markSessionPersisted(session: SessionState): void {\n\tsession.persistenceChange = undefined;\n}\n"]}
1
+ {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { buildLegacySessionContext, convertToLlm, type SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport { getCurrentSystemMessage, type Message, type Tool } from \"@earendil-works/pi-ai\";\nimport { PiServerError, PiServerErrorCode } from \"./error-codes.ts\";\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\tif (session.staticContextHash === newHash) return session;\n\tsession.staticContext = context;\n\tsession.staticContextHash = newHash;\n\tsession.revision++;\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.push(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\tbranch.reverse();\n\treturn branch;\n}\n\nfunction deriveActiveMessages(session: SessionState): Message[] {\n\tconst branch = getSessionBranch(session);\n\treturn convertToLlm(buildLegacySessionContext(branch).messages);\n}\n\nfunction refreshActiveMessages(session: SessionState): void {\n\tsession.messages = deriveActiveMessages(session);\n}\n\nfunction refreshTreeHashes(session: SessionState): void {\n\tsession.prefixHashes = buildPiServerTreePrefixHashes(session.entries);\n\tsession.treeHash = session.prefixHashes[session.prefixHashes.length - 1];\n}\n\nfunction appendTreeHashes(session: SessionState, entries: SessionTreeEntry[]): void {\n\tfor (const entry of entries) {\n\t\tsession.treeHash = appendPiServerTreeHash(session.treeHash, hashPiServerTreeEntry(entry));\n\t\tsession.prefixHashes.push(session.treeHash);\n\t}\n}\n\nfunction assertValidLeaf(entries: SessionTreeEntry[], leafId: string | null): void {\n\tif (leafId === null) return;\n\tif (!entries.some((entry) => entry.id === leafId)) {\n\t\tthrow new PiServerError(`leafId ${leafId} does not exist in session tree`, PiServerErrorCode.LEAF_ID_NOT_FOUND, {\n\t\t\tleafId,\n\t\t});\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 PiServerError(`entry ${entry.id} already exists`, PiServerErrorCode.ENTRY_ALREADY_EXISTS, {\n\t\t\t\t\tentryId: entry.id,\n\t\t\t\t});\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 PiServerError(\n\t\t\t\t`parent entry ${entry.parentId} does not exist`,\n\t\t\t\tPiServerErrorCode.PARENT_ENTRY_NOT_FOUND,\n\t\t\t\t{ parentId: entry.parentId },\n\t\t\t);\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 PiServerError(`leafId ${leafId} does not exist in session tree`, PiServerErrorCode.LEAF_ID_NOT_FOUND, {\n\t\t\tleafId,\n\t\t});\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 timestamp = new Date().toISOString();\n\tconst systemMessage = getCurrentSystemMessage(buildLegacySessionContext(getSessionBranch(session)).messages);\n\tconst entry: SessionTreeEntry = {\n\t\ttype: \"compaction\",\n\t\tid: randomUUID(),\n\t\tparentId: session.leafId,\n\t\ttimestamp,\n\t\tsummary: compaction.summary,\n\t\tfirstKeptEntryId: compaction.firstKeptEntryId,\n\t\ttokensBefore: compaction.tokensBefore,\n\t\tdetails: compaction.details,\n\t\t...(systemMessage ? { systemMessage: { ...systemMessage, timestamp: new Date(timestamp).getTime() } } : {}),\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 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 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 deleteSession(sessionId: string): boolean {\n\treturn sessions.delete(sessionId);\n}\n\nexport function clearAllSessions(): void {\n\tsessions.clear();\n}\n\nfunction markWalPersistenceChange(session: SessionState, entries: SessionTreeEntry[]): void {\n\tif (session.persistenceChange?.kind === \"snapshot\") return;\n\tsession.persistenceChange = {\n\t\tkind: \"wal\",\n\t\tentries: [...(session.persistenceChange?.entries ?? []), ...entries.map((entry) => ({ ...entry }))],\n\t};\n}\n\nexport function markSessionPersisted(session: SessionState): void {\n\tsession.persistenceChange = undefined;\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { isDeepStrictEqual } from "node:util";
3
3
  import { buildLegacySessionContext, convertToLlm } from "@earendil-works/pi-agent-core";
4
+ import { getCurrentSystemMessage } from "@earendil-works/pi-ai";
4
5
  import { PiServerError, PiServerErrorCode } from "./error-codes.js";
5
6
  import { appendPiServerTreeHash, buildPiServerTreePrefixHashes, hashPiServerSessionEntries, hashPiServerStaticContext, hashPiServerTreeEntry, } from "./pi-server-protocol.js";
6
7
  const sessions = new Map();
@@ -207,15 +208,18 @@ export function switchSessionLeaf(sessionId, leafId) {
207
208
  }
208
209
  export function appendCompactionEntry(sessionId, compaction) {
209
210
  const session = getOrCreateSession(sessionId);
211
+ const timestamp = new Date().toISOString();
212
+ const systemMessage = getCurrentSystemMessage(buildLegacySessionContext(getSessionBranch(session)).messages);
210
213
  const entry = {
211
214
  type: "compaction",
212
215
  id: randomUUID(),
213
216
  parentId: session.leafId,
214
- timestamp: new Date().toISOString(),
217
+ timestamp,
215
218
  summary: compaction.summary,
216
219
  firstKeptEntryId: compaction.firstKeptEntryId,
217
220
  tokensBefore: compaction.tokensBefore,
218
221
  details: compaction.details,
222
+ ...(systemMessage ? { systemMessage: { ...systemMessage, timestamp: new Date(timestamp).getTime() } } : {}),
219
223
  };
220
224
  session.entries.push(entry);
221
225
  session.leafId = entry.id;
@@ -1 +1 @@
1
- {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { buildLegacySessionContext, convertToLlm, type SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport type { Message, Tool } from \"@earendil-works/pi-ai\";\nimport { PiServerError, PiServerErrorCode } from \"./error-codes.ts\";\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\tif (session.staticContextHash === newHash) return session;\n\tsession.staticContext = context;\n\tsession.staticContextHash = newHash;\n\tsession.revision++;\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.push(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\tbranch.reverse();\n\treturn branch;\n}\n\nfunction deriveActiveMessages(session: SessionState): Message[] {\n\tconst branch = getSessionBranch(session);\n\treturn convertToLlm(buildLegacySessionContext(branch).messages);\n}\n\nfunction refreshActiveMessages(session: SessionState): void {\n\tsession.messages = deriveActiveMessages(session);\n}\n\nfunction refreshTreeHashes(session: SessionState): void {\n\tsession.prefixHashes = buildPiServerTreePrefixHashes(session.entries);\n\tsession.treeHash = session.prefixHashes[session.prefixHashes.length - 1];\n}\n\nfunction appendTreeHashes(session: SessionState, entries: SessionTreeEntry[]): void {\n\tfor (const entry of entries) {\n\t\tsession.treeHash = appendPiServerTreeHash(session.treeHash, hashPiServerTreeEntry(entry));\n\t\tsession.prefixHashes.push(session.treeHash);\n\t}\n}\n\nfunction assertValidLeaf(entries: SessionTreeEntry[], leafId: string | null): void {\n\tif (leafId === null) return;\n\tif (!entries.some((entry) => entry.id === leafId)) {\n\t\tthrow new PiServerError(`leafId ${leafId} does not exist in session tree`, PiServerErrorCode.LEAF_ID_NOT_FOUND, {\n\t\t\tleafId,\n\t\t});\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 PiServerError(`entry ${entry.id} already exists`, PiServerErrorCode.ENTRY_ALREADY_EXISTS, {\n\t\t\t\t\tentryId: entry.id,\n\t\t\t\t});\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 PiServerError(\n\t\t\t\t`parent entry ${entry.parentId} does not exist`,\n\t\t\t\tPiServerErrorCode.PARENT_ENTRY_NOT_FOUND,\n\t\t\t\t{ parentId: entry.parentId },\n\t\t\t);\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 PiServerError(`leafId ${leafId} does not exist in session tree`, PiServerErrorCode.LEAF_ID_NOT_FOUND, {\n\t\t\tleafId,\n\t\t});\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 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 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 deleteSession(sessionId: string): boolean {\n\treturn sessions.delete(sessionId);\n}\n\nexport function clearAllSessions(): void {\n\tsessions.clear();\n}\n\nfunction markWalPersistenceChange(session: SessionState, entries: SessionTreeEntry[]): void {\n\tif (session.persistenceChange?.kind === \"snapshot\") return;\n\tsession.persistenceChange = {\n\t\tkind: \"wal\",\n\t\tentries: [...(session.persistenceChange?.entries ?? []), ...entries.map((entry) => ({ ...entry }))],\n\t};\n}\n\nexport function markSessionPersisted(session: SessionState): void {\n\tsession.persistenceChange = undefined;\n}\n"]}
1
+ {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { buildLegacySessionContext, convertToLlm, type SessionTreeEntry } from \"@earendil-works/pi-agent-core\";\nimport { getCurrentSystemMessage, type Message, type Tool } from \"@earendil-works/pi-ai\";\nimport { PiServerError, PiServerErrorCode } from \"./error-codes.ts\";\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\tif (session.staticContextHash === newHash) return session;\n\tsession.staticContext = context;\n\tsession.staticContextHash = newHash;\n\tsession.revision++;\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.push(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\tbranch.reverse();\n\treturn branch;\n}\n\nfunction deriveActiveMessages(session: SessionState): Message[] {\n\tconst branch = getSessionBranch(session);\n\treturn convertToLlm(buildLegacySessionContext(branch).messages);\n}\n\nfunction refreshActiveMessages(session: SessionState): void {\n\tsession.messages = deriveActiveMessages(session);\n}\n\nfunction refreshTreeHashes(session: SessionState): void {\n\tsession.prefixHashes = buildPiServerTreePrefixHashes(session.entries);\n\tsession.treeHash = session.prefixHashes[session.prefixHashes.length - 1];\n}\n\nfunction appendTreeHashes(session: SessionState, entries: SessionTreeEntry[]): void {\n\tfor (const entry of entries) {\n\t\tsession.treeHash = appendPiServerTreeHash(session.treeHash, hashPiServerTreeEntry(entry));\n\t\tsession.prefixHashes.push(session.treeHash);\n\t}\n}\n\nfunction assertValidLeaf(entries: SessionTreeEntry[], leafId: string | null): void {\n\tif (leafId === null) return;\n\tif (!entries.some((entry) => entry.id === leafId)) {\n\t\tthrow new PiServerError(`leafId ${leafId} does not exist in session tree`, PiServerErrorCode.LEAF_ID_NOT_FOUND, {\n\t\t\tleafId,\n\t\t});\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 PiServerError(`entry ${entry.id} already exists`, PiServerErrorCode.ENTRY_ALREADY_EXISTS, {\n\t\t\t\t\tentryId: entry.id,\n\t\t\t\t});\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 PiServerError(\n\t\t\t\t`parent entry ${entry.parentId} does not exist`,\n\t\t\t\tPiServerErrorCode.PARENT_ENTRY_NOT_FOUND,\n\t\t\t\t{ parentId: entry.parentId },\n\t\t\t);\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 PiServerError(`leafId ${leafId} does not exist in session tree`, PiServerErrorCode.LEAF_ID_NOT_FOUND, {\n\t\t\tleafId,\n\t\t});\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 timestamp = new Date().toISOString();\n\tconst systemMessage = getCurrentSystemMessage(buildLegacySessionContext(getSessionBranch(session)).messages);\n\tconst entry: SessionTreeEntry = {\n\t\ttype: \"compaction\",\n\t\tid: randomUUID(),\n\t\tparentId: session.leafId,\n\t\ttimestamp,\n\t\tsummary: compaction.summary,\n\t\tfirstKeptEntryId: compaction.firstKeptEntryId,\n\t\ttokensBefore: compaction.tokensBefore,\n\t\tdetails: compaction.details,\n\t\t...(systemMessage ? { systemMessage: { ...systemMessage, timestamp: new Date(timestamp).getTime() } } : {}),\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 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 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 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.86.1-piclient.2",
3
+ "version": "0.87.0-piclient.1",
4
4
  "description": "Proxy server for pi-client that stores session state and forwards incremental requests to upstream LLM providers",
5
5
  "type": "module",
6
6
  "bin": {
@@ -27,8 +27,8 @@
27
27
  "prepublishOnly": "npm run clean && npm run build"
28
28
  },
29
29
  "dependencies": {
30
- "@earendil-works/pi-ai": "npm:@averyyy/pi-ai@0.86.1-piclient.2",
31
- "@earendil-works/pi-agent-core": "npm:@averyyy/pi-agent-core@0.86.1-piclient.2"
30
+ "@earendil-works/pi-ai": "npm:@averyyy/pi-ai@0.87.0-piclient.1",
31
+ "@earendil-works/pi-agent-core": "npm:@averyyy/pi-agent-core@0.87.0-piclient.1"
32
32
  },
33
33
  "keywords": [
34
34
  "pi-server",