codekin 0.5.4 → 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.
Files changed (35) hide show
  1. package/README.md +1 -0
  2. package/dist/assets/{index-COGLICp9.js → index-BFkKlY3O.js} +46 -42
  3. package/dist/index.html +1 -1
  4. package/package.json +1 -1
  5. package/server/dist/approval-manager.js +1 -2
  6. package/server/dist/approval-manager.js.map +1 -1
  7. package/server/dist/claude-process.d.ts +9 -0
  8. package/server/dist/claude-process.js +35 -3
  9. package/server/dist/claude-process.js.map +1 -1
  10. package/server/dist/config.js +2 -1
  11. package/server/dist/config.js.map +1 -1
  12. package/server/dist/native-permissions.js +1 -1
  13. package/server/dist/native-permissions.js.map +1 -1
  14. package/server/dist/orchestrator-children.js +22 -6
  15. package/server/dist/orchestrator-children.js.map +1 -1
  16. package/server/dist/orchestrator-reports.d.ts +0 -4
  17. package/server/dist/orchestrator-reports.js +6 -12
  18. package/server/dist/orchestrator-reports.js.map +1 -1
  19. package/server/dist/prompt-router.d.ts +95 -0
  20. package/server/dist/prompt-router.js +577 -0
  21. package/server/dist/prompt-router.js.map +1 -0
  22. package/server/dist/session-lifecycle.d.ts +73 -0
  23. package/server/dist/session-lifecycle.js +387 -0
  24. package/server/dist/session-lifecycle.js.map +1 -0
  25. package/server/dist/session-manager.d.ts +33 -60
  26. package/server/dist/session-manager.js +234 -801
  27. package/server/dist/session-manager.js.map +1 -1
  28. package/server/dist/session-persistence.js +21 -3
  29. package/server/dist/session-persistence.js.map +1 -1
  30. package/server/dist/tsconfig.tsbuildinfo +1 -1
  31. package/server/dist/types.d.ts +4 -0
  32. package/server/dist/ws-message-handler.js +2 -1
  33. package/server/dist/ws-message-handler.js.map +1 -1
  34. package/server/dist/ws-server.js +6 -0
  35. package/server/dist/ws-server.js.map +1 -1
@@ -15,12 +15,14 @@
15
15
  * - SessionNaming: AI-powered session name generation with retry logic
16
16
  * - SessionPersistence: disk I/O for session state
17
17
  * - DiffManager: stateless git-diff operations
18
- * - evaluateRestart: pure restart-decision logic
18
+ * - SessionLifecycle: Claude process start/stop/restart and event wiring
19
+ * - evaluateRestart: pure restart-decision logic (used by SessionLifecycle)
19
20
  */
20
21
  import type { WebSocket } from 'ws';
21
22
  import { SessionArchive } from './session-archive.js';
22
23
  import type { DiffFileStatus, DiffScope, Session, SessionInfo, WsServerMessage } from './types.js';
23
24
  import { ApprovalManager } from './approval-manager.js';
25
+ import { SessionLifecycle } from './session-lifecycle.js';
24
26
  export interface CreateSessionOptions {
25
27
  source?: 'manual' | 'webhook' | 'workflow' | 'stepflow' | 'orchestrator' | 'agent';
26
28
  id?: string;
@@ -60,6 +62,10 @@ export declare class SessionManager {
60
62
  private sessionPersistence;
61
63
  /** Delegated diff operations (git diff, discard changes). */
62
64
  private diffManager;
65
+ /** Delegated prompt routing and tool approval logic. */
66
+ private promptRouter;
67
+ /** Delegated Claude process lifecycle (start, stop, restart, event wiring). */
68
+ private sessionLifecycle;
63
69
  /** Interval handle for the idle session reaper. */
64
70
  private _idleReaperInterval;
65
71
  constructor();
@@ -86,8 +92,16 @@ export declare class SessionManager {
86
92
  * Create a git worktree for a session. Creates a new branch and worktree
87
93
  * as a sibling directory of the project root.
88
94
  * Returns the worktree path on success, or null on failure.
95
+ *
96
+ * @param targetBranch — use this as the worktree branch name instead of
97
+ * the default `wt/{shortId}`. The orchestrator uses this to create the
98
+ * worktree directly on the desired feature branch so Claude doesn't
99
+ * need to create a second branch.
100
+ * @param baseBranch — create the worktree branch from this ref (e.g.
101
+ * 'main'). Defaults to auto-detecting the default branch. Prevents
102
+ * worktrees from accidentally branching off a random HEAD.
89
103
  */
90
- createWorktree(sessionId: string, workingDir: string): Promise<string | null>;
104
+ createWorktree(sessionId: string, workingDir: string, targetBranch?: string, baseBranch?: string): Promise<string | null>;
91
105
  /**
92
106
  * Resolve the Claude CLI project storage directory for a given working dir.
93
107
  * Claude encodes the absolute path by replacing `/` with `-`.
@@ -106,8 +120,15 @@ export declare class SessionManager {
106
120
  /**
107
121
  * Clean up a git worktree and its branch. Runs asynchronously and logs errors
108
122
  * but never throws — session deletion must not be blocked by cleanup failures.
123
+ * Retries once on failure after a short delay.
109
124
  */
110
125
  private cleanupWorktree;
126
+ /**
127
+ * Detect the default branch of a repository (main, master, etc.).
128
+ * Tries `git symbolic-ref refs/remotes/origin/HEAD` first, then checks
129
+ * for common branch names. Returns null if detection fails.
130
+ */
131
+ private detectDefaultBranch;
111
132
  /** Get the configured worktree branch prefix (defaults to 'wt/'). */
112
133
  getWorktreeBranchPrefix(): string;
113
134
  /** Set the worktree branch prefix. */
@@ -152,21 +173,14 @@ export declare class SessionManager {
152
173
  private archiveSessionIfWorthSaving;
153
174
  /**
154
175
  * Spawn (or re-spawn) a Claude CLI process for a session.
155
- * Wires up all event handlers for streaming text, tools, prompts, and auto-restart.
176
+ * Delegates to SessionLifecycle.
156
177
  */
157
178
  startClaude(sessionId: string): boolean;
158
179
  /**
159
- * Wait for a session's Claude process to emit its system_init event,
160
- * indicating it is ready to accept input. Resolves immediately if the
161
- * session already has a claudeSessionId (process previously initialized).
162
- * Times out after `timeoutMs` (default 30s) to avoid hanging indefinitely.
180
+ * Wait for a session's Claude process to emit its system_init event.
181
+ * Delegates to SessionLifecycle.
163
182
  */
164
183
  waitForReady(sessionId: string, timeoutMs?: number): Promise<void>;
165
- /**
166
- * Attach all ClaudeProcess event listeners for a session.
167
- * Extracted from startClaude() to keep that method focused on process setup.
168
- */
169
- private wireClaudeEvents;
170
184
  /** Broadcast a message and add it to the session's output history. */
171
185
  private broadcastAndHistory;
172
186
  /**
@@ -182,8 +196,6 @@ export declare class SessionManager {
182
196
  private onImageEvent;
183
197
  private onToolActiveEvent;
184
198
  private onToolDoneEvent;
185
- private onPromptEvent;
186
- private onControlRequestEvent;
187
199
  /**
188
200
  * Handle a Claude process 'result' event: update session state, apply API
189
201
  * retry logic for transient errors, broadcast result to clients, and trigger
@@ -206,76 +218,37 @@ export declare class SessionManager {
206
218
  * and trigger session naming if needed.
207
219
  */
208
220
  private finalizeResult;
209
- /**
210
- * Handle a Claude process 'exit' event: clean up state, notify exit listeners,
211
- * and either auto-restart (within limits) or broadcast the final exit message.
212
- *
213
- * Uses evaluateRestart() for the restart decision, keeping this method focused
214
- * on state updates, listener notification, and message broadcasting.
215
- */
216
- private handleClaudeExit;
221
+ /** Handle Claude process exit. Delegates to SessionLifecycle. @internal — used by tests. */
222
+ handleClaudeExit(...args: Parameters<SessionLifecycle['handleClaudeExit']>): void;
217
223
  /**
218
224
  * Send user input to a session's Claude process.
219
225
  * Auto-starts Claude if not running, with session context for continuity.
220
226
  */
221
227
  sendInput(sessionId: string, data: string): void;
222
228
  /**
223
- * Route a user's prompt response to the correct handler: pending tool approval
224
- * (from PermissionRequest hook), pending control request (from control_request
225
- * fallback path), or plain message fallback.
229
+ * Route a user's prompt response to the correct handler.
230
+ * Delegates to PromptRouter.
226
231
  */
227
232
  sendPromptResponse(sessionId: string, value: string | string[], requestId?: string): void;
228
- /** Decode the allow/deny/always/pattern intent from a prompt response value. */
229
- private decodeApprovalValue;
230
- /** Resolve a pending PreToolUse hook approval and update auto-approval registries. */
231
- private resolveToolApproval;
232
- /**
233
- * Send an AskUserQuestion control response, mapping the user's answer(s) into
234
- * the structured answers map the tool expects.
235
- */
236
- private handleAskUserQuestion;
237
- /** Send a permission control response (allow/always_allow/approve_pattern/deny). */
238
- private sendControlResponseForRequest;
239
233
  /**
240
234
  * Called by the PermissionRequest hook HTTP endpoint. Sends a prompt to clients
241
235
  * and returns a Promise that resolves when the user approves/denies.
236
+ * Delegates to PromptRouter.
242
237
  */
243
238
  requestToolApproval(sessionId: string, toolName: string, toolInput: Record<string, unknown>): Promise<{
244
239
  allow: boolean;
245
240
  always: boolean;
246
241
  answer?: string;
247
242
  }>;
248
- /**
249
- * Handle ExitPlanMode approval through PlanManager.
250
- * Shows a plan-specific approval prompt (Approve/Reject) and blocks the hook
251
- * until the user responds. On approve, returns allow:true (the hook will use
252
- * the deny-with-approval-message workaround). On deny, returns allow:false.
253
- */
254
- private handleExitPlanModeApproval;
255
- /**
256
- * Check if a tool invocation can be auto-approved without prompting the user.
257
- * Returns 'registry' if matched by auto-approval rules, 'session' if matched
258
- * by the session's allowedTools list, 'headless' if the session has no clients
259
- * and is a non-interactive source, or 'prompt' if the user needs to decide.
260
- */
261
- private resolveAutoApproval;
262
- /**
263
- * Check if a tool invocation matches any of the session's allowedTools patterns.
264
- * Patterns follow Claude CLI format: 'ToolName' or 'ToolName(prefix:*)'.
265
- * Examples: 'WebFetch', 'Bash(curl:*)', 'Bash(git:*)'.
266
- */
267
- private matchesAllowedTools;
268
- /** Build a human-readable prompt string for a tool permission dialog. */
269
- private summarizeToolPermission;
270
243
  /** Update the model for a session and restart Claude with the new model. */
271
244
  setModel(sessionId: string, model: string): boolean;
272
245
  /** Update the permission mode for a session and restart Claude with the new mode. */
273
246
  setPermissionMode(sessionId: string, permissionMode: import('./types.js').PermissionMode): boolean;
247
+ /** Stop the Claude process for a session. Delegates to SessionLifecycle. */
274
248
  stopClaude(sessionId: string): void;
275
249
  /**
276
250
  * Stop the Claude process and wait for it to fully exit before resolving.
277
- * This prevents race conditions when restarting with the same session ID
278
- * (e.g. during mid-session worktree migration).
251
+ * Delegates to SessionLifecycle.
279
252
  */
280
253
  stopClaudeAndWait(sessionId: string): Promise<void>;
281
254
  /** Check if an error result text matches a transient API error worth retrying. */