codekin 0.5.3 → 0.5.5
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 +2 -1
- package/dist/assets/{index-84JYN21S.js → index-BFkKlY3O.js} +46 -42
- package/dist/assets/index-CjEQkT2b.css +1 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/server/dist/approval-manager.js +1 -1
- package/server/dist/approval-manager.js.map +1 -1
- package/server/dist/auth-routes.js +31 -1
- package/server/dist/auth-routes.js.map +1 -1
- package/server/dist/claude-process.d.ts +26 -0
- package/server/dist/claude-process.js +111 -18
- package/server/dist/claude-process.js.map +1 -1
- package/server/dist/config.d.ts +8 -0
- package/server/dist/config.js +19 -1
- package/server/dist/config.js.map +1 -1
- package/server/dist/native-permissions.js +1 -1
- package/server/dist/native-permissions.js.map +1 -1
- package/server/dist/orchestrator-children.js +22 -6
- package/server/dist/orchestrator-children.js.map +1 -1
- package/server/dist/orchestrator-reports.d.ts +0 -4
- package/server/dist/orchestrator-reports.js +6 -12
- package/server/dist/orchestrator-reports.js.map +1 -1
- package/server/dist/prompt-router.d.ts +95 -0
- package/server/dist/prompt-router.js +577 -0
- package/server/dist/prompt-router.js.map +1 -0
- package/server/dist/session-lifecycle.d.ts +73 -0
- package/server/dist/session-lifecycle.js +387 -0
- package/server/dist/session-lifecycle.js.map +1 -0
- package/server/dist/session-manager.d.ts +33 -60
- package/server/dist/session-manager.js +239 -785
- package/server/dist/session-manager.js.map +1 -1
- package/server/dist/session-naming.js +2 -1
- package/server/dist/session-naming.js.map +1 -1
- package/server/dist/session-persistence.js +21 -3
- package/server/dist/session-persistence.js.map +1 -1
- package/server/dist/session-routes.js +18 -0
- package/server/dist/session-routes.js.map +1 -1
- package/server/dist/tsconfig.tsbuildinfo +1 -1
- package/server/dist/types.d.ts +4 -0
- package/server/dist/upload-routes.js +3 -1
- package/server/dist/upload-routes.js.map +1 -1
- package/server/dist/webhook-handler.js +2 -1
- package/server/dist/webhook-handler.js.map +1 -1
- package/server/dist/ws-message-handler.js +19 -0
- package/server/dist/ws-message-handler.js.map +1 -1
- package/server/dist/ws-server.js +39 -9
- package/server/dist/ws-server.js.map +1 -1
- package/dist/assets/index-C0Iuc3iT.css +0 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude process lifecycle management extracted from SessionManager.
|
|
3
|
+
*
|
|
4
|
+
* Handles:
|
|
5
|
+
* - Starting/stopping Claude CLI processes (startClaude, stopClaude, stopClaudeAndWait)
|
|
6
|
+
* - Waiting for process readiness (waitForReady)
|
|
7
|
+
* - Wiring Claude process event handlers (wireClaudeEvents)
|
|
8
|
+
* - Handling process exit and auto-restart logic (handleClaudeExit)
|
|
9
|
+
*/
|
|
10
|
+
import { ClaudeProcess } from './claude-process.js';
|
|
11
|
+
import { ApprovalManager } from './approval-manager.js';
|
|
12
|
+
import type { PromptRouter } from './prompt-router.js';
|
|
13
|
+
import type { Session, WsServerMessage } from './types.js';
|
|
14
|
+
/** Dependencies injected by SessionManager so SessionLifecycle can interact with session state. */
|
|
15
|
+
export interface SessionLifecycleDeps {
|
|
16
|
+
getSession(id: string): Session | undefined;
|
|
17
|
+
hasSession(id: string): boolean;
|
|
18
|
+
broadcast(session: Session, msg: WsServerMessage): void;
|
|
19
|
+
addToHistory(session: Session, msg: WsServerMessage): void;
|
|
20
|
+
broadcastAndHistory(session: Session, msg: WsServerMessage): void;
|
|
21
|
+
persistToDisk(): void;
|
|
22
|
+
globalBroadcast: ((msg: WsServerMessage) => void) | null;
|
|
23
|
+
authToken: string;
|
|
24
|
+
serverPort: number;
|
|
25
|
+
approvalManager: ApprovalManager;
|
|
26
|
+
promptRouter: PromptRouter;
|
|
27
|
+
exitListeners: Array<(sessionId: string, code: number | null, signal: string | null, willRestart: boolean) => void>;
|
|
28
|
+
onSystemInit(cp: ClaudeProcess, session: Session, model: string): void;
|
|
29
|
+
onTextEvent(session: Session, sessionId: string, text: string): void;
|
|
30
|
+
onThinkingEvent(session: Session, summary: string): void;
|
|
31
|
+
onToolOutputEvent(session: Session, content: string, isError: boolean): void;
|
|
32
|
+
onImageEvent(session: Session, base64: string, mediaType: string): void;
|
|
33
|
+
onToolActiveEvent(session: Session, toolName: string, toolInput: string | undefined): void;
|
|
34
|
+
onToolDoneEvent(session: Session, toolName: string, summary: string | undefined): void;
|
|
35
|
+
handleClaudeResult(session: Session, sessionId: string, result: string, isError: boolean): void;
|
|
36
|
+
buildSessionContext(session: Session): string | null;
|
|
37
|
+
}
|
|
38
|
+
export declare class SessionLifecycle {
|
|
39
|
+
private deps;
|
|
40
|
+
constructor(deps: SessionLifecycleDeps);
|
|
41
|
+
/**
|
|
42
|
+
* Spawn (or re-spawn) a Claude CLI process for a session.
|
|
43
|
+
* Wires up all event handlers for streaming text, tools, prompts, and auto-restart.
|
|
44
|
+
*/
|
|
45
|
+
startClaude(sessionId: string): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Wait for a session's Claude process to emit its system_init event,
|
|
48
|
+
* indicating it is ready to accept input. Resolves immediately if the
|
|
49
|
+
* session already has a claudeSessionId (process previously initialized).
|
|
50
|
+
* Times out after `timeoutMs` (default 30s) to avoid hanging indefinitely.
|
|
51
|
+
*/
|
|
52
|
+
waitForReady(sessionId: string, timeoutMs?: number): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Attach all ClaudeProcess event listeners for a session.
|
|
55
|
+
* Called by startClaude() to keep that method focused on process setup.
|
|
56
|
+
*/
|
|
57
|
+
wireClaudeEvents(cp: ClaudeProcess, session: Session, sessionId: string): void;
|
|
58
|
+
/**
|
|
59
|
+
* Handle a Claude process 'exit' event: clean up state, notify exit listeners,
|
|
60
|
+
* and either auto-restart (within limits) or broadcast the final exit message.
|
|
61
|
+
*
|
|
62
|
+
* Uses evaluateRestart() for the restart decision, keeping this method focused
|
|
63
|
+
* on state updates, listener notification, and message broadcasting.
|
|
64
|
+
*/
|
|
65
|
+
handleClaudeExit(exitedProcess: ClaudeProcess, session: Session, sessionId: string, code: number | null, signal: string | null): void;
|
|
66
|
+
stopClaude(sessionId: string): void;
|
|
67
|
+
/**
|
|
68
|
+
* Stop the Claude process and wait for it to fully exit before resolving.
|
|
69
|
+
* This prevents race conditions when restarting with the same session ID
|
|
70
|
+
* (e.g. during mid-session worktree migration).
|
|
71
|
+
*/
|
|
72
|
+
stopClaudeAndWait(sessionId: string): Promise<void>;
|
|
73
|
+
}
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude process lifecycle management extracted from SessionManager.
|
|
3
|
+
*
|
|
4
|
+
* Handles:
|
|
5
|
+
* - Starting/stopping Claude CLI processes (startClaude, stopClaude, stopClaudeAndWait)
|
|
6
|
+
* - Waiting for process readiness (waitForReady)
|
|
7
|
+
* - Wiring Claude process event handlers (wireClaudeEvents)
|
|
8
|
+
* - Handling process exit and auto-restart logic (handleClaudeExit)
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync } from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { ClaudeProcess } from './claude-process.js';
|
|
13
|
+
import { PORT } from './config.js';
|
|
14
|
+
import { deriveSessionToken } from './crypto-utils.js';
|
|
15
|
+
import { evaluateRestart } from './session-restart-scheduler.js';
|
|
16
|
+
export class SessionLifecycle {
|
|
17
|
+
deps;
|
|
18
|
+
constructor(deps) {
|
|
19
|
+
this.deps = deps;
|
|
20
|
+
}
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Claude process lifecycle
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
/**
|
|
25
|
+
* Spawn (or re-spawn) a Claude CLI process for a session.
|
|
26
|
+
* Wires up all event handlers for streaming text, tools, prompts, and auto-restart.
|
|
27
|
+
*/
|
|
28
|
+
startClaude(sessionId) {
|
|
29
|
+
const session = this.deps.getSession(sessionId);
|
|
30
|
+
if (!session)
|
|
31
|
+
return false;
|
|
32
|
+
// Clear stopped flag and any pending restart timer on explicit start
|
|
33
|
+
session._stoppedByUser = false;
|
|
34
|
+
if (session._restartTimer) {
|
|
35
|
+
clearTimeout(session._restartTimer);
|
|
36
|
+
session._restartTimer = undefined;
|
|
37
|
+
}
|
|
38
|
+
// Validate that the working directory still exists. Worktree directories
|
|
39
|
+
// can be removed externally (cleanup, manual deletion, failed creation that
|
|
40
|
+
// left a stale placeholder). Fall back to groupDir (the original repo) so
|
|
41
|
+
// the session can still function instead of entering an infinite restart loop.
|
|
42
|
+
if (!existsSync(session.workingDir) || (session.worktreePath && !existsSync(path.join(session.workingDir, '.git')))) {
|
|
43
|
+
const fallback = session.groupDir ?? session.workingDir;
|
|
44
|
+
if (fallback !== session.workingDir && existsSync(fallback)) {
|
|
45
|
+
const deadPath = session.workingDir;
|
|
46
|
+
console.warn(`[startClaude] Working directory ${deadPath} missing or not a valid worktree — falling back to ${fallback}`);
|
|
47
|
+
session.workingDir = fallback;
|
|
48
|
+
session.worktreePath = undefined;
|
|
49
|
+
this.deps.persistToDisk();
|
|
50
|
+
this.deps.globalBroadcast?.({ type: 'sessions_updated' });
|
|
51
|
+
const fallbackMsg = {
|
|
52
|
+
type: 'system_message',
|
|
53
|
+
subtype: 'notification',
|
|
54
|
+
text: `Worktree directory ${deadPath} no longer exists. Falling back to original repository: ${fallback}`,
|
|
55
|
+
};
|
|
56
|
+
this.deps.addToHistory(session, fallbackMsg);
|
|
57
|
+
this.deps.broadcast(session, fallbackMsg);
|
|
58
|
+
}
|
|
59
|
+
else if (!existsSync(session.workingDir)) {
|
|
60
|
+
console.error(`[startClaude] Working directory ${session.workingDir} does not exist and no fallback available — cannot start`);
|
|
61
|
+
session._stoppedByUser = true; // prevent restart loop
|
|
62
|
+
const errMsg = {
|
|
63
|
+
type: 'system_message',
|
|
64
|
+
subtype: 'error',
|
|
65
|
+
text: `Working directory ${session.workingDir} no longer exists and no fallback is available. Session cannot start.`,
|
|
66
|
+
};
|
|
67
|
+
this.deps.addToHistory(session, errMsg);
|
|
68
|
+
this.deps.broadcast(session, errMsg);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Kill existing process if any — remove listeners first to prevent the
|
|
73
|
+
// old process's exit handler from clobbering the new process reference
|
|
74
|
+
// and triggering an unwanted auto-restart cycle.
|
|
75
|
+
if (session.claudeProcess) {
|
|
76
|
+
session.claudeProcess.removeAllListeners();
|
|
77
|
+
session.claudeProcess.stop();
|
|
78
|
+
}
|
|
79
|
+
// Derive a session-scoped token instead of forwarding the master auth token.
|
|
80
|
+
// This limits child process privileges to approve/deny for their own session only.
|
|
81
|
+
const sessionToken = this.deps.authToken
|
|
82
|
+
? deriveSessionToken(this.deps.authToken, sessionId)
|
|
83
|
+
: '';
|
|
84
|
+
// Both CODEKIN_TOKEN (legacy name, used by older hooks) and CODEKIN_AUTH_TOKEN
|
|
85
|
+
// (current canonical name) are set to the same derived value for backward compatibility.
|
|
86
|
+
const extraEnv = {
|
|
87
|
+
CODEKIN_SESSION_ID: sessionId,
|
|
88
|
+
CODEKIN_PORT: String(this.deps.serverPort || PORT),
|
|
89
|
+
CODEKIN_TOKEN: sessionToken,
|
|
90
|
+
CODEKIN_AUTH_TOKEN: sessionToken,
|
|
91
|
+
CODEKIN_SESSION_TYPE: session.source || 'manual',
|
|
92
|
+
...(session.permissionMode === 'dangerouslySkipPermissions' ? { CODEKIN_SKIP_PERMISSIONS: '1' } : {}),
|
|
93
|
+
};
|
|
94
|
+
// Pass CLAUDE_PROJECT_DIR so hooks and CLAUDE.md resolve correctly
|
|
95
|
+
// even when the session's working directory differs from the project root
|
|
96
|
+
// (e.g. worktrees, webhook workspaces). Note: this does NOT control
|
|
97
|
+
// session storage path — Claude CLI uses the CWD for that.
|
|
98
|
+
if (session.groupDir) {
|
|
99
|
+
extraEnv.CLAUDE_PROJECT_DIR = session.groupDir;
|
|
100
|
+
}
|
|
101
|
+
else if (process.env.CLAUDE_PROJECT_DIR) {
|
|
102
|
+
extraEnv.CLAUDE_PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR;
|
|
103
|
+
}
|
|
104
|
+
// When claudeSessionId exists, the session has run before and a JSONL file
|
|
105
|
+
// exists on disk. Use --resume (not --session-id) to continue it — --session-id
|
|
106
|
+
// creates a *new* session and fails with "already in use" if the JSONL exists.
|
|
107
|
+
const resume = !!session.claudeSessionId;
|
|
108
|
+
// Build comprehensive allowedTools from session-level overrides + registry approvals
|
|
109
|
+
const repoDir = session.groupDir ?? session.workingDir;
|
|
110
|
+
const registryPatterns = this.deps.approvalManager.getAllowedToolsForRepo(repoDir);
|
|
111
|
+
const mergedAllowedTools = [...new Set([...(session.allowedTools || []), ...registryPatterns])];
|
|
112
|
+
const cp = new ClaudeProcess(session.workingDir, {
|
|
113
|
+
sessionId: session.claudeSessionId || undefined,
|
|
114
|
+
extraEnv,
|
|
115
|
+
model: session.model,
|
|
116
|
+
permissionMode: session.permissionMode,
|
|
117
|
+
resume,
|
|
118
|
+
allowedTools: mergedAllowedTools,
|
|
119
|
+
});
|
|
120
|
+
this.wireClaudeEvents(cp, session, sessionId);
|
|
121
|
+
cp.start();
|
|
122
|
+
session.claudeProcess = cp;
|
|
123
|
+
this.deps.globalBroadcast?.({ type: 'sessions_updated' });
|
|
124
|
+
const startMsg = { type: 'claude_started', sessionId };
|
|
125
|
+
this.deps.addToHistory(session, startMsg);
|
|
126
|
+
this.deps.broadcast(session, startMsg);
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Wait for a session's Claude process to emit its system_init event,
|
|
131
|
+
* indicating it is ready to accept input. Resolves immediately if the
|
|
132
|
+
* session already has a claudeSessionId (process previously initialized).
|
|
133
|
+
* Times out after `timeoutMs` (default 30s) to avoid hanging indefinitely.
|
|
134
|
+
*/
|
|
135
|
+
waitForReady(sessionId, timeoutMs = 30_000) {
|
|
136
|
+
const session = this.deps.getSession(sessionId);
|
|
137
|
+
if (!session?.claudeProcess)
|
|
138
|
+
return Promise.resolve();
|
|
139
|
+
// If the process already completed init in a prior turn, resolve immediately
|
|
140
|
+
if (session.claudeSessionId)
|
|
141
|
+
return Promise.resolve();
|
|
142
|
+
return new Promise((resolve) => {
|
|
143
|
+
const timer = setTimeout(() => {
|
|
144
|
+
console.warn(`[waitForReady] Timed out waiting for system_init on ${sessionId} after ${timeoutMs}ms`);
|
|
145
|
+
resolve();
|
|
146
|
+
}, timeoutMs);
|
|
147
|
+
session.claudeProcess.once('system_init', () => {
|
|
148
|
+
clearTimeout(timer);
|
|
149
|
+
resolve();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Attach all ClaudeProcess event listeners for a session.
|
|
155
|
+
* Called by startClaude() to keep that method focused on process setup.
|
|
156
|
+
*/
|
|
157
|
+
wireClaudeEvents(cp, session, sessionId) {
|
|
158
|
+
cp.on('system_init', (model) => this.deps.onSystemInit(cp, session, model));
|
|
159
|
+
cp.on('text', (text) => this.deps.onTextEvent(session, sessionId, text));
|
|
160
|
+
cp.on('thinking', (summary) => this.deps.onThinkingEvent(session, summary));
|
|
161
|
+
cp.on('tool_output', (content, isError) => this.deps.onToolOutputEvent(session, content, isError));
|
|
162
|
+
cp.on('image', (base64Data, mediaType) => this.deps.onImageEvent(session, base64Data, mediaType));
|
|
163
|
+
cp.on('tool_active', (toolName, toolInput) => this.deps.onToolActiveEvent(session, toolName, toolInput));
|
|
164
|
+
cp.on('tool_done', (toolName, summary) => this.deps.onToolDoneEvent(session, toolName, summary));
|
|
165
|
+
cp.on('planning_mode', (active) => {
|
|
166
|
+
// Route EnterPlanMode through PlanManager for UI state tracking.
|
|
167
|
+
// ExitPlanMode (active=false) is ignored here — the PreToolUse hook
|
|
168
|
+
// is the enforcement gate, and it calls handleExitPlanModeApproval()
|
|
169
|
+
// which transitions PlanManager to 'reviewing'.
|
|
170
|
+
if (active) {
|
|
171
|
+
session.planManager.onEnterPlanMode();
|
|
172
|
+
}
|
|
173
|
+
// ExitPlanMode stream event intentionally ignored — hook handles it.
|
|
174
|
+
});
|
|
175
|
+
cp.on('todo_update', (tasks) => { this.deps.broadcastAndHistory(session, { type: 'todo_update', tasks }); });
|
|
176
|
+
cp.on('prompt', (...args) => this.deps.promptRouter.onPromptEvent(session, ...args));
|
|
177
|
+
cp.on('control_request', (requestId, toolName, toolInput) => this.deps.promptRouter.onControlRequestEvent(cp, session, sessionId, requestId, toolName, toolInput));
|
|
178
|
+
cp.on('result', (result, isError) => {
|
|
179
|
+
session.planManager.onTurnEnd();
|
|
180
|
+
this.deps.handleClaudeResult(session, sessionId, result, isError);
|
|
181
|
+
});
|
|
182
|
+
cp.on('error', (message) => this.deps.broadcast(session, { type: 'error', message }));
|
|
183
|
+
cp.on('exit', (code, signal) => { cp.removeAllListeners(); this.handleClaudeExit(cp, session, sessionId, code, signal); });
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Handle a Claude process 'exit' event: clean up state, notify exit listeners,
|
|
187
|
+
* and either auto-restart (within limits) or broadcast the final exit message.
|
|
188
|
+
*
|
|
189
|
+
* Uses evaluateRestart() for the restart decision, keeping this method focused
|
|
190
|
+
* on state updates, listener notification, and message broadcasting.
|
|
191
|
+
*/
|
|
192
|
+
handleClaudeExit(exitedProcess, session, sessionId, code, signal) {
|
|
193
|
+
// Guard: ignore exit events from stale processes that were replaced by a
|
|
194
|
+
// new startClaude() call. Without this, the old process's exit handler
|
|
195
|
+
// would null out session.claudeProcess (which now points to the NEW
|
|
196
|
+
// process), orphaning it and triggering an unwanted auto-restart cycle.
|
|
197
|
+
if (session.claudeProcess && session.claudeProcess !== exitedProcess) {
|
|
198
|
+
console.log(`[restart] Ignoring exit from stale process for session ${sessionId}`);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
session.claudeProcess = null;
|
|
202
|
+
session.isProcessing = false;
|
|
203
|
+
session.planManager.reset();
|
|
204
|
+
this.deps.globalBroadcast?.({ type: 'sessions_updated' });
|
|
205
|
+
// "Session ID is already in use" means another process holds the lock.
|
|
206
|
+
// Retrying with the same session ID will fail every time, so treat this
|
|
207
|
+
// as a non-restartable exit (same as stopped-by-user).
|
|
208
|
+
const sessionConflict = exitedProcess.hasSessionConflict();
|
|
209
|
+
// If the process exited without ever producing stdout output, --resume
|
|
210
|
+
// hung on a broken/stale session. Clear claudeSessionId so the next
|
|
211
|
+
// restart attempt uses a fresh session instead of retrying the same
|
|
212
|
+
// broken resume — which would just hang again.
|
|
213
|
+
// Exception: if spawn() itself failed (ENOENT/EACCES), the process never
|
|
214
|
+
// started — the session data on disk is fine, so preserve the ID for retry.
|
|
215
|
+
if (!exitedProcess.hadOutput() && session.claudeSessionId && !exitedProcess.hasSpawnFailed()) {
|
|
216
|
+
console.warn(`[restart] Session ${sessionId} produced no output before exit — clearing claudeSessionId to force fresh session`);
|
|
217
|
+
session.claudeSessionId = null;
|
|
218
|
+
}
|
|
219
|
+
if (exitedProcess.hasSpawnFailed()) {
|
|
220
|
+
console.warn(`[restart] Session ${sessionId} spawn failed (binary not found) — preserving claudeSessionId for retry`);
|
|
221
|
+
}
|
|
222
|
+
// Before evaluating restart, check if the working directory still exists.
|
|
223
|
+
// If a worktree was deleted mid-session, fall back to the original repo
|
|
224
|
+
// instead of entering a guaranteed restart death loop where every attempt
|
|
225
|
+
// fails with the same missing CWD.
|
|
226
|
+
if (!existsSync(session.workingDir)) {
|
|
227
|
+
const fallback = session.groupDir;
|
|
228
|
+
if (fallback && existsSync(fallback)) {
|
|
229
|
+
const deadPath = session.workingDir;
|
|
230
|
+
console.warn(`[restart] Working directory ${deadPath} no longer exists — falling back to ${fallback}`);
|
|
231
|
+
session.workingDir = fallback;
|
|
232
|
+
session.worktreePath = undefined;
|
|
233
|
+
this.deps.persistToDisk();
|
|
234
|
+
this.deps.globalBroadcast?.({ type: 'sessions_updated' });
|
|
235
|
+
const fallbackMsg = {
|
|
236
|
+
type: 'system_message',
|
|
237
|
+
subtype: 'notification',
|
|
238
|
+
text: `Worktree directory ${deadPath} was removed. Restarting in original repository: ${fallback}`,
|
|
239
|
+
};
|
|
240
|
+
this.deps.addToHistory(session, fallbackMsg);
|
|
241
|
+
this.deps.broadcast(session, fallbackMsg);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
// No fallback available — don't waste restart attempts
|
|
245
|
+
console.error(`[restart] Working directory ${session.workingDir} does not exist and no fallback — stopping session`);
|
|
246
|
+
session._stoppedByUser = true;
|
|
247
|
+
for (const listener of this.deps.exitListeners) {
|
|
248
|
+
try {
|
|
249
|
+
listener(sessionId, code, signal, false);
|
|
250
|
+
}
|
|
251
|
+
catch { /* listener error */ }
|
|
252
|
+
}
|
|
253
|
+
const msg = {
|
|
254
|
+
type: 'system_message',
|
|
255
|
+
subtype: 'error',
|
|
256
|
+
text: `Working directory ${session.workingDir} no longer exists and no fallback is available. Please delete this session and create a new one.`,
|
|
257
|
+
};
|
|
258
|
+
this.deps.addToHistory(session, msg);
|
|
259
|
+
this.deps.broadcast(session, msg);
|
|
260
|
+
this.deps.broadcast(session, { type: 'exit', code: code ?? -1, signal });
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const action = evaluateRestart({
|
|
265
|
+
restartCount: session.restartCount,
|
|
266
|
+
lastRestartAt: session.lastRestartAt,
|
|
267
|
+
stoppedByUser: session._stoppedByUser || sessionConflict,
|
|
268
|
+
});
|
|
269
|
+
if (action.kind === 'stopped_by_user') {
|
|
270
|
+
for (const listener of this.deps.exitListeners) {
|
|
271
|
+
try {
|
|
272
|
+
listener(sessionId, code, signal, false);
|
|
273
|
+
}
|
|
274
|
+
catch { /* listener error */ }
|
|
275
|
+
}
|
|
276
|
+
const text = sessionConflict
|
|
277
|
+
? 'Claude process exited: session ID is already in use by another process. Please restart manually.'
|
|
278
|
+
: `Claude process exited: code=${code}, signal=${signal}`;
|
|
279
|
+
const msg = { type: 'system_message', subtype: 'exit', text };
|
|
280
|
+
this.deps.addToHistory(session, msg);
|
|
281
|
+
this.deps.broadcast(session, msg);
|
|
282
|
+
this.deps.broadcast(session, { type: 'exit', code: code ?? -1, signal });
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (action.kind === 'restart') {
|
|
286
|
+
session.restartCount = action.updatedCount;
|
|
287
|
+
session.lastRestartAt = action.updatedLastRestartAt;
|
|
288
|
+
for (const listener of this.deps.exitListeners) {
|
|
289
|
+
try {
|
|
290
|
+
listener(sessionId, code, signal, true);
|
|
291
|
+
}
|
|
292
|
+
catch { /* listener error */ }
|
|
293
|
+
}
|
|
294
|
+
const msg = {
|
|
295
|
+
type: 'system_message',
|
|
296
|
+
subtype: 'restart',
|
|
297
|
+
text: `Claude process exited unexpectedly (code=${code}, signal=${signal}). Restarting (attempt ${action.attempt}/${action.maxAttempts})...`,
|
|
298
|
+
};
|
|
299
|
+
this.deps.addToHistory(session, msg);
|
|
300
|
+
this.deps.broadcast(session, msg);
|
|
301
|
+
// Clear any previously scheduled restart to prevent duplicate spawns
|
|
302
|
+
if (session._restartTimer)
|
|
303
|
+
clearTimeout(session._restartTimer);
|
|
304
|
+
session._restartTimer = setTimeout(() => {
|
|
305
|
+
session._restartTimer = undefined;
|
|
306
|
+
// Verify session still exists and hasn't been stopped
|
|
307
|
+
if (!this.deps.hasSession(sessionId) || session._stoppedByUser)
|
|
308
|
+
return;
|
|
309
|
+
// startClaude uses --resume when claudeSessionId exists, so the CLI
|
|
310
|
+
// picks up the full conversation history from the JSONL automatically.
|
|
311
|
+
this.startClaude(sessionId);
|
|
312
|
+
// Fallback: if claudeSessionId was already null (fresh session that
|
|
313
|
+
// crashed before system_init), inject a context summary so the new
|
|
314
|
+
// session has some awareness of prior conversation.
|
|
315
|
+
if (!session.claudeSessionId && session.claudeProcess && session.outputHistory.length > 0) {
|
|
316
|
+
session.claudeProcess.once('system_init', () => {
|
|
317
|
+
const context = this.deps.buildSessionContext(session);
|
|
318
|
+
if (context) {
|
|
319
|
+
session.claudeProcess?.sendMessage(context + '\n\n[Session resumed after process restart. Continue where you left off. If you were in the middle of a task, resume it.]');
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}, action.delayMs);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
// action.kind === 'exhausted'
|
|
327
|
+
for (const listener of this.deps.exitListeners) {
|
|
328
|
+
try {
|
|
329
|
+
listener(sessionId, code, signal, false);
|
|
330
|
+
}
|
|
331
|
+
catch { /* listener error */ }
|
|
332
|
+
}
|
|
333
|
+
const msg = {
|
|
334
|
+
type: 'system_message',
|
|
335
|
+
subtype: 'error',
|
|
336
|
+
text: `Claude process exited unexpectedly (code=${code}, signal=${signal}). Auto-restart disabled after ${action.maxAttempts} attempts. Please restart manually.`,
|
|
337
|
+
};
|
|
338
|
+
this.deps.addToHistory(session, msg);
|
|
339
|
+
this.deps.broadcast(session, msg);
|
|
340
|
+
this.deps.broadcast(session, { type: 'exit', code: code ?? -1, signal });
|
|
341
|
+
}
|
|
342
|
+
stopClaude(sessionId) {
|
|
343
|
+
const session = this.deps.getSession(sessionId);
|
|
344
|
+
if (session?.claudeProcess) {
|
|
345
|
+
session._stoppedByUser = true;
|
|
346
|
+
if (session._apiRetryTimer)
|
|
347
|
+
clearTimeout(session._apiRetryTimer);
|
|
348
|
+
if (session._restartTimer) {
|
|
349
|
+
clearTimeout(session._restartTimer);
|
|
350
|
+
session._restartTimer = undefined;
|
|
351
|
+
}
|
|
352
|
+
session.claudeProcess.removeAllListeners();
|
|
353
|
+
session.claudeProcess.stop();
|
|
354
|
+
session.claudeProcess = null;
|
|
355
|
+
this.deps.broadcast(session, { type: 'claude_stopped' });
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Stop the Claude process and wait for it to fully exit before resolving.
|
|
360
|
+
* This prevents race conditions when restarting with the same session ID
|
|
361
|
+
* (e.g. during mid-session worktree migration).
|
|
362
|
+
*/
|
|
363
|
+
async stopClaudeAndWait(sessionId) {
|
|
364
|
+
const session = this.deps.getSession(sessionId);
|
|
365
|
+
if (!session?.claudeProcess)
|
|
366
|
+
return;
|
|
367
|
+
const cp = session.claudeProcess;
|
|
368
|
+
session._stoppedByUser = true;
|
|
369
|
+
if (session._apiRetryTimer)
|
|
370
|
+
clearTimeout(session._apiRetryTimer);
|
|
371
|
+
if (session._restartTimer) {
|
|
372
|
+
clearTimeout(session._restartTimer);
|
|
373
|
+
session._restartTimer = undefined;
|
|
374
|
+
}
|
|
375
|
+
cp.removeAllListeners();
|
|
376
|
+
cp.stop();
|
|
377
|
+
this.deps.broadcast(session, { type: 'claude_stopped' });
|
|
378
|
+
// Wait for the underlying OS process to fully exit BEFORE nulling the
|
|
379
|
+
// reference. Previously this was set to null before the await, which
|
|
380
|
+
// allowed another caller (e.g. move_to_worktree) to call startClaude()
|
|
381
|
+
// while the old process was still alive — causing concurrent git access
|
|
382
|
+
// and index corruption.
|
|
383
|
+
await cp.waitForExit();
|
|
384
|
+
session.claudeProcess = null;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
//# sourceMappingURL=session-lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-lifecycle.js","sourceRoot":"","sources":["../session-lifecycle.ts"],"names":[],"mappings":"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"}
|