orquesta-agent 0.2.157 → 0.2.158

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.
@@ -134,6 +134,9 @@ export interface StartSessionOptions {
134
134
  channel: BroadcastChannel;
135
135
  cols?: number;
136
136
  rows?: number;
137
+ /** Workstream (sub-agent) this session belongs to. undefined = default slot. */
138
+ subAgentId?: string;
139
+ subAgentName?: string;
137
140
  }
138
141
  /**
139
142
  * Start an interactive Claude CLI session.
@@ -157,15 +160,36 @@ export declare function endSession(sessionId: string): boolean;
157
160
  */
158
161
  export declare function resizeSession(sessionId: string, cols: number, rows: number): boolean;
159
162
  /**
160
- * Check if there's an active interactive session.
163
+ * Check if there's ANY active interactive session.
161
164
  */
162
165
  export declare function hasActiveSession(): boolean;
163
166
  /**
164
- * Get the current session ID if there's an active session.
167
+ * Check if there's an active interactive session bound to a given workstream
168
+ * slot (undefined/'' = the default slot). Prompt-queue gating uses this so a
169
+ * session in workstream A doesn't block prompts targeting workstream B.
170
+ */
171
+ export declare function hasActiveSessionFor(subAgentId?: string | null): boolean;
172
+ /**
173
+ * Get the first active session ID, if any (back-compat single-session callers).
165
174
  */
166
175
  export declare function getActiveSessionId(): string | null;
176
+ export interface SessionInfo {
177
+ sessionId: string;
178
+ subAgentId: string | null;
179
+ subAgentName: string | null;
180
+ workingDirectory: string;
181
+ uptime: number;
182
+ cliType?: 'claude' | 'orquesta';
183
+ }
184
+ /**
185
+ * List every active interactive session — the dashboard renders these so each
186
+ * user can see which workstreams are busy and attach to one (or start a new one
187
+ * elsewhere).
188
+ */
189
+ export declare function getSessionList(): SessionInfo[];
167
190
  /**
168
- * Get full session status for dashboard status queries.
191
+ * Get full session status for dashboard status queries. Returns the first
192
+ * active session for back-compat, plus the full list under `sessions`.
169
193
  */
170
194
  export declare function getSessionStatus(): {
171
195
  active: boolean;
@@ -173,10 +197,11 @@ export declare function getSessionStatus(): {
173
197
  workingDirectory: string | null;
174
198
  uptime: number | null;
175
199
  cliType?: 'claude' | 'orquesta';
200
+ sessions: SessionInfo[];
176
201
  };
177
202
  /**
178
- * Force terminate any active session (for cleanup).
203
+ * Force terminate a session (for cleanup). With no sessionId, terminates ALL.
179
204
  * Sends session:ended back to the dashboard so it can update UI state.
180
205
  */
181
- export declare function terminateSession(): void;
206
+ export declare function terminateSession(sessionId?: string): void;
182
207
  //# sourceMappingURL=executor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
package/dist/executor.js CHANGED
@@ -2505,8 +2505,11 @@ export async function generatePlanItems(options) {
2505
2505
  async function sendViaFreshChannel(_channelName, channel, generationId, planId, items, error) {
2506
2506
  await sendPlanItemsGenerated(channel, generationId, planId, items, error);
2507
2507
  }
2508
- // Track active interactive session (only one at a time)
2509
- let activeSession = null;
2508
+ // Track active interactive sessions, keyed by sessionId. The agent runs one PTY
2509
+ // per workstream concurrently, so several sessions can be live at once.
2510
+ const sessions = new Map();
2511
+ // Helper: normalize a slot key (undefined/'' both mean the default slot).
2512
+ function slotKey(subAgentId) { return subAgentId || ''; }
2510
2513
  // Callback fired when a session ends — allows index.ts to drain the prompt queue
2511
2514
  let onSessionEndedCallback = null;
2512
2515
  // Per-file byte offset into /tmp/rr-*.log (rogerthat-handoff listener inboxes).
@@ -2614,36 +2617,38 @@ function readNewRogerthatMessages() {
2614
2617
  const PHONE_IDLE_THRESHOLD_MS = 1500;
2615
2618
  /** Internal helper — try to flush buffered phone messages into the PTY. */
2616
2619
  function tryFlushPhoneToPty(sessionId) {
2617
- if (!activeSession || activeSession.sessionId !== sessionId || !activeSession.isActive)
2620
+ const s = sessions.get(sessionId);
2621
+ if (!s || !s.isActive)
2618
2622
  return;
2619
- if (activeSession.pendingPhoneTimer) {
2620
- clearTimeout(activeSession.pendingPhoneTimer);
2621
- activeSession.pendingPhoneTimer = null;
2623
+ if (s.pendingPhoneTimer) {
2624
+ clearTimeout(s.pendingPhoneTimer);
2625
+ s.pendingPhoneTimer = null;
2622
2626
  }
2623
- const block = activeSession.pendingPhoneBlock || '';
2627
+ const block = s.pendingPhoneBlock || '';
2624
2628
  if (!block)
2625
2629
  return;
2626
- const lastOut = activeSession.lastPtyOutputAt ?? 0;
2630
+ const lastOut = s.lastPtyOutputAt ?? 0;
2627
2631
  const idleFor = Date.now() - lastOut;
2628
2632
  if (idleFor >= PHONE_IDLE_THRESHOLD_MS) {
2629
- activeSession.pendingPhoneBlock = '';
2633
+ s.pendingPhoneBlock = '';
2630
2634
  logger.info(`[Session] Flushing ${block.length} chars of buffered phone messages to PTY (idle ${idleFor}ms)`);
2631
2635
  writeSessionText(sessionId, block);
2632
2636
  }
2633
2637
  else {
2634
2638
  const waitMs = PHONE_IDLE_THRESHOLD_MS - idleFor + 100;
2635
- activeSession.pendingPhoneTimer = setTimeout(() => tryFlushPhoneToPty(sessionId), waitMs);
2639
+ s.pendingPhoneTimer = setTimeout(() => tryFlushPhoneToPty(sessionId), waitMs);
2636
2640
  }
2637
2641
  }
2638
2642
  /** Internal helper — called when fs.watch notifies us about /tmp/rr-*.log changes. */
2639
2643
  function onRogerthatLogChange(sessionId) {
2640
- if (!activeSession || activeSession.sessionId !== sessionId || !activeSession.isActive)
2644
+ const s = sessions.get(sessionId);
2645
+ if (!s || !s.isActive)
2641
2646
  return;
2642
2647
  const block = readNewRogerthatMessages();
2643
2648
  if (!block)
2644
2649
  return;
2645
- activeSession.pendingPhoneBlock = (activeSession.pendingPhoneBlock || '') + block;
2646
- if (!activeSession.pendingPhoneTimer)
2650
+ s.pendingPhoneBlock = (s.pendingPhoneBlock || '') + block;
2651
+ if (!s.pendingPhoneTimer)
2647
2652
  tryFlushPhoneToPty(sessionId);
2648
2653
  }
2649
2654
  /**
@@ -2684,11 +2689,12 @@ export function setOnSessionEnded(cb) {
2684
2689
  * The session runs with --dangerously-skip-permissions for uninterrupted interaction.
2685
2690
  */
2686
2691
  export async function startSession(options) {
2687
- const { sessionId, workingDirectory, channel, cols = 120, rows = 40 } = options;
2688
- // Auto-terminate any existing session before starting a new one
2689
- if (activeSession) {
2690
- logger.warn(`Auto-terminating existing session ${activeSession.sessionId} to start new one`);
2691
- terminateSession();
2692
+ const { sessionId, workingDirectory, channel, cols = 120, rows = 40, subAgentId, subAgentName } = options;
2693
+ // If this exact sessionId is already live, terminate it before re-creating.
2694
+ // Sessions on OTHER workstreams are left untouched — they run concurrently.
2695
+ if (sessions.has(sessionId)) {
2696
+ logger.warn(`Restarting existing session ${sessionId}`);
2697
+ terminateSession(sessionId);
2692
2698
  }
2693
2699
  const cwd = workingDirectory || process.cwd();
2694
2700
  const startTime = Date.now();
@@ -2797,12 +2803,14 @@ export async function startSession(options) {
2797
2803
  }
2798
2804
  logger.success(`Session started with PID: ${ptyProcess.pid}`);
2799
2805
  // Create session object
2800
- activeSession = {
2806
+ const session = {
2801
2807
  sessionId,
2802
2808
  ptyProcess,
2803
2809
  channel,
2804
2810
  workingDirectory: cwd,
2805
2811
  startTime,
2812
+ subAgentId,
2813
+ subAgentName,
2806
2814
  cliType: cliCommand === 'orquesta' ? 'orquesta' : 'claude',
2807
2815
  isActive: true,
2808
2816
  pendingPhoneBlock: '',
@@ -2810,10 +2818,11 @@ export async function startSession(options) {
2810
2818
  lastPtyOutputAt: Date.now(),
2811
2819
  rogerthatWatcher: null,
2812
2820
  };
2821
+ sessions.set(sessionId, session);
2813
2822
  // Arm the live phone-message monitor — pushes new /tmp/rr-*.log content into
2814
2823
  // the PTY as if the operator had typed it, so they can hold a full
2815
2824
  // conversation from the phone without touching the dashboard.
2816
- activeSession.rogerthatWatcher = startRogerthatMonitor(sessionId);
2825
+ session.rogerthatWatcher = startRogerthatMonitor(sessionId);
2817
2826
  // Send session:started only after Claude produces its first output.
2818
2827
  // This ensures the UI doesn't activate the input field before Claude is
2819
2828
  // actually ready — early input would otherwise be silently ignored.
@@ -2830,7 +2839,7 @@ export async function startSession(options) {
2830
2839
  const FLUSH_INTERVAL_MS = 80;
2831
2840
  const flushOutput = () => {
2832
2841
  flushTimer = null;
2833
- if (!outputBuffer || !activeSession?.isActive)
2842
+ if (!outputBuffer || !sessions.get(sessionId)?.isActive)
2834
2843
  return;
2835
2844
  const data = outputBuffer;
2836
2845
  outputBuffer = '';
@@ -2846,13 +2855,14 @@ export async function startSession(options) {
2846
2855
  logger.output('stdout', data.slice(0, 100).replace(/[\x00-\x1f]/g, '·') + (data.length > 100 ? '...' : ''));
2847
2856
  // Mark the PTY as recently active — the rogerthat live monitor uses this
2848
2857
  // to decide whether Claude is idle enough to receive injected phone input.
2849
- if (activeSession?.sessionId === sessionId) {
2850
- activeSession.lastPtyOutputAt = Date.now();
2858
+ const liveSession = sessions.get(sessionId);
2859
+ if (liveSession) {
2860
+ liveSession.lastPtyOutputAt = Date.now();
2851
2861
  }
2852
2862
  // Confirm session is ready on first output from Claude
2853
2863
  if (!sessionStartedSent) {
2854
2864
  sessionStartedSent = true;
2855
- sendSessionStarted(channel, sessionId, cwd, cliCommand === 'orquesta' ? 'orquesta' : 'claude');
2865
+ sendSessionStarted(channel, sessionId, cwd, cliCommand === 'orquesta' ? 'orquesta' : 'claude', { id: subAgentId, name: subAgentName });
2856
2866
  }
2857
2867
  // Append to buffer; schedule flush if not already pending
2858
2868
  outputBuffer += data;
@@ -2865,7 +2875,7 @@ export async function startSession(options) {
2865
2875
  if (!sessionStartedSent) {
2866
2876
  sessionStartedSent = true;
2867
2877
  logger.warn('[Session] No initial output after 5s, sending session:started anyway');
2868
- sendSessionStarted(channel, sessionId, cwd, cliCommand === 'orquesta' ? 'orquesta' : 'claude');
2878
+ sendSessionStarted(channel, sessionId, cwd, cliCommand === 'orquesta' ? 'orquesta' : 'claude', { id: subAgentId, name: subAgentName });
2869
2879
  }
2870
2880
  }, 5000);
2871
2881
  // Handle PTY exit
@@ -2890,18 +2900,19 @@ export async function startSession(options) {
2890
2900
  logger.error(`[Session] Early exit detected: ${hint}`);
2891
2901
  sendSessionError(channel, sessionId, hint);
2892
2902
  }
2893
- if (activeSession?.sessionId === sessionId) {
2903
+ const exitingSession = sessions.get(sessionId);
2904
+ if (exitingSession) {
2894
2905
  // Tear down the rogerthat live monitor before clearing the session
2895
2906
  try {
2896
- activeSession.rogerthatWatcher?.close();
2907
+ exitingSession.rogerthatWatcher?.close();
2897
2908
  }
2898
2909
  catch { /* already closed */ }
2899
- if (activeSession.pendingPhoneTimer) {
2900
- clearTimeout(activeSession.pendingPhoneTimer);
2901
- activeSession.pendingPhoneTimer = null;
2910
+ if (exitingSession.pendingPhoneTimer) {
2911
+ clearTimeout(exitingSession.pendingPhoneTimer);
2912
+ exitingSession.pendingPhoneTimer = null;
2902
2913
  }
2903
2914
  sendSessionEnded(channel, sessionId, exitCode ?? 0, duration);
2904
- activeSession = null;
2915
+ sessions.delete(sessionId);
2905
2916
  // Note: do NOT clear rogerthatLogOffsets here — the map is shared with
2906
2917
  // one-shot execute() prompts and persists for the agent process lifetime.
2907
2918
  // Notify index.ts to drain any queued prompts
@@ -2919,14 +2930,15 @@ export async function startSession(options) {
2919
2930
  * their absolute paths are appended to the message text so Claude can Read them.
2920
2931
  */
2921
2932
  export function sendSessionInput(sessionId, input, raw = false, attachments) {
2922
- if (!activeSession || activeSession.sessionId !== sessionId) {
2933
+ const s = sessions.get(sessionId);
2934
+ if (!s) {
2923
2935
  logger.warn(`No active session with ID: ${sessionId}`);
2924
2936
  return false;
2925
2937
  }
2926
2938
  try {
2927
2939
  if (raw) {
2928
2940
  // Send raw bytes directly (e.g. ESC=\x1b, Ctrl+C=\x03)
2929
- activeSession.ptyProcess.write(input);
2941
+ s.ptyProcess.write(input);
2930
2942
  logger.info(`Session raw input sent: 0x${Buffer.from(input).toString('hex')}`);
2931
2943
  return true;
2932
2944
  }
@@ -2982,10 +2994,11 @@ export function sendSessionInput(sessionId, input, raw = false, attachments) {
2982
2994
  * before Enter arrives.
2983
2995
  */
2984
2996
  function writeSessionText(sessionId, input) {
2985
- if (!activeSession || activeSession.sessionId !== sessionId || !activeSession.isActive)
2997
+ const s = sessions.get(sessionId);
2998
+ if (!s || !s.isActive)
2986
2999
  return;
2987
3000
  const text = input.endsWith('\r') ? input.slice(0, -1) : input;
2988
- const isOrquesta = activeSession.cliType === 'orquesta';
3001
+ const isOrquesta = s.cliType === 'orquesta';
2989
3002
  // Bracketed paste for non-trivial inputs. Claude's TUI uses it to treat a long
2990
3003
  // block as a coherent paste. But orquesta-cli's Ink stack SWALLOWS the
2991
3004
  // \x1b[200~…\x1b[201~ sequence at its own stdin layer — the text never reaches
@@ -2994,7 +3007,7 @@ function writeSessionText(sessionId, input) {
2994
3007
  // fine; the separate \r below submits).
2995
3008
  const usePasteMarkers = text.length > 200 && !isOrquesta;
2996
3009
  const payload = usePasteMarkers ? `\x1b[200~${text}\x1b[201~` : text;
2997
- activeSession.ptyProcess.write(payload);
3010
+ s.ptyProcess.write(payload);
2998
3011
  if (isOrquesta) {
2999
3012
  // orquesta-cli (Ink) drops stdin events while it is rendering / processing.
3000
3013
  // If we fire \r during an active turn the Enter is queued and only surfaces
@@ -3008,12 +3021,13 @@ function writeSessionText(sessionId, input) {
3008
3021
  const MAX_WAIT_MS = 5000;
3009
3022
  const startAt = Date.now();
3010
3023
  const trySubmit = () => {
3011
- if (!activeSession || activeSession.sessionId !== sessionId || !activeSession.isActive)
3024
+ const cur = sessions.get(sessionId);
3025
+ if (!cur || !cur.isActive)
3012
3026
  return;
3013
3027
  const elapsed = Date.now() - startAt;
3014
- const idleFor = Date.now() - (activeSession.lastPtyOutputAt ?? startAt);
3028
+ const idleFor = Date.now() - (cur.lastPtyOutputAt ?? startAt);
3015
3029
  if (idleFor >= IDLE_THRESHOLD_MS || elapsed >= MAX_WAIT_MS) {
3016
- activeSession.ptyProcess.write('\r');
3030
+ cur.ptyProcess.write('\r');
3017
3031
  logger.info(`Session input submitted (${text.length} chars, idle=${idleFor}ms, elapsed=${elapsed}ms)`);
3018
3032
  }
3019
3033
  else {
@@ -3028,8 +3042,9 @@ function writeSessionText(sessionId, input) {
3028
3042
  const newlineCount = (text.match(/\n/g) || []).length;
3029
3043
  const delayMs = Math.min(800, 50 + Math.floor(text.length / 20) + newlineCount * 80);
3030
3044
  setTimeout(() => {
3031
- if (activeSession?.sessionId === sessionId && activeSession.isActive) {
3032
- activeSession.ptyProcess.write('\r');
3045
+ const cur = sessions.get(sessionId);
3046
+ if (cur?.isActive) {
3047
+ cur.ptyProcess.write('\r');
3033
3048
  }
3034
3049
  }, delayMs);
3035
3050
  }
@@ -3039,7 +3054,8 @@ function writeSessionText(sessionId, input) {
3039
3054
  * End the active interactive session.
3040
3055
  */
3041
3056
  export function endSession(sessionId) {
3042
- if (!activeSession || activeSession.sessionId !== sessionId) {
3057
+ const sessionRef = sessions.get(sessionId);
3058
+ if (!sessionRef) {
3043
3059
  logger.warn(`No active session with ID: ${sessionId}`);
3044
3060
  return false;
3045
3061
  }
@@ -3048,8 +3064,7 @@ export function endSession(sessionId) {
3048
3064
  // Try graceful shutdown — send /exit to Claude CLI.
3049
3065
  // Must split text and \r into two separate writes (same reason as sendSessionInput):
3050
3066
  // Ink misprocesses `/exit\r` as a cursor-move when both arrive in the same PTY chunk.
3051
- activeSession.ptyProcess.write('/exit');
3052
- const sessionRef = activeSession;
3067
+ sessionRef.ptyProcess.write('/exit');
3053
3068
  setTimeout(() => {
3054
3069
  try {
3055
3070
  sessionRef.ptyProcess.write('\r');
@@ -3057,27 +3072,27 @@ export function endSession(sessionId) {
3057
3072
  catch (_) { /* already dead */ }
3058
3073
  }, 50);
3059
3074
  // Mark as inactive so no more output is forwarded
3060
- activeSession.isActive = false;
3075
+ sessionRef.isActive = false;
3061
3076
  // Give Claude a moment to exit gracefully before forcing termination
3062
3077
  setTimeout(() => {
3063
- if (activeSession?.sessionId === sessionId) {
3078
+ if (sessions.get(sessionId) === sessionRef) {
3064
3079
  logger.warn('Session did not exit gracefully, forcing termination');
3065
3080
  try {
3066
3081
  sessionRef.ptyProcess.kill('SIGTERM');
3067
3082
  }
3068
3083
  catch (_) { /* already dead */ }
3069
3084
  setTimeout(() => {
3070
- if (activeSession?.sessionId === sessionId) {
3071
- const { channel: ch, startTime: st } = activeSession;
3085
+ if (sessions.get(sessionId) === sessionRef) {
3086
+ const { channel: ch, startTime: st } = sessionRef;
3072
3087
  const duration = Date.now() - st;
3073
3088
  logger.warn(`Session ${sessionId} did not respond to SIGTERM, sending SIGKILL`);
3074
- activeSession.isActive = false;
3075
- activeSession = null; // null BEFORE kill so onExit doesn't double-send
3089
+ sessionRef.isActive = false;
3090
+ sessions.delete(sessionId); // delete BEFORE kill so onExit doesn't double-send
3076
3091
  try {
3077
3092
  sessionRef.ptyProcess.kill('SIGKILL');
3078
3093
  }
3079
3094
  catch (_) { /* already dead */ }
3080
- // Manually send session:ended — onExit won't do it since we nulled activeSession
3095
+ // Manually send session:ended — onExit won't do it since we removed the session
3081
3096
  sendSessionEnded(ch, sessionId, -1, duration).catch(() => { });
3082
3097
  if (onSessionEndedCallback)
3083
3098
  onSessionEndedCallback();
@@ -3096,10 +3111,11 @@ export function endSession(sessionId) {
3096
3111
  * Resize the active interactive session PTY to match the frontend terminal dimensions.
3097
3112
  */
3098
3113
  export function resizeSession(sessionId, cols, rows) {
3099
- if (!activeSession || activeSession.sessionId !== sessionId)
3114
+ const s = sessions.get(sessionId);
3115
+ if (!s)
3100
3116
  return false;
3101
3117
  try {
3102
- activeSession.ptyProcess.resize(cols, rows);
3118
+ s.ptyProcess.resize(cols, rows);
3103
3119
  logger.debug(`[Session] Resized PTY to ${cols}x${rows}`);
3104
3120
  return true;
3105
3121
  }
@@ -3109,54 +3125,110 @@ export function resizeSession(sessionId, cols, rows) {
3109
3125
  }
3110
3126
  }
3111
3127
  /**
3112
- * Check if there's an active interactive session.
3128
+ * Check if there's ANY active interactive session.
3113
3129
  */
3114
3130
  export function hasActiveSession() {
3115
- return activeSession !== null && activeSession.isActive;
3131
+ for (const s of sessions.values())
3132
+ if (s.isActive)
3133
+ return true;
3134
+ return false;
3135
+ }
3136
+ /**
3137
+ * Check if there's an active interactive session bound to a given workstream
3138
+ * slot (undefined/'' = the default slot). Prompt-queue gating uses this so a
3139
+ * session in workstream A doesn't block prompts targeting workstream B.
3140
+ */
3141
+ export function hasActiveSessionFor(subAgentId) {
3142
+ const key = slotKey(subAgentId);
3143
+ for (const s of sessions.values()) {
3144
+ if (s.isActive && slotKey(s.subAgentId) === key)
3145
+ return true;
3146
+ }
3147
+ return false;
3116
3148
  }
3117
3149
  /**
3118
- * Get the current session ID if there's an active session.
3150
+ * Get the first active session ID, if any (back-compat single-session callers).
3119
3151
  */
3120
3152
  export function getActiveSessionId() {
3121
- return activeSession?.sessionId ?? null;
3153
+ for (const s of sessions.values())
3154
+ if (s.isActive)
3155
+ return s.sessionId;
3156
+ return null;
3122
3157
  }
3123
3158
  /**
3124
- * Get full session status for dashboard status queries.
3159
+ * List every active interactive session the dashboard renders these so each
3160
+ * user can see which workstreams are busy and attach to one (or start a new one
3161
+ * elsewhere).
3162
+ */
3163
+ export function getSessionList() {
3164
+ const now = Date.now();
3165
+ const list = [];
3166
+ for (const s of sessions.values()) {
3167
+ if (!s.isActive)
3168
+ continue;
3169
+ list.push({
3170
+ sessionId: s.sessionId,
3171
+ subAgentId: s.subAgentId ?? null,
3172
+ subAgentName: s.subAgentName ?? null,
3173
+ workingDirectory: s.workingDirectory,
3174
+ uptime: now - s.startTime,
3175
+ cliType: s.cliType,
3176
+ });
3177
+ }
3178
+ return list;
3179
+ }
3180
+ /**
3181
+ * Get full session status for dashboard status queries. Returns the first
3182
+ * active session for back-compat, plus the full list under `sessions`.
3125
3183
  */
3126
3184
  export function getSessionStatus() {
3127
- if (activeSession && activeSession.isActive) {
3185
+ const list = getSessionList();
3186
+ const first = list[0];
3187
+ if (first) {
3128
3188
  return {
3129
3189
  active: true,
3130
- sessionId: activeSession.sessionId,
3131
- workingDirectory: activeSession.workingDirectory,
3132
- uptime: Date.now() - activeSession.startTime,
3190
+ sessionId: first.sessionId,
3191
+ workingDirectory: first.workingDirectory,
3192
+ uptime: first.uptime,
3133
3193
  // Lets the dashboard re-attach with the right controls (PLAN vs Mode).
3134
- cliType: activeSession.cliType,
3194
+ cliType: first.cliType,
3195
+ sessions: list,
3135
3196
  };
3136
3197
  }
3137
- return { active: false, sessionId: null, workingDirectory: null, uptime: null };
3198
+ return { active: false, sessionId: null, workingDirectory: null, uptime: null, sessions: [] };
3138
3199
  }
3139
3200
  /**
3140
- * Force terminate any active session (for cleanup).
3201
+ * Force terminate a session (for cleanup). With no sessionId, terminates ALL.
3141
3202
  * Sends session:ended back to the dashboard so it can update UI state.
3142
3203
  */
3143
- export function terminateSession() {
3144
- if (activeSession) {
3145
- const { sessionId, channel, startTime, ptyProcess } = activeSession;
3204
+ export function terminateSession(sessionId) {
3205
+ const targets = sessionId
3206
+ ? (sessions.has(sessionId) ? [sessions.get(sessionId)] : [])
3207
+ : [...sessions.values()];
3208
+ for (const s of targets) {
3209
+ const { sessionId: sid, channel, startTime, ptyProcess } = s;
3146
3210
  const duration = Date.now() - startTime;
3147
- logger.warn(`Force terminating active session: ${sessionId}`);
3148
- activeSession.isActive = false;
3149
- activeSession = null; // null BEFORE kill so onExit doesn't double-send
3211
+ logger.warn(`Force terminating active session: ${sid}`);
3212
+ s.isActive = false;
3213
+ try {
3214
+ s.rogerthatWatcher?.close();
3215
+ }
3216
+ catch { /* already closed */ }
3217
+ if (s.pendingPhoneTimer) {
3218
+ clearTimeout(s.pendingPhoneTimer);
3219
+ s.pendingPhoneTimer = null;
3220
+ }
3221
+ sessions.delete(sid); // delete BEFORE kill so onExit doesn't double-send
3150
3222
  try {
3151
3223
  ptyProcess.kill('SIGKILL');
3152
3224
  }
3153
3225
  catch (_) { /* already dead */ }
3154
3226
  // Notify dashboard — the onExit handler won't fire session:ended
3155
- // because we already nulled activeSession above
3156
- sendSessionEnded(channel, sessionId, -1, duration).catch(() => { });
3157
- // Drain any queued prompts
3158
- if (onSessionEndedCallback)
3159
- onSessionEndedCallback();
3227
+ // because we already removed the session above
3228
+ sendSessionEnded(channel, sid, -1, duration).catch(() => { });
3160
3229
  }
3230
+ // Drain any queued prompts once after terminating
3231
+ if (targets.length > 0 && onSessionEndedCallback)
3232
+ onSessionEndedCallback();
3161
3233
  }
3162
3234
  //# sourceMappingURL=executor.js.map