claude-threads 0.15.0 → 0.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +5 -5
- package/dist/index.js +20397 -387
- package/dist/mcp/permission-server.js +34039 -139
- package/package.json +14 -18
- package/dist/changelog.d.ts +0 -20
- package/dist/changelog.js +0 -134
- package/dist/claude/cli.d.ts +0 -50
- package/dist/claude/cli.js +0 -181
- package/dist/config/migration.d.ts +0 -45
- package/dist/config/migration.js +0 -35
- package/dist/config.d.ts +0 -21
- package/dist/config.js +0 -7
- package/dist/git/worktree.d.ts +0 -46
- package/dist/git/worktree.js +0 -228
- package/dist/index.d.ts +0 -2
- package/dist/logo.d.ts +0 -14
- package/dist/logo.js +0 -41
- package/dist/mattermost/api.d.ts +0 -85
- package/dist/mattermost/api.js +0 -124
- package/dist/mattermost/api.test.d.ts +0 -1
- package/dist/mattermost/api.test.js +0 -319
- package/dist/mcp/permission-server.d.ts +0 -2
- package/dist/onboarding.d.ts +0 -1
- package/dist/onboarding.js +0 -318
- package/dist/persistence/session-store.d.ts +0 -71
- package/dist/persistence/session-store.js +0 -152
- package/dist/platform/client.d.ts +0 -140
- package/dist/platform/client.js +0 -1
- package/dist/platform/formatter.d.ts +0 -74
- package/dist/platform/formatter.js +0 -1
- package/dist/platform/index.d.ts +0 -11
- package/dist/platform/index.js +0 -8
- package/dist/platform/mattermost/client.d.ts +0 -70
- package/dist/platform/mattermost/client.js +0 -404
- package/dist/platform/mattermost/formatter.d.ts +0 -20
- package/dist/platform/mattermost/formatter.js +0 -46
- package/dist/platform/mattermost/permission-api.d.ts +0 -10
- package/dist/platform/mattermost/permission-api.js +0 -139
- package/dist/platform/mattermost/types.d.ts +0 -71
- package/dist/platform/mattermost/types.js +0 -1
- package/dist/platform/permission-api-factory.d.ts +0 -11
- package/dist/platform/permission-api-factory.js +0 -21
- package/dist/platform/permission-api.d.ts +0 -67
- package/dist/platform/permission-api.js +0 -8
- package/dist/platform/types.d.ts +0 -70
- package/dist/platform/types.js +0 -7
- package/dist/session/commands.d.ts +0 -52
- package/dist/session/commands.js +0 -323
- package/dist/session/events.d.ts +0 -25
- package/dist/session/events.js +0 -368
- package/dist/session/index.d.ts +0 -7
- package/dist/session/index.js +0 -6
- package/dist/session/lifecycle.d.ts +0 -70
- package/dist/session/lifecycle.js +0 -456
- package/dist/session/manager.d.ts +0 -96
- package/dist/session/manager.js +0 -537
- package/dist/session/reactions.d.ts +0 -25
- package/dist/session/reactions.js +0 -151
- package/dist/session/streaming.d.ts +0 -47
- package/dist/session/streaming.js +0 -152
- package/dist/session/types.d.ts +0 -78
- package/dist/session/types.js +0 -9
- package/dist/session/worktree.d.ts +0 -56
- package/dist/session/worktree.js +0 -339
- package/dist/update-notifier.d.ts +0 -3
- package/dist/update-notifier.js +0 -41
- package/dist/utils/emoji.d.ts +0 -43
- package/dist/utils/emoji.js +0 -65
- package/dist/utils/emoji.test.d.ts +0 -1
- package/dist/utils/emoji.test.js +0 -131
- package/dist/utils/logger.d.ts +0 -34
- package/dist/utils/logger.js +0 -42
- package/dist/utils/logger.test.d.ts +0 -1
- package/dist/utils/logger.test.js +0 -121
- package/dist/utils/tool-formatter.d.ts +0 -53
- package/dist/utils/tool-formatter.js +0 -252
- package/dist/utils/tool-formatter.test.d.ts +0 -1
- package/dist/utils/tool-formatter.test.js +0 -372
package/dist/session/manager.js
DELETED
|
@@ -1,537 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SessionManager - Orchestrates Claude Code sessions across chat platforms
|
|
3
|
-
*
|
|
4
|
-
* This is the main coordinator that delegates to specialized modules:
|
|
5
|
-
* - lifecycle.ts: Session start, resume, exit
|
|
6
|
-
* - events.ts: Claude event handling
|
|
7
|
-
* - reactions.ts: User reaction handling
|
|
8
|
-
* - commands.ts: User commands (!cd, !invite, etc.)
|
|
9
|
-
* - worktree.ts: Git worktree management
|
|
10
|
-
* - streaming.ts: Message streaming and flushing
|
|
11
|
-
*/
|
|
12
|
-
import { SessionStore } from '../persistence/session-store.js';
|
|
13
|
-
import { isCancelEmoji, isEscapeEmoji, } from '../utils/emoji.js';
|
|
14
|
-
// Import extracted modules
|
|
15
|
-
import * as streaming from './streaming.js';
|
|
16
|
-
import * as events from './events.js';
|
|
17
|
-
import * as reactions from './reactions.js';
|
|
18
|
-
import * as commands from './commands.js';
|
|
19
|
-
import * as lifecycle from './lifecycle.js';
|
|
20
|
-
import * as worktreeModule from './worktree.js';
|
|
21
|
-
// Import constants for internal use
|
|
22
|
-
import { MAX_SESSIONS, SESSION_TIMEOUT_MS, SESSION_WARNING_MS } from './types.js';
|
|
23
|
-
/**
|
|
24
|
-
* SessionManager - Main orchestrator for Claude Code sessions
|
|
25
|
-
*/
|
|
26
|
-
export class SessionManager {
|
|
27
|
-
// Platform management
|
|
28
|
-
platforms = new Map();
|
|
29
|
-
workingDir;
|
|
30
|
-
skipPermissions;
|
|
31
|
-
chromeEnabled;
|
|
32
|
-
worktreeMode;
|
|
33
|
-
debug = process.env.DEBUG === '1' || process.argv.includes('--debug');
|
|
34
|
-
// Session state
|
|
35
|
-
sessions = new Map();
|
|
36
|
-
postIndex = new Map();
|
|
37
|
-
// Persistence
|
|
38
|
-
sessionStore = new SessionStore();
|
|
39
|
-
// Cleanup
|
|
40
|
-
cleanupTimer = null;
|
|
41
|
-
// Shutdown flag
|
|
42
|
-
isShuttingDown = false;
|
|
43
|
-
constructor(workingDir, skipPermissions = false, chromeEnabled = false, worktreeMode = 'prompt') {
|
|
44
|
-
this.workingDir = workingDir;
|
|
45
|
-
this.skipPermissions = skipPermissions;
|
|
46
|
-
this.chromeEnabled = chromeEnabled;
|
|
47
|
-
this.worktreeMode = worktreeMode;
|
|
48
|
-
// Start periodic cleanup
|
|
49
|
-
this.cleanupTimer = setInterval(() => {
|
|
50
|
-
lifecycle.cleanupIdleSessions(SESSION_TIMEOUT_MS, SESSION_WARNING_MS, this.getLifecycleContext());
|
|
51
|
-
}, 60000);
|
|
52
|
-
}
|
|
53
|
-
// ---------------------------------------------------------------------------
|
|
54
|
-
// Platform Management
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
addPlatform(platformId, client) {
|
|
57
|
-
this.platforms.set(platformId, client);
|
|
58
|
-
client.on('message', (post, user) => this.handleMessage(platformId, post, user));
|
|
59
|
-
client.on('reaction', (reaction, user) => {
|
|
60
|
-
if (user) {
|
|
61
|
-
this.handleReaction(platformId, reaction.postId, reaction.emojiName, user.username);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
console.log(` 📡 Platform "${platformId}" registered`);
|
|
65
|
-
}
|
|
66
|
-
removePlatform(platformId) {
|
|
67
|
-
this.platforms.delete(platformId);
|
|
68
|
-
}
|
|
69
|
-
// ---------------------------------------------------------------------------
|
|
70
|
-
// Context Builders (for module delegation)
|
|
71
|
-
// ---------------------------------------------------------------------------
|
|
72
|
-
getLifecycleContext() {
|
|
73
|
-
return {
|
|
74
|
-
workingDir: this.workingDir,
|
|
75
|
-
skipPermissions: this.skipPermissions,
|
|
76
|
-
chromeEnabled: this.chromeEnabled,
|
|
77
|
-
debug: this.debug,
|
|
78
|
-
maxSessions: MAX_SESSIONS,
|
|
79
|
-
sessions: this.sessions,
|
|
80
|
-
postIndex: this.postIndex,
|
|
81
|
-
platforms: this.platforms,
|
|
82
|
-
sessionStore: this.sessionStore,
|
|
83
|
-
isShuttingDown: this.isShuttingDown,
|
|
84
|
-
getSessionId: (pid, tid) => this.getSessionId(pid, tid),
|
|
85
|
-
findSessionByThreadId: (tid) => this.findSessionByThreadId(tid),
|
|
86
|
-
handleEvent: (sid, e) => this.handleEvent(sid, e),
|
|
87
|
-
handleExit: (sid, code) => this.handleExit(sid, code),
|
|
88
|
-
registerPost: (pid, tid) => this.registerPost(pid, tid),
|
|
89
|
-
startTyping: (s) => this.startTyping(s),
|
|
90
|
-
stopTyping: (s) => this.stopTyping(s),
|
|
91
|
-
flush: (s) => this.flush(s),
|
|
92
|
-
persistSession: (s) => this.persistSession(s),
|
|
93
|
-
unpersistSession: (sid) => this.unpersistSession(sid),
|
|
94
|
-
updateSessionHeader: (s) => this.updateSessionHeader(s),
|
|
95
|
-
shouldPromptForWorktree: (s) => this.shouldPromptForWorktree(s),
|
|
96
|
-
postWorktreePrompt: (s, r) => this.postWorktreePrompt(s, r),
|
|
97
|
-
buildMessageContent: (t, p, f) => this.buildMessageContent(t, p, f),
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
getEventContext() {
|
|
101
|
-
return {
|
|
102
|
-
debug: this.debug,
|
|
103
|
-
registerPost: (pid, tid) => this.registerPost(pid, tid),
|
|
104
|
-
flush: (s) => this.flush(s),
|
|
105
|
-
startTyping: (s) => this.startTyping(s),
|
|
106
|
-
stopTyping: (s) => this.stopTyping(s),
|
|
107
|
-
appendContent: (s, t) => this.appendContent(s, t),
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
getReactionContext() {
|
|
111
|
-
return {
|
|
112
|
-
debug: this.debug,
|
|
113
|
-
startTyping: (s) => this.startTyping(s),
|
|
114
|
-
stopTyping: (s) => this.stopTyping(s),
|
|
115
|
-
updateSessionHeader: (s) => this.updateSessionHeader(s),
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
getCommandContext() {
|
|
119
|
-
return {
|
|
120
|
-
skipPermissions: this.skipPermissions,
|
|
121
|
-
chromeEnabled: this.chromeEnabled,
|
|
122
|
-
maxSessions: MAX_SESSIONS,
|
|
123
|
-
handleEvent: (tid, e) => this.handleEvent(tid, e),
|
|
124
|
-
handleExit: (tid, code) => this.handleExit(tid, code),
|
|
125
|
-
flush: (s) => this.flush(s),
|
|
126
|
-
startTyping: (s) => this.startTyping(s),
|
|
127
|
-
stopTyping: (s) => this.stopTyping(s),
|
|
128
|
-
persistSession: (s) => this.persistSession(s),
|
|
129
|
-
killSession: (tid) => this.killSession(tid),
|
|
130
|
-
registerPost: (pid, tid) => this.registerPost(pid, tid),
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
// ---------------------------------------------------------------------------
|
|
134
|
-
// Session ID and Post Index
|
|
135
|
-
// ---------------------------------------------------------------------------
|
|
136
|
-
getSessionId(platformId, threadId) {
|
|
137
|
-
return `${platformId}:${threadId}`;
|
|
138
|
-
}
|
|
139
|
-
registerPost(postId, threadId) {
|
|
140
|
-
this.postIndex.set(postId, threadId);
|
|
141
|
-
}
|
|
142
|
-
getSessionByPost(postId) {
|
|
143
|
-
const threadId = this.postIndex.get(postId);
|
|
144
|
-
if (!threadId)
|
|
145
|
-
return undefined;
|
|
146
|
-
return this.findSessionByThreadId(threadId);
|
|
147
|
-
}
|
|
148
|
-
// ---------------------------------------------------------------------------
|
|
149
|
-
// Message Handling
|
|
150
|
-
// ---------------------------------------------------------------------------
|
|
151
|
-
async handleMessage(_platformId, _post, _user) {
|
|
152
|
-
// Message handling is done by the platform client routing to startSession/sendFollowUp
|
|
153
|
-
// This is just a placeholder for the event subscription
|
|
154
|
-
}
|
|
155
|
-
// ---------------------------------------------------------------------------
|
|
156
|
-
// Reaction Handling
|
|
157
|
-
// ---------------------------------------------------------------------------
|
|
158
|
-
async handleReaction(platformId, postId, emojiName, username) {
|
|
159
|
-
const session = this.getSessionByPost(postId);
|
|
160
|
-
if (!session)
|
|
161
|
-
return;
|
|
162
|
-
// Verify this reaction is from the same platform
|
|
163
|
-
if (session.platformId !== platformId)
|
|
164
|
-
return;
|
|
165
|
-
// Only process reactions from allowed users
|
|
166
|
-
if (!session.sessionAllowedUsers.has(username) && !session.platform.isUserAllowed(username)) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
await this.handleSessionReaction(session, postId, emojiName, username);
|
|
170
|
-
}
|
|
171
|
-
async handleSessionReaction(session, postId, emojiName, username) {
|
|
172
|
-
// Handle ❌ on worktree prompt
|
|
173
|
-
if (session.worktreePromptPostId === postId && emojiName === 'x') {
|
|
174
|
-
await worktreeModule.handleWorktreeSkip(session, username, (s) => this.persistSession(s), (s) => this.startTyping(s));
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
// Handle cancel/escape reactions on session start post
|
|
178
|
-
if (session.sessionStartPostId === postId) {
|
|
179
|
-
if (isCancelEmoji(emojiName)) {
|
|
180
|
-
await commands.cancelSession(session, username, this.getCommandContext());
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
if (isEscapeEmoji(emojiName)) {
|
|
184
|
-
await commands.interruptSession(session, username);
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
// Handle question reactions
|
|
189
|
-
if (session.pendingQuestionSet?.currentPostId === postId) {
|
|
190
|
-
await reactions.handleQuestionReaction(session, postId, emojiName, username, this.getReactionContext());
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
// Handle plan approval reactions
|
|
194
|
-
if (session.pendingApproval?.postId === postId) {
|
|
195
|
-
await reactions.handleApprovalReaction(session, emojiName, username, this.getReactionContext());
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
// Handle message approval reactions
|
|
199
|
-
if (session.pendingMessageApproval?.postId === postId) {
|
|
200
|
-
await reactions.handleMessageApprovalReaction(session, emojiName, username, this.getReactionContext());
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
// ---------------------------------------------------------------------------
|
|
205
|
-
// Event Handling (delegates to events module)
|
|
206
|
-
// ---------------------------------------------------------------------------
|
|
207
|
-
handleEvent(sessionId, event) {
|
|
208
|
-
const session = this.sessions.get(sessionId);
|
|
209
|
-
if (!session)
|
|
210
|
-
return;
|
|
211
|
-
events.handleEvent(session, event, this.getEventContext());
|
|
212
|
-
}
|
|
213
|
-
// ---------------------------------------------------------------------------
|
|
214
|
-
// Exit Handling (delegates to lifecycle module)
|
|
215
|
-
// ---------------------------------------------------------------------------
|
|
216
|
-
async handleExit(sessionId, code) {
|
|
217
|
-
await lifecycle.handleExit(sessionId, code, this.getLifecycleContext());
|
|
218
|
-
}
|
|
219
|
-
// ---------------------------------------------------------------------------
|
|
220
|
-
// Streaming utilities (delegates to streaming module)
|
|
221
|
-
// ---------------------------------------------------------------------------
|
|
222
|
-
appendContent(session, text) {
|
|
223
|
-
if (!text)
|
|
224
|
-
return;
|
|
225
|
-
session.pendingContent += text + '\n';
|
|
226
|
-
streaming.scheduleUpdate(session, (s) => this.flush(s));
|
|
227
|
-
}
|
|
228
|
-
async flush(session) {
|
|
229
|
-
await streaming.flush(session, (pid, tid) => this.registerPost(pid, tid));
|
|
230
|
-
}
|
|
231
|
-
startTyping(session) {
|
|
232
|
-
streaming.startTyping(session);
|
|
233
|
-
}
|
|
234
|
-
stopTyping(session) {
|
|
235
|
-
streaming.stopTyping(session);
|
|
236
|
-
}
|
|
237
|
-
async buildMessageContent(text, platform, files) {
|
|
238
|
-
return streaming.buildMessageContent(text, platform, files, this.debug);
|
|
239
|
-
}
|
|
240
|
-
// ---------------------------------------------------------------------------
|
|
241
|
-
// Worktree utilities
|
|
242
|
-
// ---------------------------------------------------------------------------
|
|
243
|
-
async shouldPromptForWorktree(session) {
|
|
244
|
-
return worktreeModule.shouldPromptForWorktree(session, this.worktreeMode, (repoRoot, excludeId) => this.hasOtherSessionInRepo(repoRoot, excludeId));
|
|
245
|
-
}
|
|
246
|
-
hasOtherSessionInRepo(repoRoot, excludeThreadId) {
|
|
247
|
-
for (const session of this.sessions.values()) {
|
|
248
|
-
// Skip the session we're checking from (compare raw threadIds)
|
|
249
|
-
if (session.threadId === excludeThreadId)
|
|
250
|
-
continue;
|
|
251
|
-
if (session.workingDir === repoRoot)
|
|
252
|
-
return true;
|
|
253
|
-
if (session.worktreeInfo?.repoRoot === repoRoot)
|
|
254
|
-
return true;
|
|
255
|
-
}
|
|
256
|
-
return false;
|
|
257
|
-
}
|
|
258
|
-
async postWorktreePrompt(session, reason) {
|
|
259
|
-
await worktreeModule.postWorktreePrompt(session, reason, (pid, tid) => this.registerPost(pid, tid));
|
|
260
|
-
this.stopTyping(session);
|
|
261
|
-
}
|
|
262
|
-
// ---------------------------------------------------------------------------
|
|
263
|
-
// Persistence
|
|
264
|
-
// ---------------------------------------------------------------------------
|
|
265
|
-
persistSession(session) {
|
|
266
|
-
const state = {
|
|
267
|
-
platformId: session.platformId,
|
|
268
|
-
threadId: session.threadId,
|
|
269
|
-
claudeSessionId: session.claudeSessionId,
|
|
270
|
-
startedBy: session.startedBy,
|
|
271
|
-
startedAt: session.startedAt.toISOString(),
|
|
272
|
-
lastActivityAt: session.lastActivityAt.toISOString(),
|
|
273
|
-
sessionNumber: session.sessionNumber,
|
|
274
|
-
workingDir: session.workingDir,
|
|
275
|
-
planApproved: session.planApproved,
|
|
276
|
-
sessionAllowedUsers: [...session.sessionAllowedUsers],
|
|
277
|
-
forceInteractivePermissions: session.forceInteractivePermissions,
|
|
278
|
-
sessionStartPostId: session.sessionStartPostId,
|
|
279
|
-
tasksPostId: session.tasksPostId,
|
|
280
|
-
worktreeInfo: session.worktreeInfo,
|
|
281
|
-
pendingWorktreePrompt: session.pendingWorktreePrompt,
|
|
282
|
-
worktreePromptDisabled: session.worktreePromptDisabled,
|
|
283
|
-
queuedPrompt: session.queuedPrompt,
|
|
284
|
-
};
|
|
285
|
-
this.sessionStore.save(session.sessionId, state);
|
|
286
|
-
}
|
|
287
|
-
unpersistSession(sessionId) {
|
|
288
|
-
this.sessionStore.remove(sessionId);
|
|
289
|
-
}
|
|
290
|
-
// ---------------------------------------------------------------------------
|
|
291
|
-
// Session Header
|
|
292
|
-
// ---------------------------------------------------------------------------
|
|
293
|
-
async updateSessionHeader(session) {
|
|
294
|
-
await commands.updateSessionHeader(session, this.getCommandContext());
|
|
295
|
-
}
|
|
296
|
-
// ---------------------------------------------------------------------------
|
|
297
|
-
// Public API
|
|
298
|
-
// ---------------------------------------------------------------------------
|
|
299
|
-
async initialize() {
|
|
300
|
-
const persisted = this.sessionStore.load();
|
|
301
|
-
if (persisted.size === 0)
|
|
302
|
-
return;
|
|
303
|
-
console.log(` 🔄 Found ${persisted.size} persisted session(s), attempting resume...`);
|
|
304
|
-
for (const state of persisted.values()) {
|
|
305
|
-
await lifecycle.resumeSession(state, this.getLifecycleContext());
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
async startSession(options, username, replyToPostId, platformId = 'default') {
|
|
309
|
-
await lifecycle.startSession(options, username, replyToPostId, platformId, this.getLifecycleContext());
|
|
310
|
-
}
|
|
311
|
-
// Helper to find session by threadId (sessions are keyed by composite platformId:threadId)
|
|
312
|
-
findSessionByThreadId(threadId) {
|
|
313
|
-
for (const session of this.sessions.values()) {
|
|
314
|
-
if (session.threadId === threadId) {
|
|
315
|
-
return session;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
return undefined;
|
|
319
|
-
}
|
|
320
|
-
// Helper to find persisted session by threadId (persisted sessions are keyed by composite sessionId)
|
|
321
|
-
findPersistedByThreadId(threadId) {
|
|
322
|
-
const persisted = this.sessionStore.load();
|
|
323
|
-
for (const session of persisted.values()) {
|
|
324
|
-
if (session.threadId === threadId) {
|
|
325
|
-
return session;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
return undefined;
|
|
329
|
-
}
|
|
330
|
-
async sendFollowUp(threadId, message, files) {
|
|
331
|
-
const session = this.findSessionByThreadId(threadId);
|
|
332
|
-
if (!session || !session.claude.isRunning())
|
|
333
|
-
return;
|
|
334
|
-
await lifecycle.sendFollowUp(session, message, files, this.getLifecycleContext());
|
|
335
|
-
}
|
|
336
|
-
isSessionActive() {
|
|
337
|
-
return this.sessions.size > 0;
|
|
338
|
-
}
|
|
339
|
-
isInSessionThread(threadRoot) {
|
|
340
|
-
const session = this.findSessionByThreadId(threadRoot);
|
|
341
|
-
return session !== undefined && session.claude.isRunning();
|
|
342
|
-
}
|
|
343
|
-
hasPausedSession(threadId) {
|
|
344
|
-
if (this.findSessionByThreadId(threadId))
|
|
345
|
-
return false;
|
|
346
|
-
return this.findPersistedByThreadId(threadId) !== undefined;
|
|
347
|
-
}
|
|
348
|
-
async resumePausedSession(threadId, message, files) {
|
|
349
|
-
await lifecycle.resumePausedSession(threadId, message, files, this.getLifecycleContext());
|
|
350
|
-
}
|
|
351
|
-
getPersistedSession(threadId) {
|
|
352
|
-
return this.findPersistedByThreadId(threadId);
|
|
353
|
-
}
|
|
354
|
-
killSession(threadId, unpersist = true) {
|
|
355
|
-
const session = this.findSessionByThreadId(threadId);
|
|
356
|
-
if (!session)
|
|
357
|
-
return;
|
|
358
|
-
lifecycle.killSession(session, unpersist, this.getLifecycleContext());
|
|
359
|
-
}
|
|
360
|
-
killAllSessions() {
|
|
361
|
-
lifecycle.killAllSessions(this.getLifecycleContext());
|
|
362
|
-
}
|
|
363
|
-
// Commands
|
|
364
|
-
async cancelSession(threadId, username) {
|
|
365
|
-
const session = this.findSessionByThreadId(threadId);
|
|
366
|
-
if (!session)
|
|
367
|
-
return;
|
|
368
|
-
await commands.cancelSession(session, username, this.getCommandContext());
|
|
369
|
-
}
|
|
370
|
-
async interruptSession(threadId, username) {
|
|
371
|
-
const session = this.findSessionByThreadId(threadId);
|
|
372
|
-
if (!session)
|
|
373
|
-
return;
|
|
374
|
-
await commands.interruptSession(session, username);
|
|
375
|
-
}
|
|
376
|
-
async changeDirectory(threadId, newDir, username) {
|
|
377
|
-
const session = this.findSessionByThreadId(threadId);
|
|
378
|
-
if (!session)
|
|
379
|
-
return;
|
|
380
|
-
await commands.changeDirectory(session, newDir, username, this.getCommandContext());
|
|
381
|
-
}
|
|
382
|
-
async inviteUser(threadId, invitedUser, invitedBy) {
|
|
383
|
-
const session = this.findSessionByThreadId(threadId);
|
|
384
|
-
if (!session)
|
|
385
|
-
return;
|
|
386
|
-
await commands.inviteUser(session, invitedUser, invitedBy, this.getCommandContext());
|
|
387
|
-
}
|
|
388
|
-
async kickUser(threadId, kickedUser, kickedBy) {
|
|
389
|
-
const session = this.findSessionByThreadId(threadId);
|
|
390
|
-
if (!session)
|
|
391
|
-
return;
|
|
392
|
-
await commands.kickUser(session, kickedUser, kickedBy, this.getCommandContext());
|
|
393
|
-
}
|
|
394
|
-
async enableInteractivePermissions(threadId, username) {
|
|
395
|
-
const session = this.findSessionByThreadId(threadId);
|
|
396
|
-
if (!session)
|
|
397
|
-
return;
|
|
398
|
-
await commands.enableInteractivePermissions(session, username, this.getCommandContext());
|
|
399
|
-
}
|
|
400
|
-
isSessionInteractive(threadId) {
|
|
401
|
-
const session = this.findSessionByThreadId(threadId);
|
|
402
|
-
if (!session)
|
|
403
|
-
return !this.skipPermissions;
|
|
404
|
-
if (!this.skipPermissions)
|
|
405
|
-
return true;
|
|
406
|
-
return session.forceInteractivePermissions;
|
|
407
|
-
}
|
|
408
|
-
async requestMessageApproval(threadId, username, message) {
|
|
409
|
-
const session = this.findSessionByThreadId(threadId);
|
|
410
|
-
if (!session)
|
|
411
|
-
return;
|
|
412
|
-
await commands.requestMessageApproval(session, username, message, this.getCommandContext());
|
|
413
|
-
}
|
|
414
|
-
// Worktree commands
|
|
415
|
-
async handleWorktreeBranchResponse(threadId, branchName, username) {
|
|
416
|
-
const session = this.findSessionByThreadId(threadId);
|
|
417
|
-
if (!session)
|
|
418
|
-
return false;
|
|
419
|
-
return worktreeModule.handleWorktreeBranchResponse(session, branchName, username, (tid, branch, user) => this.createAndSwitchToWorktree(tid, branch, user));
|
|
420
|
-
}
|
|
421
|
-
async handleWorktreeSkip(threadId, username) {
|
|
422
|
-
const session = this.findSessionByThreadId(threadId);
|
|
423
|
-
if (!session)
|
|
424
|
-
return;
|
|
425
|
-
await worktreeModule.handleWorktreeSkip(session, username, (s) => this.persistSession(s), (s) => this.startTyping(s));
|
|
426
|
-
}
|
|
427
|
-
async createAndSwitchToWorktree(threadId, branch, username) {
|
|
428
|
-
const session = this.findSessionByThreadId(threadId);
|
|
429
|
-
if (!session)
|
|
430
|
-
return;
|
|
431
|
-
await worktreeModule.createAndSwitchToWorktree(session, branch, username, {
|
|
432
|
-
skipPermissions: this.skipPermissions,
|
|
433
|
-
chromeEnabled: this.chromeEnabled,
|
|
434
|
-
handleEvent: (tid, e) => this.handleEvent(tid, e),
|
|
435
|
-
handleExit: (tid, code) => this.handleExit(tid, code),
|
|
436
|
-
updateSessionHeader: (s) => this.updateSessionHeader(s),
|
|
437
|
-
flush: (s) => this.flush(s),
|
|
438
|
-
persistSession: (s) => this.persistSession(s),
|
|
439
|
-
startTyping: (s) => this.startTyping(s),
|
|
440
|
-
stopTyping: (s) => this.stopTyping(s),
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
async switchToWorktree(threadId, branchOrPath, username) {
|
|
444
|
-
const session = this.findSessionByThreadId(threadId);
|
|
445
|
-
if (!session)
|
|
446
|
-
return;
|
|
447
|
-
await worktreeModule.switchToWorktree(session, branchOrPath, username, (tid, dir, user) => this.changeDirectory(tid, dir, user));
|
|
448
|
-
}
|
|
449
|
-
async listWorktreesCommand(threadId, _username) {
|
|
450
|
-
const session = this.findSessionByThreadId(threadId);
|
|
451
|
-
if (!session)
|
|
452
|
-
return;
|
|
453
|
-
await worktreeModule.listWorktreesCommand(session);
|
|
454
|
-
}
|
|
455
|
-
async removeWorktreeCommand(threadId, branchOrPath, username) {
|
|
456
|
-
const session = this.findSessionByThreadId(threadId);
|
|
457
|
-
if (!session)
|
|
458
|
-
return;
|
|
459
|
-
await worktreeModule.removeWorktreeCommand(session, branchOrPath, username);
|
|
460
|
-
}
|
|
461
|
-
async disableWorktreePrompt(threadId, username) {
|
|
462
|
-
const session = this.findSessionByThreadId(threadId);
|
|
463
|
-
if (!session)
|
|
464
|
-
return;
|
|
465
|
-
await worktreeModule.disableWorktreePrompt(session, username, (s) => this.persistSession(s));
|
|
466
|
-
}
|
|
467
|
-
hasPendingWorktreePrompt(threadId) {
|
|
468
|
-
const session = this.findSessionByThreadId(threadId);
|
|
469
|
-
return session?.pendingWorktreePrompt === true;
|
|
470
|
-
}
|
|
471
|
-
// Missing public methods needed by index.ts
|
|
472
|
-
getActiveThreadIds() {
|
|
473
|
-
// Return raw threadIds (not composite sessionIds) for posting to chat
|
|
474
|
-
return [...this.sessions.values()].map(s => s.threadId);
|
|
475
|
-
}
|
|
476
|
-
killAllSessionsAndUnpersist() {
|
|
477
|
-
for (const session of this.sessions.values()) {
|
|
478
|
-
this.stopTyping(session);
|
|
479
|
-
session.claude.kill();
|
|
480
|
-
this.unpersistSession(session.sessionId);
|
|
481
|
-
}
|
|
482
|
-
this.sessions.clear();
|
|
483
|
-
this.postIndex.clear();
|
|
484
|
-
}
|
|
485
|
-
isUserAllowedInSession(threadId, username) {
|
|
486
|
-
const session = this.findSessionByThreadId(threadId);
|
|
487
|
-
if (!session) {
|
|
488
|
-
// Check persisted session
|
|
489
|
-
const persisted = this.getPersistedSession(threadId);
|
|
490
|
-
if (persisted) {
|
|
491
|
-
return persisted.sessionAllowedUsers.includes(username) ||
|
|
492
|
-
this.platforms.get(persisted.platformId)?.isUserAllowed(username) || false;
|
|
493
|
-
}
|
|
494
|
-
return false;
|
|
495
|
-
}
|
|
496
|
-
return session.sessionAllowedUsers.has(username) || session.platform.isUserAllowed(username);
|
|
497
|
-
}
|
|
498
|
-
async startSessionWithWorktree(options, branch, username, replyToPostId, platformId = 'default') {
|
|
499
|
-
// Start normal session first
|
|
500
|
-
await this.startSession(options, username, replyToPostId, platformId);
|
|
501
|
-
// Then switch to worktree
|
|
502
|
-
const threadId = replyToPostId || '';
|
|
503
|
-
const session = this.sessions.get(this.getSessionId(platformId, threadId));
|
|
504
|
-
if (session) {
|
|
505
|
-
await this.createAndSwitchToWorktree(session.threadId, branch, username);
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
setShuttingDown() {
|
|
509
|
-
this.isShuttingDown = true;
|
|
510
|
-
}
|
|
511
|
-
// Shutdown
|
|
512
|
-
async shutdown(message) {
|
|
513
|
-
this.isShuttingDown = true;
|
|
514
|
-
if (this.cleanupTimer) {
|
|
515
|
-
clearInterval(this.cleanupTimer);
|
|
516
|
-
this.cleanupTimer = null;
|
|
517
|
-
}
|
|
518
|
-
// Post shutdown message to all active sessions
|
|
519
|
-
if (message) {
|
|
520
|
-
for (const session of this.sessions.values()) {
|
|
521
|
-
try {
|
|
522
|
-
await session.platform.createPost(message, session.threadId);
|
|
523
|
-
}
|
|
524
|
-
catch {
|
|
525
|
-
// Ignore
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
// Kill all sessions but preserve persistence
|
|
530
|
-
for (const session of this.sessions.values()) {
|
|
531
|
-
this.stopTyping(session);
|
|
532
|
-
session.claude.kill();
|
|
533
|
-
}
|
|
534
|
-
this.sessions.clear();
|
|
535
|
-
this.postIndex.clear();
|
|
536
|
-
}
|
|
537
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* User reaction handling module
|
|
3
|
-
*
|
|
4
|
-
* Handles emoji reactions on posts: plan approval, question answers,
|
|
5
|
-
* message approval, cancel/escape actions.
|
|
6
|
-
*/
|
|
7
|
-
import type { Session } from './types.js';
|
|
8
|
-
export interface ReactionContext {
|
|
9
|
-
debug: boolean;
|
|
10
|
-
startTyping: (session: Session) => void;
|
|
11
|
-
stopTyping: (session: Session) => void;
|
|
12
|
-
updateSessionHeader: (session: Session) => Promise<void>;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Handle a reaction on a question post (number emoji to select an option).
|
|
16
|
-
*/
|
|
17
|
-
export declare function handleQuestionReaction(session: Session, postId: string, emojiName: string, username: string, ctx: ReactionContext): Promise<void>;
|
|
18
|
-
/**
|
|
19
|
-
* Handle a reaction on a plan approval post (thumbs up/down).
|
|
20
|
-
*/
|
|
21
|
-
export declare function handleApprovalReaction(session: Session, emojiName: string, username: string, ctx: ReactionContext): Promise<void>;
|
|
22
|
-
/**
|
|
23
|
-
* Handle a reaction on a message approval post (approve/invite/deny).
|
|
24
|
-
*/
|
|
25
|
-
export declare function handleMessageApprovalReaction(session: Session, emoji: string, approver: string, ctx: ReactionContext): Promise<void>;
|