* Handle spawn operation - creates a new NCode instance. * Uses in-process mode when enabled, otherwise uses tmux/iTerm2 split-pane view. * Falls back to in-process if pane backend detection fails (e.g., iTerm2 without * it2 CLI or tmux installed).
( input: SpawnInput, context: ToolUseContext, )
| 1027 | * it2 CLI or tmux installed). |
| 1028 | */ |
| 1029 | async function handleSpawn( |
| 1030 | input: SpawnInput, |
| 1031 | context: ToolUseContext, |
| 1032 | ): Promise<{ data: SpawnOutput }> { |
| 1033 | // Check if in-process mode is enabled via feature flag |
| 1034 | if (isInProcessEnabled()) { |
| 1035 | return handleSpawnInProcess(input, context) |
| 1036 | } |
| 1037 | |
| 1038 | // Pre-flight: ensure a pane backend is available before attempting pane-based spawn. |
| 1039 | // This handles auto-mode cases like iTerm2 without it2 or tmux installed, where |
| 1040 | // isInProcessEnabled() returns false but detectAndGetBackend() has no viable backend. |
| 1041 | // Narrowly scoped so user cancellation and other spawn errors propagate normally. |
| 1042 | try { |
| 1043 | await detectAndGetBackend() |
| 1044 | } catch (error) { |
| 1045 | // Only fall back silently in auto mode. If the user explicitly configured |
| 1046 | // teammateMode: 'tmux', let the error propagate so they see the actionable |
| 1047 | // install instructions from getTmuxInstallInstructions(). |
| 1048 | if (getTeammateModeFromSnapshot() !== 'auto') { |
| 1049 | throw error |
| 1050 | } |
| 1051 | logForDebugging( |
| 1052 | `[handleSpawn] No pane backend available, falling back to in-process: ${errorMessage(error)}`, |
| 1053 | ) |
| 1054 | // Record the fallback so isInProcessEnabled() reflects the actual mode |
| 1055 | // (fixes banner and other UI that would otherwise show tmux attach commands). |
| 1056 | markInProcessFallback() |
| 1057 | return handleSpawnInProcess(input, context) |
| 1058 | } |
| 1059 | |
| 1060 | // Backend is available (and now cached) - proceed with pane spawning. |
| 1061 | // Any errors here (user cancellation, validation, etc.) propagate to the caller. |
| 1062 | const useSplitPane = input.use_splitpane !== false |
| 1063 | if (useSplitPane) { |
| 1064 | return handleSpawnSplitPane(input, context) |
| 1065 | } |
| 1066 | return handleSpawnSeparateWindow(input, context) |
| 1067 | } |
| 1068 | |
| 1069 | // ============================================================================ |
| 1070 | // Main Export |
no test coverage detected