@majkapp/majk-chat-sessions 1.0.0

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/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # Magic Chat Sessions
2
+
3
+ Session management for Magic Chat conversations. This package provides persistent conversation storage and retrieval capabilities.
4
+
5
+ ## Features
6
+
7
+ - **Persistent Sessions**: Store complete conversation histories with metadata
8
+ - **Working Directory Organization**: Sessions are organized by the working directory where they were created
9
+ - **Automatic Cleanup**: Configurable session cleanup based on age and count limits
10
+ - **UUID-based Session IDs**: Each session has a unique identifier for easy reference
11
+ - **Flexible Storage**: Pluggable storage interface with filesystem implementation
12
+
13
+ ## Usage
14
+
15
+ ### Basic Session Management
16
+
17
+ ```typescript
18
+ import { SessionManager } from '@majkapp/majk-chat-sessions';
19
+
20
+ const sessionManager = new SessionManager();
21
+
22
+ // Create a new session
23
+ const sessionId = await sessionManager.createSession('/path/to/project', {
24
+ title: 'My Chat Session',
25
+ provider: 'openai',
26
+ model: 'gpt-4',
27
+ initialMessages: []
28
+ });
29
+
30
+ // Add messages to the session
31
+ await sessionManager.addMessage({
32
+ role: 'user',
33
+ content: 'Hello, how are you?'
34
+ });
35
+
36
+ // Continue the most recent session in a directory
37
+ const sessionData = await sessionManager.continueLatestSession('/path/to/project');
38
+
39
+ // Load a specific session
40
+ const sessionData = await sessionManager.loadSession(sessionId, '/path/to/project');
41
+
42
+ // List all sessions in a directory
43
+ const sessions = await sessionManager.listSessions('/path/to/project');
44
+ ```
45
+
46
+ ### CLI Integration
47
+
48
+ The sessions package is integrated into the Magic Chat CLI with the following flags:
49
+
50
+ - `--continue`: Continue the most recent session in the current directory
51
+ - `--session <uuid>`: Continue a specific session by UUID
52
+ - `--session-title <title>`: Set a title for new sessions
53
+ - `--list-sessions`: List all sessions in the current directory
54
+ - `--delete-session <uuid>`: Delete a specific session
55
+
56
+ ### Directory Organization
57
+
58
+ Sessions are stored in `~/.majk/chat-sessions/` organized by sanitized working directory names:
59
+
60
+ ```
61
+ ~/.majk/chat-sessions/
62
+ ├── home_user_projects_myapp/
63
+ │ ├── 550e8400-e29b-41d4-a716-446655440000.json
64
+ │ └── 6ba7b810-9dad-11d1-80b4-00c04fd430c8.json
65
+ └── home_user_documents/
66
+ └── f47ac10b-58cc-4372-a567-0e02b2c3d479.json
67
+ ```
68
+
69
+ ## Configuration
70
+
71
+ ```typescript
72
+ import { SessionManager, FileSystemSessionStorage } from '@majkapp/majk-chat-sessions';
73
+
74
+ const sessionManager = new SessionManager({
75
+ baseDirectory: '/custom/sessions/path', // Default: ~/.majk/chat-sessions
76
+ maxSessions: 500, // Default: 1000
77
+ autoCleanup: true, // Default: true
78
+ cleanupAfterDays: 30 // Default: 90
79
+ });
80
+ ```
81
+
82
+ ## Custom Storage
83
+
84
+ You can implement custom storage backends by implementing the `SessionStorage` interface:
85
+
86
+ ```typescript
87
+ import { SessionStorage, SessionData, SessionListItem } from '@majkapp/majk-chat-sessions';
88
+
89
+ class CustomStorage implements SessionStorage {
90
+ async save(sessionData: SessionData): Promise<void> {
91
+ // Your implementation
92
+ }
93
+
94
+ async load(sessionId: string, workingDirectory: string): Promise<SessionData | null> {
95
+ // Your implementation
96
+ }
97
+
98
+ // ... implement other methods
99
+ }
100
+
101
+ const sessionManager = new SessionManager({
102
+ storage: new CustomStorage()
103
+ });
104
+ ```
105
+
106
+ ## Session Data Structure
107
+
108
+ ```typescript
109
+ interface SessionData {
110
+ metadata: {
111
+ id: string;
112
+ workingDirectory: string;
113
+ createdAt: Date;
114
+ updatedAt: Date;
115
+ title?: string;
116
+ provider?: string;
117
+ model?: string;
118
+ totalMessages: number;
119
+ };
120
+ messages: Message[]; // Array of chat messages
121
+ context?: Record<string, any>; // Additional context data
122
+ }
123
+ ```
124
+
125
+ ## License
126
+
127
+ MIT
@@ -0,0 +1,5 @@
1
+ export { SessionManager } from './session-manager';
2
+ export { FileSystemSessionStorage } from './storage/filesystem';
3
+ export { SessionMetadata, SessionData, SessionListItem, SessionStorageConfig, SessionStorage } from './types/session';
4
+ export { SessionManagerConfig } from './session-manager';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EACL,eAAe,EACf,WAAW,EACX,eAAe,EACf,oBAAoB,EACpB,cAAc,EACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FileSystemSessionStorage = exports.SessionManager = void 0;
4
+ var session_manager_1 = require("./session-manager");
5
+ Object.defineProperty(exports, "SessionManager", { enumerable: true, get: function () { return session_manager_1.SessionManager; } });
6
+ var filesystem_1 = require("./storage/filesystem");
7
+ Object.defineProperty(exports, "FileSystemSessionStorage", { enumerable: true, get: function () { return filesystem_1.FileSystemSessionStorage; } });
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qDAAmD;AAA1C,iHAAA,cAAc,OAAA;AACvB,mDAAgE;AAAvD,sHAAA,wBAAwB,OAAA"}
@@ -0,0 +1,33 @@
1
+ import { Message } from '@majkapp/majk-chat-core';
2
+ import { SessionStorage, SessionData, SessionMetadata, SessionListItem, SessionStorageConfig } from './types/session';
3
+ export interface SessionManagerConfig extends SessionStorageConfig {
4
+ storage?: SessionStorage;
5
+ }
6
+ export declare class SessionManager {
7
+ private storage;
8
+ private currentSession;
9
+ constructor(config?: SessionManagerConfig);
10
+ createSession(workingDirectory: string, options?: {
11
+ title?: string;
12
+ provider?: string;
13
+ model?: string;
14
+ initialMessages?: Message[];
15
+ context?: Record<string, any>;
16
+ }): Promise<string>;
17
+ loadSession(sessionId: string, workingDirectory: string): Promise<SessionData | null>;
18
+ continueLatestSession(workingDirectory: string): Promise<SessionData | null>;
19
+ addMessage(message: Message): Promise<void>;
20
+ addMessages(messages: Message[]): Promise<void>;
21
+ updateSessionMetadata(updates: Partial<SessionMetadata>): Promise<void>;
22
+ updateSessionContext(context: Record<string, any>): Promise<void>;
23
+ getCurrentSession(): SessionData | null;
24
+ getCurrentSessionId(): string | null;
25
+ getMessages(): Message[];
26
+ listSessions(workingDirectory: string): Promise<SessionListItem[]>;
27
+ deleteSession(sessionId: string, workingDirectory: string): Promise<boolean>;
28
+ sessionExists(sessionId: string, workingDirectory: string): Promise<boolean>;
29
+ cleanup(workingDirectory?: string): Promise<number>;
30
+ clearCurrentSession(): void;
31
+ generateSessionTitle(maxLength?: number): Promise<string | null>;
32
+ }
33
+ //# sourceMappingURL=session-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAGtH,MAAM,WAAW,oBAAqB,SAAQ,oBAAoB;IAChE,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,cAAc,CAA4B;gBAEtC,MAAM,GAAE,oBAAyB;IAIvC,aAAa,CACjB,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;QAC5B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC1B,GACL,OAAO,CAAC,MAAM,CAAC;IA2BZ,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAUrF,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAU5E,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAW3C,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAW/C,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAUvE,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IASvE,iBAAiB,IAAI,WAAW,GAAG,IAAI;IAIvC,mBAAmB,IAAI,MAAM,GAAG,IAAI;IAIpC,WAAW,IAAI,OAAO,EAAE;IAIlB,YAAY,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAIlE,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAU5E,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI5E,OAAO,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIzD,mBAAmB,IAAI,IAAI;IAIrB,oBAAoB,CAAC,SAAS,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CA+B3E"}
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionManager = void 0;
4
+ const uuid_1 = require("uuid");
5
+ const filesystem_1 = require("./storage/filesystem");
6
+ class SessionManager {
7
+ constructor(config = {}) {
8
+ this.currentSession = null;
9
+ this.storage = config.storage || new filesystem_1.FileSystemSessionStorage(config);
10
+ }
11
+ async createSession(workingDirectory, options = {}) {
12
+ const sessionId = (0, uuid_1.v4)();
13
+ const now = new Date();
14
+ const metadata = {
15
+ id: sessionId,
16
+ workingDirectory,
17
+ createdAt: now,
18
+ updatedAt: now,
19
+ title: options.title,
20
+ provider: options.provider,
21
+ model: options.model,
22
+ totalMessages: options.initialMessages?.length || 0
23
+ };
24
+ const sessionData = {
25
+ metadata,
26
+ messages: options.initialMessages || [],
27
+ context: options.context
28
+ };
29
+ await this.storage.save(sessionData);
30
+ this.currentSession = sessionData;
31
+ return sessionId;
32
+ }
33
+ async loadSession(sessionId, workingDirectory) {
34
+ const sessionData = await this.storage.load(sessionId, workingDirectory);
35
+ if (sessionData) {
36
+ this.currentSession = sessionData;
37
+ }
38
+ return sessionData;
39
+ }
40
+ async continueLatestSession(workingDirectory) {
41
+ const sessionData = await this.storage.getLatest(workingDirectory);
42
+ if (sessionData) {
43
+ this.currentSession = sessionData;
44
+ }
45
+ return sessionData;
46
+ }
47
+ async addMessage(message) {
48
+ if (!this.currentSession) {
49
+ throw new Error('No active session. Create or load a session first.');
50
+ }
51
+ this.currentSession.messages.push(message);
52
+ this.currentSession.metadata.totalMessages = this.currentSession.messages.length;
53
+ this.currentSession.metadata.updatedAt = new Date();
54
+ await this.storage.save(this.currentSession);
55
+ }
56
+ async addMessages(messages) {
57
+ if (!this.currentSession) {
58
+ throw new Error('No active session. Create or load a session first.');
59
+ }
60
+ this.currentSession.messages.push(...messages);
61
+ this.currentSession.metadata.totalMessages = this.currentSession.messages.length;
62
+ this.currentSession.metadata.updatedAt = new Date();
63
+ await this.storage.save(this.currentSession);
64
+ }
65
+ async updateSessionMetadata(updates) {
66
+ if (!this.currentSession) {
67
+ throw new Error('No active session. Create or load a session first.');
68
+ }
69
+ Object.assign(this.currentSession.metadata, updates);
70
+ this.currentSession.metadata.updatedAt = new Date();
71
+ await this.storage.save(this.currentSession);
72
+ }
73
+ async updateSessionContext(context) {
74
+ if (!this.currentSession) {
75
+ throw new Error('No active session. Create or load a session first.');
76
+ }
77
+ this.currentSession.context = { ...this.currentSession.context, ...context };
78
+ await this.storage.save(this.currentSession);
79
+ }
80
+ getCurrentSession() {
81
+ return this.currentSession;
82
+ }
83
+ getCurrentSessionId() {
84
+ return this.currentSession?.metadata.id || null;
85
+ }
86
+ getMessages() {
87
+ return this.currentSession?.messages || [];
88
+ }
89
+ async listSessions(workingDirectory) {
90
+ return await this.storage.list(workingDirectory);
91
+ }
92
+ async deleteSession(sessionId, workingDirectory) {
93
+ const deleted = await this.storage.delete(sessionId, workingDirectory);
94
+ if (deleted && this.currentSession?.metadata.id === sessionId) {
95
+ this.currentSession = null;
96
+ }
97
+ return deleted;
98
+ }
99
+ async sessionExists(sessionId, workingDirectory) {
100
+ return await this.storage.exists(sessionId, workingDirectory);
101
+ }
102
+ async cleanup(workingDirectory) {
103
+ return await this.storage.cleanup(workingDirectory);
104
+ }
105
+ clearCurrentSession() {
106
+ this.currentSession = null;
107
+ }
108
+ async generateSessionTitle(maxLength = 50) {
109
+ if (!this.currentSession || this.currentSession.messages.length === 0) {
110
+ return null;
111
+ }
112
+ const firstUserMessage = this.currentSession.messages.find(m => m.role === 'user');
113
+ if (!firstUserMessage) {
114
+ return null;
115
+ }
116
+ let content = '';
117
+ if (typeof firstUserMessage.content === 'string') {
118
+ content = firstUserMessage.content;
119
+ }
120
+ else if (Array.isArray(firstUserMessage.content)) {
121
+ const textBlocks = firstUserMessage.content.filter(block => block.type === 'text');
122
+ if (textBlocks.length > 0) {
123
+ content = textBlocks[0].text;
124
+ }
125
+ }
126
+ if (!content) {
127
+ return null;
128
+ }
129
+ const title = content.length > maxLength
130
+ ? content.substring(0, maxLength).trim() + '...'
131
+ : content.trim();
132
+ await this.updateSessionMetadata({ title });
133
+ return title;
134
+ }
135
+ }
136
+ exports.SessionManager = SessionManager;
137
+ //# sourceMappingURL=session-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { SessionStorage, SessionData, SessionListItem, SessionStorageConfig } from '../types/session';
2
+ export declare class FileSystemSessionStorage implements SessionStorage {
3
+ private baseDirectory;
4
+ private maxSessions;
5
+ private autoCleanup;
6
+ private cleanupAfterDays;
7
+ constructor(config?: SessionStorageConfig);
8
+ private sanitizeDirectoryName;
9
+ private getSessionDirectory;
10
+ private getSessionFilePath;
11
+ private ensureDirectoryExists;
12
+ private readSessionFile;
13
+ private writeSessionFile;
14
+ save(sessionData: SessionData): Promise<void>;
15
+ load(sessionId: string, workingDirectory: string): Promise<SessionData | null>;
16
+ exists(sessionId: string, workingDirectory: string): Promise<boolean>;
17
+ list(workingDirectory: string): Promise<SessionListItem[]>;
18
+ private extractLastMessageContent;
19
+ delete(sessionId: string, workingDirectory: string): Promise<boolean>;
20
+ getLatest(workingDirectory: string): Promise<SessionData | null>;
21
+ cleanup(workingDirectory?: string): Promise<number>;
22
+ private performCleanup;
23
+ }
24
+ //# sourceMappingURL=filesystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.d.ts","sourceRoot":"","sources":["../../src/storage/filesystem.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAEtG,qBAAa,wBAAyB,YAAW,cAAc;IAC7D,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,gBAAgB,CAAS;gBAErB,MAAM,GAAE,oBAAyB;IAO7C,OAAO,CAAC,qBAAqB;IAQ7B,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,kBAAkB;YAIZ,qBAAqB;YAQrB,eAAe;YAiBf,gBAAgB;IASxB,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB7C,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAK9E,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUrE,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA+ChE,OAAO,CAAC,yBAAyB;IAkB3B,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAcrE,SAAS,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAWhE,OAAO,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAuB3C,cAAc;CA2B7B"}
@@ -0,0 +1,248 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.FileSystemSessionStorage = void 0;
37
+ const fs = __importStar(require("fs/promises"));
38
+ const path = __importStar(require("path"));
39
+ const os = __importStar(require("os"));
40
+ class FileSystemSessionStorage {
41
+ constructor(config = {}) {
42
+ this.baseDirectory = config.baseDirectory || path.join(os.homedir(), '.majk', 'chat-sessions');
43
+ this.maxSessions = config.maxSessions || 1000;
44
+ this.autoCleanup = config.autoCleanup ?? true;
45
+ this.cleanupAfterDays = config.cleanupAfterDays || 90;
46
+ }
47
+ sanitizeDirectoryName(dirPath) {
48
+ return dirPath
49
+ .replace(/^\/+|\/+$/g, '')
50
+ .replace(/[^a-zA-Z0-9_\-]/g, '_')
51
+ .replace(/_+/g, '_')
52
+ .toLowerCase();
53
+ }
54
+ getSessionDirectory(workingDirectory) {
55
+ const sanitizedName = this.sanitizeDirectoryName(workingDirectory);
56
+ const homeSanitized = this.sanitizeDirectoryName(os.homedir());
57
+ let sessionDirName;
58
+ if (workingDirectory.startsWith(os.homedir())) {
59
+ const relativePath = path.relative(os.homedir(), workingDirectory);
60
+ sessionDirName = `home_${this.sanitizeDirectoryName(relativePath)}`;
61
+ }
62
+ else {
63
+ sessionDirName = sanitizedName;
64
+ }
65
+ return path.join(this.baseDirectory, sessionDirName);
66
+ }
67
+ getSessionFilePath(sessionId, workingDirectory) {
68
+ return path.join(this.getSessionDirectory(workingDirectory), `${sessionId}.json`);
69
+ }
70
+ async ensureDirectoryExists(dirPath) {
71
+ try {
72
+ await fs.access(dirPath);
73
+ }
74
+ catch {
75
+ await fs.mkdir(dirPath, { recursive: true });
76
+ }
77
+ }
78
+ async readSessionFile(filePath) {
79
+ try {
80
+ const content = await fs.readFile(filePath, 'utf-8');
81
+ const data = JSON.parse(content);
82
+ data.metadata.createdAt = new Date(data.metadata.createdAt);
83
+ data.metadata.updatedAt = new Date(data.metadata.updatedAt);
84
+ return data;
85
+ }
86
+ catch (error) {
87
+ if (error.code === 'ENOENT') {
88
+ return null;
89
+ }
90
+ throw new Error(`Failed to read session file: ${error}`);
91
+ }
92
+ }
93
+ async writeSessionFile(filePath, sessionData) {
94
+ try {
95
+ const content = JSON.stringify(sessionData, null, 2);
96
+ await fs.writeFile(filePath, content, 'utf-8');
97
+ }
98
+ catch (error) {
99
+ throw new Error(`Failed to write session file: ${error}`);
100
+ }
101
+ }
102
+ async save(sessionData) {
103
+ const sessionDir = this.getSessionDirectory(sessionData.metadata.workingDirectory);
104
+ await this.ensureDirectoryExists(sessionDir);
105
+ const filePath = this.getSessionFilePath(sessionData.metadata.id, sessionData.metadata.workingDirectory);
106
+ sessionData.metadata.updatedAt = new Date();
107
+ sessionData.metadata.totalMessages = sessionData.messages.length;
108
+ await this.writeSessionFile(filePath, sessionData);
109
+ if (this.autoCleanup) {
110
+ await this.performCleanup(sessionData.metadata.workingDirectory);
111
+ }
112
+ }
113
+ async load(sessionId, workingDirectory) {
114
+ const filePath = this.getSessionFilePath(sessionId, workingDirectory);
115
+ return await this.readSessionFile(filePath);
116
+ }
117
+ async exists(sessionId, workingDirectory) {
118
+ const filePath = this.getSessionFilePath(sessionId, workingDirectory);
119
+ try {
120
+ await fs.access(filePath);
121
+ return true;
122
+ }
123
+ catch {
124
+ return false;
125
+ }
126
+ }
127
+ async list(workingDirectory) {
128
+ const sessionDir = this.getSessionDirectory(workingDirectory);
129
+ try {
130
+ await fs.access(sessionDir);
131
+ }
132
+ catch {
133
+ return [];
134
+ }
135
+ try {
136
+ const files = await fs.readdir(sessionDir);
137
+ const sessionFiles = files.filter(file => file.endsWith('.json'));
138
+ const sessions = [];
139
+ for (const file of sessionFiles) {
140
+ const filePath = path.join(sessionDir, file);
141
+ try {
142
+ const sessionData = await this.readSessionFile(filePath);
143
+ if (sessionData) {
144
+ const lastMessage = sessionData.messages.length > 0
145
+ ? this.extractLastMessageContent(sessionData.messages[sessionData.messages.length - 1])
146
+ : undefined;
147
+ sessions.push({
148
+ id: sessionData.metadata.id,
149
+ title: sessionData.metadata.title,
150
+ createdAt: sessionData.metadata.createdAt,
151
+ updatedAt: sessionData.metadata.updatedAt,
152
+ totalMessages: sessionData.metadata.totalMessages,
153
+ lastMessage
154
+ });
155
+ }
156
+ }
157
+ catch (error) {
158
+ // Skip corrupted or unreadable session files
159
+ console.warn(`Skipping corrupted session file: ${file}`);
160
+ continue;
161
+ }
162
+ }
163
+ return sessions.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
164
+ }
165
+ catch (error) {
166
+ throw new Error(`Failed to list sessions: ${error}`);
167
+ }
168
+ }
169
+ extractLastMessageContent(message) {
170
+ if (typeof message.content === 'string') {
171
+ return message.content.length > 100
172
+ ? message.content.substring(0, 100) + '...'
173
+ : message.content;
174
+ }
175
+ if (Array.isArray(message.content)) {
176
+ const textBlocks = message.content.filter((block) => block.type === 'text');
177
+ if (textBlocks.length > 0) {
178
+ const text = textBlocks[0].text;
179
+ return text.length > 100 ? text.substring(0, 100) + '...' : text;
180
+ }
181
+ }
182
+ return '[Complex content]';
183
+ }
184
+ async delete(sessionId, workingDirectory) {
185
+ const filePath = this.getSessionFilePath(sessionId, workingDirectory);
186
+ try {
187
+ await fs.unlink(filePath);
188
+ return true;
189
+ }
190
+ catch (error) {
191
+ if (error.code === 'ENOENT') {
192
+ return false;
193
+ }
194
+ throw new Error(`Failed to delete session: ${error}`);
195
+ }
196
+ }
197
+ async getLatest(workingDirectory) {
198
+ const sessions = await this.list(workingDirectory);
199
+ if (sessions.length === 0) {
200
+ return null;
201
+ }
202
+ const latest = sessions[0];
203
+ return await this.load(latest.id, workingDirectory);
204
+ }
205
+ async cleanup(workingDirectory) {
206
+ let cleanedUp = 0;
207
+ if (workingDirectory) {
208
+ cleanedUp += await this.performCleanup(workingDirectory);
209
+ }
210
+ else {
211
+ try {
212
+ const workingDirs = await fs.readdir(this.baseDirectory);
213
+ for (const dir of workingDirs) {
214
+ const fullPath = path.join(this.baseDirectory, dir);
215
+ const stats = await fs.stat(fullPath);
216
+ if (stats.isDirectory()) {
217
+ cleanedUp += await this.performCleanup(fullPath);
218
+ }
219
+ }
220
+ }
221
+ catch (error) {
222
+ // Ignore if base directory doesn't exist
223
+ }
224
+ }
225
+ return cleanedUp;
226
+ }
227
+ async performCleanup(workingDirectory) {
228
+ const cutoffDate = new Date();
229
+ cutoffDate.setDate(cutoffDate.getDate() - this.cleanupAfterDays);
230
+ const sessions = await this.list(workingDirectory);
231
+ let cleanedUp = 0;
232
+ const sessionsToDelete = sessions.filter(session => session.updatedAt < cutoffDate);
233
+ if (sessions.length - sessionsToDelete.length > this.maxSessions) {
234
+ const excessSessions = sessions
235
+ .slice(this.maxSessions)
236
+ .filter(session => !sessionsToDelete.includes(session));
237
+ sessionsToDelete.push(...excessSessions);
238
+ }
239
+ for (const session of sessionsToDelete) {
240
+ if (await this.delete(session.id, workingDirectory)) {
241
+ cleanedUp++;
242
+ }
243
+ }
244
+ return cleanedUp;
245
+ }
246
+ }
247
+ exports.FileSystemSessionStorage = FileSystemSessionStorage;
248
+ //# sourceMappingURL=filesystem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.js","sourceRoot":"","sources":["../../src/storage/filesystem.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { Message } from '@majkapp/majk-chat-core';
2
+ export interface SessionMetadata {
3
+ id: string;
4
+ workingDirectory: string;
5
+ createdAt: Date;
6
+ updatedAt: Date;
7
+ title?: string;
8
+ provider?: string;
9
+ model?: string;
10
+ totalMessages: number;
11
+ }
12
+ export interface SessionData {
13
+ metadata: SessionMetadata;
14
+ messages: Message[];
15
+ context?: Record<string, any>;
16
+ }
17
+ export interface SessionListItem {
18
+ id: string;
19
+ title?: string;
20
+ createdAt: Date;
21
+ updatedAt: Date;
22
+ totalMessages: number;
23
+ lastMessage?: string;
24
+ }
25
+ export interface SessionStorageConfig {
26
+ baseDirectory?: string;
27
+ maxSessions?: number;
28
+ autoCleanup?: boolean;
29
+ cleanupAfterDays?: number;
30
+ }
31
+ export interface SessionStorage {
32
+ save(sessionData: SessionData): Promise<void>;
33
+ load(sessionId: string, workingDirectory: string): Promise<SessionData | null>;
34
+ exists(sessionId: string, workingDirectory: string): Promise<boolean>;
35
+ list(workingDirectory: string): Promise<SessionListItem[]>;
36
+ delete(sessionId: string, workingDirectory: string): Promise<boolean>;
37
+ getLatest(workingDirectory: string): Promise<SessionData | null>;
38
+ cleanup(workingDirectory?: string): Promise<number>;
39
+ }
40
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/types/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC/E,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtE,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAC3D,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtE,SAAS,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACjE,OAAO,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACrD"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/types/session.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@majkapp/majk-chat-sessions",
3
+ "version": "1.0.0",
4
+ "description": "Session management for Magic Chat conversations",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "test": "jest",
10
+ "test:watch": "jest --watch",
11
+ "clean": "rm -rf dist",
12
+ "prepare": "echo 'Use npm run build from root to build all packages'"
13
+ },
14
+ "keywords": [
15
+ "majk-chat",
16
+ "sessions",
17
+ "conversation",
18
+ "storage"
19
+ ],
20
+ "author": "Jules White",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "@majkapp/majk-chat-core": "file:../majk-chat-core",
24
+ "uuid": "^9.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/jest": "^29.5.12",
28
+ "@types/node": "^20.14.0",
29
+ "@types/uuid": "^9.0.0",
30
+ "jest": "^29.7.0",
31
+ "ts-jest": "^29.1.5",
32
+ "typescript": "^5.5.0"
33
+ },
34
+ "engines": {
35
+ "node": ">=18.0.0"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "files": [
41
+ "dist/**/*",
42
+ "README.md"
43
+ ]
44
+ }