@yeaft/webchat-agent 0.1.124 → 0.1.125

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,184 +0,0 @@
1
- /**
2
- * RolePlay Session — session index management for .roleplay/session.json.
3
- *
4
- * Analogous to agent/crew/session.js but much simpler:
5
- * - No multi-process concerns (single process)
6
- * - No worktree management
7
- * - Atomic write for crash safety
8
- *
9
- * session.json schema:
10
- * {
11
- * sessions: [{ name, teamType, language, projectDir, conversationId,
12
- * roles, createdAt, updatedAt, status }],
13
- * activeSession: string | null
14
- * }
15
- */
16
- import { promises as fs } from 'fs';
17
- import { join } from 'path';
18
- import { existsSync, readFileSync, writeFileSync, renameSync } from 'fs';
19
-
20
- const SESSION_FILE = 'session.json';
21
-
22
- // ─── Read / Write ───────────────────────────────────────────────────
23
-
24
- /**
25
- * Load .roleplay/session.json.
26
- * Returns { sessions: [], activeSession: null } if file doesn't exist or is corrupt.
27
- *
28
- * @param {string} projectDir
29
- * @returns {{ sessions: Array, activeSession: string|null }}
30
- */
31
- export function loadRolePlaySessionIndex(projectDir) {
32
- const filePath = join(projectDir, '.roleplay', SESSION_FILE);
33
- if (!existsSync(filePath)) {
34
- return { sessions: [], activeSession: null };
35
- }
36
- try {
37
- const raw = readFileSync(filePath, 'utf-8');
38
- const data = JSON.parse(raw);
39
- return {
40
- sessions: Array.isArray(data.sessions) ? data.sessions : [],
41
- activeSession: data.activeSession || null,
42
- };
43
- } catch (e) {
44
- console.warn('[RolePlaySession] Failed to load session.json:', e.message);
45
- return { sessions: [], activeSession: null };
46
- }
47
- }
48
-
49
- /**
50
- * Save .roleplay/session.json atomically (write tmp → rename).
51
- *
52
- * @param {string} projectDir
53
- * @param {{ sessions: Array, activeSession: string|null }} data
54
- */
55
- export async function saveRolePlaySessionIndex(projectDir, data) {
56
- const rpDir = join(projectDir, '.roleplay');
57
- const filePath = join(rpDir, SESSION_FILE);
58
- const tmpPath = `${filePath}.tmp.${Date.now()}`;
59
-
60
- const content = JSON.stringify(data, null, 2);
61
- try {
62
- await fs.mkdir(rpDir, { recursive: true });
63
- await fs.writeFile(tmpPath, content);
64
- await fs.rename(tmpPath, filePath);
65
- } catch (e) {
66
- console.error('[RolePlaySession] Failed to save session.json:', e.message);
67
- // Clean up tmp file if rename failed
68
- try { await fs.unlink(tmpPath); } catch {}
69
- throw e;
70
- }
71
- }
72
-
73
- // ─── Session CRUD ───────────────────────────────────────────────────
74
-
75
- /**
76
- * Add a new session entry to .roleplay/session.json.
77
- * Sets it as the activeSession.
78
- *
79
- * @param {string} projectDir
80
- * @param {object} session - session entry to add:
81
- * { name, teamType, language, projectDir, conversationId, roles, createdAt }
82
- * @returns {object} the session entry with updatedAt/status fields added
83
- */
84
- export async function addRolePlaySession(projectDir, session) {
85
- const index = loadRolePlaySessionIndex(projectDir);
86
-
87
- // Check for duplicate session name
88
- const existing = index.sessions.find(s => s.name === session.name);
89
- if (existing) {
90
- // Update existing entry instead of creating duplicate
91
- Object.assign(existing, {
92
- ...session,
93
- updatedAt: Date.now(),
94
- status: 'active',
95
- });
96
- index.activeSession = session.name;
97
- await saveRolePlaySessionIndex(projectDir, index);
98
- return existing;
99
- }
100
-
101
- const entry = {
102
- ...session,
103
- updatedAt: Date.now(),
104
- status: 'active',
105
- };
106
-
107
- index.sessions.push(entry);
108
- index.activeSession = session.name;
109
- await saveRolePlaySessionIndex(projectDir, index);
110
- return entry;
111
- }
112
-
113
- /**
114
- * Remove (archive) a session from .roleplay/session.json.
115
- * The session entry is marked as 'archived', not deleted,
116
- * so the context/ directory content remains valid.
117
- *
118
- * Optionally removes the session's roles/ directory.
119
- *
120
- * @param {string} projectDir
121
- * @param {string} sessionName
122
- * @param {object} [options]
123
- * @param {boolean} [options.removeDir=true] - remove .roleplay/roles/{sessionName}/
124
- */
125
- export async function removeRolePlaySession(projectDir, sessionName, options = {}) {
126
- const { removeDir = true } = options;
127
- const index = loadRolePlaySessionIndex(projectDir);
128
-
129
- const session = index.sessions.find(s => s.name === sessionName);
130
- if (session) {
131
- session.status = 'archived';
132
- session.updatedAt = Date.now();
133
- }
134
-
135
- // Clear activeSession if it was the removed one
136
- if (index.activeSession === sessionName) {
137
- // Pick next active session (most recently updated non-archived)
138
- const active = index.sessions
139
- .filter(s => s.status === 'active')
140
- .sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
141
- index.activeSession = active[0]?.name || null;
142
- }
143
-
144
- await saveRolePlaySessionIndex(projectDir, index);
145
-
146
- // Remove the session directory (but keep context/)
147
- if (removeDir) {
148
- const sessionDir = join(projectDir, '.roleplay', 'roles', sessionName);
149
- try {
150
- await fs.rm(sessionDir, { recursive: true, force: true });
151
- } catch {
152
- // Not critical — may not exist
153
- }
154
- }
155
- }
156
-
157
- /**
158
- * Find a session entry by conversationId.
159
- *
160
- * @param {string} projectDir
161
- * @param {string} conversationId
162
- * @returns {object|null} session entry or null
163
- */
164
- export function findRolePlaySessionByConversationId(projectDir, conversationId) {
165
- if (!conversationId) return null;
166
- const index = loadRolePlaySessionIndex(projectDir);
167
- return index.sessions.find(s => s.conversationId === conversationId) || null;
168
- }
169
-
170
- /**
171
- * Update the activeSession pointer and updatedAt timestamp.
172
- *
173
- * @param {string} projectDir
174
- * @param {string} sessionName
175
- */
176
- export async function setActiveRolePlaySession(projectDir, sessionName) {
177
- const index = loadRolePlaySessionIndex(projectDir);
178
- const session = index.sessions.find(s => s.name === sessionName);
179
- if (session) {
180
- session.updatedAt = Date.now();
181
- index.activeSession = sessionName;
182
- await saveRolePlaySessionIndex(projectDir, index);
183
- }
184
- }