mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-20 19:02:10 +00:00
Fix SessionEntry type to exclude SessionHeader
- SessionEntry now only contains conversation entries (messages, compaction, etc.) - SessionHeader is separate, not part of SessionEntry - FileEntry = SessionHeader | SessionEntry (for file storage) - getEntries() filters out header, returns SessionEntry[] - Added getHeader() for accessing session metadata - Updated compaction and tests to not expect header in entries - Updated mom package to use FileEntry for internal storage
This commit is contained in:
parent
251fea752c
commit
9478a3c1f5
6 changed files with 50 additions and 75 deletions
|
|
@ -759,7 +759,7 @@ export class AgentSession {
|
||||||
if (lastEntry?.type === "compaction") {
|
if (lastEntry?.type === "compaction") {
|
||||||
throw new Error("Already compacted");
|
throw new Error("Already compacted");
|
||||||
}
|
}
|
||||||
throw new Error("Nothing to compact (session too small or needs migration)");
|
throw new Error("Nothing to compact (session too small)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find previous compaction summary if any
|
// Find previous compaction summary if any
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import type { AppMessage } from "@mariozechner/pi-agent-core";
|
||||||
import type { AssistantMessage, Model, Usage } from "@mariozechner/pi-ai";
|
import type { AssistantMessage, Model, Usage } from "@mariozechner/pi-ai";
|
||||||
import { complete } from "@mariozechner/pi-ai";
|
import { complete } from "@mariozechner/pi-ai";
|
||||||
import { messageTransformer } from "./messages.js";
|
import { messageTransformer } from "./messages.js";
|
||||||
import type { CompactionEntry, ConversationEntry, SessionEntry } from "./session-manager.js";
|
import type { CompactionEntry, SessionEntry } from "./session-manager.js";
|
||||||
|
|
||||||
/** Result from compact() - SessionManager adds uuid/parentUuid when saving */
|
/** Result from compact() - SessionManager adds uuid/parentUuid when saving */
|
||||||
export interface CompactionResult {
|
export interface CompactionResult {
|
||||||
|
|
@ -251,7 +251,7 @@ export function findCutPoint(
|
||||||
while (cutIndex > startIndex) {
|
while (cutIndex > startIndex) {
|
||||||
const prevEntry = entries[cutIndex - 1];
|
const prevEntry = entries[cutIndex - 1];
|
||||||
// Stop at session header or compaction boundaries
|
// Stop at session header or compaction boundaries
|
||||||
if (prevEntry.type === "session" || prevEntry.type === "compaction") {
|
if (prevEntry.type === "compaction") {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (prevEntry.type === "message") {
|
if (prevEntry.type === "message") {
|
||||||
|
|
@ -370,13 +370,10 @@ export function prepareCompaction(entries: SessionEntry[], settings: CompactionS
|
||||||
|
|
||||||
// Get UUID of first kept entry
|
// Get UUID of first kept entry
|
||||||
const firstKeptEntry = entries[cutPoint.firstKeptEntryIndex];
|
const firstKeptEntry = entries[cutPoint.firstKeptEntryIndex];
|
||||||
if (firstKeptEntry.type === "session") {
|
if (!firstKeptEntry?.id) {
|
||||||
return null; // Can't compact if first kept is header
|
|
||||||
}
|
|
||||||
const firstKeptEntryId = (firstKeptEntry as ConversationEntry).id;
|
|
||||||
if (!firstKeptEntryId) {
|
|
||||||
return null; // Session needs migration
|
return null; // Session needs migration
|
||||||
}
|
}
|
||||||
|
const firstKeptEntryId = firstKeptEntry.id;
|
||||||
|
|
||||||
const historyEnd = cutPoint.isSplitTurn ? cutPoint.turnStartIndex : cutPoint.firstKeptEntryIndex;
|
const historyEnd = cutPoint.isSplitTurn ? cutPoint.turnStartIndex : cutPoint.firstKeptEntryIndex;
|
||||||
|
|
||||||
|
|
@ -515,10 +512,7 @@ export async function compact(
|
||||||
|
|
||||||
// Get UUID of first kept entry
|
// Get UUID of first kept entry
|
||||||
const firstKeptEntry = entries[cutResult.firstKeptEntryIndex];
|
const firstKeptEntry = entries[cutResult.firstKeptEntryIndex];
|
||||||
if (firstKeptEntry.type === "session") {
|
const firstKeptEntryId = firstKeptEntry.id;
|
||||||
throw new Error("Cannot compact: first kept entry is session header");
|
|
||||||
}
|
|
||||||
const firstKeptEntryId = (firstKeptEntry as ConversationEntry).id;
|
|
||||||
if (!firstKeptEntryId) {
|
if (!firstKeptEntryId) {
|
||||||
throw new Error("First kept entry has no UUID - session may need migration");
|
throw new Error("First kept entry has no UUID - session may need migration");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,16 +87,19 @@ export type ModelChangeEntry = TreeNode & ModelChangeContent;
|
||||||
export type CompactionEntry = TreeNode & CompactionContent;
|
export type CompactionEntry = TreeNode & CompactionContent;
|
||||||
export type BranchSummaryEntry = TreeNode & BranchSummaryContent;
|
export type BranchSummaryEntry = TreeNode & BranchSummaryContent;
|
||||||
|
|
||||||
/** Conversation entry - has id/parentId for tree structure */
|
/** Session entry - has id/parentId for tree structure */
|
||||||
export type ConversationEntry =
|
export type SessionEntry =
|
||||||
| SessionMessageEntry
|
| SessionMessageEntry
|
||||||
| ThinkingLevelChangeEntry
|
| ThinkingLevelChangeEntry
|
||||||
| ModelChangeEntry
|
| ModelChangeEntry
|
||||||
| CompactionEntry
|
| CompactionEntry
|
||||||
| BranchSummaryEntry;
|
| BranchSummaryEntry;
|
||||||
|
|
||||||
/** Any session entry (header or conversation) */
|
/** @deprecated Use SessionEntry */
|
||||||
export type SessionEntry = SessionHeader | ConversationEntry;
|
export type ConversationEntry = SessionEntry;
|
||||||
|
|
||||||
|
/** Raw file entry (includes header) */
|
||||||
|
export type FileEntry = SessionHeader | SessionEntry;
|
||||||
|
|
||||||
export interface SessionContext {
|
export interface SessionContext {
|
||||||
messages: AppMessage[];
|
messages: AppMessage[];
|
||||||
|
|
@ -135,7 +138,7 @@ export function createSummaryMessage(summary: string): AppMessage {
|
||||||
* Migrate v1 entries to v2 format by adding id/parentId fields.
|
* Migrate v1 entries to v2 format by adding id/parentId fields.
|
||||||
* Mutates entries in place. Safe to call on already-migrated entries.
|
* Mutates entries in place. Safe to call on already-migrated entries.
|
||||||
*/
|
*/
|
||||||
export function migrateSessionEntries(entries: SessionEntry[]): void {
|
export function migrateSessionEntries(entries: FileEntry[]): void {
|
||||||
// Check if already migrated
|
// Check if already migrated
|
||||||
const firstConv = entries.find((e) => e.type !== "session");
|
const firstConv = entries.find((e) => e.type !== "session");
|
||||||
if (firstConv && "id" in firstConv && firstConv.id) {
|
if (firstConv && "id" in firstConv && firstConv.id) {
|
||||||
|
|
@ -171,7 +174,7 @@ export function migrateSessionEntries(entries: SessionEntry[]): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Exported for compaction.test.ts */
|
/** Exported for compaction.test.ts */
|
||||||
export function parseSessionEntries(content: string): SessionEntry[] {
|
export function parseSessionEntries(content: string): FileEntry[] {
|
||||||
const entries: SessionEntry[] = [];
|
const entries: SessionEntry[] = [];
|
||||||
const lines = content.trim().split("\n");
|
const lines = content.trim().split("\n");
|
||||||
|
|
||||||
|
|
@ -203,26 +206,18 @@ export function getLatestCompactionEntry(entries: SessionEntry[]): CompactionEnt
|
||||||
* Handles compaction and branch summaries along the path.
|
* Handles compaction and branch summaries along the path.
|
||||||
*/
|
*/
|
||||||
export function buildSessionContext(entries: SessionEntry[], leafId?: string): SessionContext {
|
export function buildSessionContext(entries: SessionEntry[], leafId?: string): SessionContext {
|
||||||
// Build uuid index for conversation entries
|
// Build uuid index
|
||||||
const byId = new Map<string, ConversationEntry>();
|
const byId = new Map<string, SessionEntry>();
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
if (entry.type !== "session") {
|
byId.set(entry.id, entry);
|
||||||
byId.set(entry.id, entry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find leaf
|
// Find leaf
|
||||||
let leaf: ConversationEntry | undefined;
|
let leaf: SessionEntry | undefined;
|
||||||
if (leafId) {
|
if (leafId) {
|
||||||
leaf = byId.get(leafId);
|
leaf = byId.get(leafId);
|
||||||
} else {
|
} else {
|
||||||
// Find last conversation entry
|
leaf = entries[entries.length - 1];
|
||||||
for (let i = entries.length - 1; i >= 0; i--) {
|
|
||||||
if (entries[i].type !== "session") {
|
|
||||||
leaf = entries[i] as ConversationEntry;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!leaf) {
|
if (!leaf) {
|
||||||
|
|
@ -230,8 +225,8 @@ export function buildSessionContext(entries: SessionEntry[], leafId?: string): S
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk from leaf to root, collecting path
|
// Walk from leaf to root, collecting path
|
||||||
const path: ConversationEntry[] = [];
|
const path: SessionEntry[] = [];
|
||||||
let current: ConversationEntry | undefined = leaf;
|
let current: SessionEntry | undefined = leaf;
|
||||||
while (current) {
|
while (current) {
|
||||||
path.unshift(current);
|
path.unshift(current);
|
||||||
current = current.parentId ? byId.get(current.parentId) : undefined;
|
current = current.parentId ? byId.get(current.parentId) : undefined;
|
||||||
|
|
@ -316,7 +311,7 @@ function getDefaultSessionDir(cwd: string): string {
|
||||||
return sessionDir;
|
return sessionDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadEntriesFromFile(filePath: string): SessionEntry[] {
|
function loadEntriesFromFile(filePath: string): FileEntry[] {
|
||||||
if (!existsSync(filePath)) return [];
|
if (!existsSync(filePath)) return [];
|
||||||
|
|
||||||
const content = readFileSync(filePath, "utf8");
|
const content = readFileSync(filePath, "utf8");
|
||||||
|
|
@ -359,7 +354,7 @@ export class SessionManager {
|
||||||
private cwd: string;
|
private cwd: string;
|
||||||
private persist: boolean;
|
private persist: boolean;
|
||||||
private flushed: boolean = false;
|
private flushed: boolean = false;
|
||||||
private inMemoryEntries: SessionEntry[] = [];
|
private inMemoryEntries: FileEntry[] = [];
|
||||||
|
|
||||||
// Tree structure (v2)
|
// Tree structure (v2)
|
||||||
private byId: Map<string, ConversationEntry> = new Map();
|
private byId: Map<string, ConversationEntry> = new Map();
|
||||||
|
|
@ -570,11 +565,19 @@ export class SessionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all session entries. Returns a defensive copy.
|
* Get session header.
|
||||||
|
*/
|
||||||
|
getHeader(): SessionHeader | null {
|
||||||
|
const h = this.inMemoryEntries.find((e) => e.type === "session");
|
||||||
|
return h ? (h as SessionHeader) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all session entries (excludes header). Returns a defensive copy.
|
||||||
* Use buildSessionContext() if you need the messages for the LLM.
|
* Use buildSessionContext() if you need the messages for the LLM.
|
||||||
*/
|
*/
|
||||||
getEntries(): SessionEntry[] {
|
getEntries(): SessionEntry[] {
|
||||||
return [...this.inMemoryEntries];
|
return this.inMemoryEntries.filter((e): e is SessionEntry => e.type !== "session");
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
@ -606,12 +609,12 @@ export class SessionManager {
|
||||||
return entry.id;
|
return entry.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
createBranchedSessionFromEntries(entries: SessionEntry[], branchBeforeIndex: number): string | null {
|
createBranchedSessionFromEntries(entries: FileEntry[], branchBeforeIndex: number): string | null {
|
||||||
const newSessionId = uuidv4();
|
const newSessionId = uuidv4();
|
||||||
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
||||||
const newSessionFile = join(this.getSessionDir(), `${timestamp}_${newSessionId}.jsonl`);
|
const newSessionFile = join(this.getSessionDir(), `${timestamp}_${newSessionId}.jsonl`);
|
||||||
|
|
||||||
const newEntries: SessionEntry[] = [];
|
const newEntries: FileEntry[] = [];
|
||||||
for (let i = 0; i < branchBeforeIndex; i++) {
|
for (let i = 0; i < branchBeforeIndex; i++) {
|
||||||
const entry = entries[i];
|
const entry = entries[i];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,7 @@ export {
|
||||||
type ConversationEntry,
|
type ConversationEntry,
|
||||||
CURRENT_SESSION_VERSION,
|
CURRENT_SESSION_VERSION,
|
||||||
createSummaryMessage,
|
createSummaryMessage,
|
||||||
|
type FileEntry,
|
||||||
getLatestCompactionEntry,
|
getLatestCompactionEntry,
|
||||||
type MessageContent,
|
type MessageContent,
|
||||||
type ModelChangeContent,
|
type ModelChangeContent,
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ function loadLargeSessionEntries(): SessionEntry[] {
|
||||||
const content = readFileSync(sessionPath, "utf-8");
|
const content = readFileSync(sessionPath, "utf-8");
|
||||||
const entries = parseSessionEntries(content);
|
const entries = parseSessionEntries(content);
|
||||||
migrateSessionEntries(entries); // Add id/parentId for v1 fixtures
|
migrateSessionEntries(entries); // Add id/parentId for v1 fixtures
|
||||||
return entries;
|
return entries.filter((e): e is SessionEntry => e.type !== "session");
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMockUsage(input: number, output: number, cacheRead = 0, cacheWrite = 0): Usage {
|
function createMockUsage(input: number, output: number, cacheRead = 0, cacheWrite = 0): Usage {
|
||||||
|
|
@ -78,16 +78,6 @@ beforeEach(() => {
|
||||||
resetEntryCounter();
|
resetEntryCounter();
|
||||||
});
|
});
|
||||||
|
|
||||||
function createSessionHeader() {
|
|
||||||
return {
|
|
||||||
type: "session" as const,
|
|
||||||
version: 2,
|
|
||||||
id: "test-session",
|
|
||||||
timestamp: "",
|
|
||||||
cwd: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMessageEntry(message: AppMessage): SessionMessageEntry {
|
function createMessageEntry(message: AppMessage): SessionMessageEntry {
|
||||||
const id = `test-id-${entryCounter++}`;
|
const id = `test-id-${entryCounter++}`;
|
||||||
const entry: SessionMessageEntry = {
|
const entry: SessionMessageEntry = {
|
||||||
|
|
@ -298,12 +288,6 @@ describe("createSummaryMessage", () => {
|
||||||
describe("buildSessionContext", () => {
|
describe("buildSessionContext", () => {
|
||||||
it("should load all messages when no compaction", () => {
|
it("should load all messages when no compaction", () => {
|
||||||
const entries: SessionEntry[] = [
|
const entries: SessionEntry[] = [
|
||||||
{
|
|
||||||
type: "session",
|
|
||||||
id: "1",
|
|
||||||
timestamp: "",
|
|
||||||
cwd: "",
|
|
||||||
},
|
|
||||||
createMessageEntry(createUserMessage("1")),
|
createMessageEntry(createUserMessage("1")),
|
||||||
createMessageEntry(createAssistantMessage("a")),
|
createMessageEntry(createAssistantMessage("a")),
|
||||||
createMessageEntry(createUserMessage("2")),
|
createMessageEntry(createUserMessage("2")),
|
||||||
|
|
@ -326,7 +310,7 @@ describe("buildSessionContext", () => {
|
||||||
const u3 = createMessageEntry(createUserMessage("3"));
|
const u3 = createMessageEntry(createUserMessage("3"));
|
||||||
const a3 = createMessageEntry(createAssistantMessage("c"));
|
const a3 = createMessageEntry(createAssistantMessage("c"));
|
||||||
|
|
||||||
const entries: SessionEntry[] = [createSessionHeader(), u1, a1, u2, a2, compaction, u3, a3];
|
const entries: SessionEntry[] = [u1, a1, u2, a2, compaction, u3, a3];
|
||||||
|
|
||||||
const loaded = buildSessionContext(entries);
|
const loaded = buildSessionContext(entries);
|
||||||
// summary + kept (u2, a2) + after (u3, a3) = 5
|
// summary + kept (u2, a2) + after (u3, a3) = 5
|
||||||
|
|
@ -350,7 +334,7 @@ describe("buildSessionContext", () => {
|
||||||
const u4 = createMessageEntry(createUserMessage("4"));
|
const u4 = createMessageEntry(createUserMessage("4"));
|
||||||
const d = createMessageEntry(createAssistantMessage("d"));
|
const d = createMessageEntry(createAssistantMessage("d"));
|
||||||
|
|
||||||
const entries: SessionEntry[] = [createSessionHeader(), u1, a1, compact1, u2, b, u3, c, compact2, u4, d];
|
const entries: SessionEntry[] = [u1, a1, compact1, u2, b, u3, c, compact2, u4, d];
|
||||||
|
|
||||||
const loaded = buildSessionContext(entries);
|
const loaded = buildSessionContext(entries);
|
||||||
// summary + kept from u3 (u3, c) + after (u4, d) = 5
|
// summary + kept from u3 (u3, c) + after (u4, d) = 5
|
||||||
|
|
@ -365,7 +349,7 @@ describe("buildSessionContext", () => {
|
||||||
const u2 = createMessageEntry(createUserMessage("2"));
|
const u2 = createMessageEntry(createUserMessage("2"));
|
||||||
const b = createMessageEntry(createAssistantMessage("b"));
|
const b = createMessageEntry(createAssistantMessage("b"));
|
||||||
|
|
||||||
const entries: SessionEntry[] = [createSessionHeader(), u1, a1, compact1, u2, b];
|
const entries: SessionEntry[] = [u1, a1, compact1, u2, b];
|
||||||
|
|
||||||
const loaded = buildSessionContext(entries);
|
const loaded = buildSessionContext(entries);
|
||||||
// summary + all messages (u1, a1, u2, b) = 5
|
// summary + all messages (u1, a1, u2, b) = 5
|
||||||
|
|
@ -374,12 +358,6 @@ describe("buildSessionContext", () => {
|
||||||
|
|
||||||
it("should track model and thinking level changes", () => {
|
it("should track model and thinking level changes", () => {
|
||||||
const entries: SessionEntry[] = [
|
const entries: SessionEntry[] = [
|
||||||
{
|
|
||||||
type: "session",
|
|
||||||
id: "1",
|
|
||||||
timestamp: "",
|
|
||||||
cwd: "",
|
|
||||||
},
|
|
||||||
createMessageEntry(createUserMessage("1")),
|
createMessageEntry(createUserMessage("1")),
|
||||||
createModelChangeEntry("openai", "gpt-4"),
|
createModelChangeEntry("openai", "gpt-4"),
|
||||||
createMessageEntry(createAssistantMessage("a")),
|
createMessageEntry(createAssistantMessage("a")),
|
||||||
|
|
@ -466,7 +444,7 @@ describe.skipIf(!process.env.ANTHROPIC_OAUTH_TOKEN)("LLM summarization", () => {
|
||||||
|
|
||||||
// Simulate appending compaction to entries by creating a proper entry
|
// Simulate appending compaction to entries by creating a proper entry
|
||||||
const lastEntry = entries[entries.length - 1];
|
const lastEntry = entries[entries.length - 1];
|
||||||
const parentId = lastEntry.type === "session" ? null : lastEntry.id;
|
const parentId = lastEntry.id;
|
||||||
const compactionEntry: CompactionEntry = {
|
const compactionEntry: CompactionEntry = {
|
||||||
type: "compaction",
|
type: "compaction",
|
||||||
id: "compaction-test-id",
|
id: "compaction-test-id",
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import type { AppMessage } from "@mariozechner/pi-agent-core";
|
||||||
import {
|
import {
|
||||||
buildSessionContext,
|
buildSessionContext,
|
||||||
type CompactionEntry,
|
type CompactionEntry,
|
||||||
|
type FileEntry,
|
||||||
type LoadedSession,
|
type LoadedSession,
|
||||||
type MessageContent,
|
type MessageContent,
|
||||||
type ModelChangeContent,
|
type ModelChangeContent,
|
||||||
|
|
@ -52,7 +53,7 @@ export class MomSessionManager {
|
||||||
private logFile: string;
|
private logFile: string;
|
||||||
private channelDir: string;
|
private channelDir: string;
|
||||||
private flushed: boolean = false;
|
private flushed: boolean = false;
|
||||||
private inMemoryEntries: SessionEntry[] = [];
|
private inMemoryEntries: FileEntry[] = [];
|
||||||
private leafId: string | null = null;
|
private leafId: string | null = null;
|
||||||
|
|
||||||
constructor(channelDir: string) {
|
constructor(channelDir: string) {
|
||||||
|
|
@ -259,17 +260,17 @@ export class MomSessionManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadEntriesFromFile(): SessionEntry[] {
|
private loadEntriesFromFile(): FileEntry[] {
|
||||||
if (!existsSync(this.contextFile)) return [];
|
if (!existsSync(this.contextFile)) return [];
|
||||||
|
|
||||||
const content = readFileSync(this.contextFile, "utf8");
|
const content = readFileSync(this.contextFile, "utf8");
|
||||||
const entries: SessionEntry[] = [];
|
const entries: FileEntry[] = [];
|
||||||
const lines = content.trim().split("\n");
|
const lines = content.trim().split("\n");
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (!line.trim()) continue;
|
if (!line.trim()) continue;
|
||||||
try {
|
try {
|
||||||
const entry = JSON.parse(line) as SessionEntry;
|
const entry = JSON.parse(line) as FileEntry;
|
||||||
entries.push(entry);
|
entries.push(entry);
|
||||||
} catch {
|
} catch {
|
||||||
// Skip malformed lines
|
// Skip malformed lines
|
||||||
|
|
@ -313,10 +314,8 @@ export class MomSessionManager {
|
||||||
|
|
||||||
loadEntries(): SessionEntry[] {
|
loadEntries(): SessionEntry[] {
|
||||||
// Re-read from file to get latest state
|
// Re-read from file to get latest state
|
||||||
if (existsSync(this.contextFile)) {
|
const entries = existsSync(this.contextFile) ? this.loadEntriesFromFile() : this.inMemoryEntries;
|
||||||
return this.loadEntriesFromFile();
|
return entries.filter((e): e is SessionEntry => e.type !== "session");
|
||||||
}
|
|
||||||
return [...this.inMemoryEntries];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSessionId(): string {
|
getSessionId(): string {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue