()
| 1894 | }) |
| 1895 | |
| 1896 | const run = async () => { |
| 1897 | if (running) { |
| 1898 | return |
| 1899 | } |
| 1900 | |
| 1901 | running = true |
| 1902 | runPhase = undefined |
| 1903 | notifySessionStateChanged('running') |
| 1904 | idleTimeout.stop() |
| 1905 | |
| 1906 | headlessProfilerCheckpoint('run_entry') |
| 1907 | // TODO(custom-tool-refactor): Should move to the init message, like browser |
| 1908 | |
| 1909 | await updateSdkMcp() |
| 1910 | headlessProfilerCheckpoint('after_updateSdkMcp') |
| 1911 | |
| 1912 | // Resolve deferred plugin installation (CLAUDE_CODE_SYNC_PLUGIN_INSTALL). |
| 1913 | // The promise was started eagerly so installation overlaps with other init. |
| 1914 | // Awaiting here guarantees plugins are available before the first ask(). |
| 1915 | // If CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS is set, races against that |
| 1916 | // deadline and proceeds without plugins on timeout (logging an error). |
| 1917 | if (pluginInstallPromise) { |
| 1918 | const timeoutMs = parseInt( |
| 1919 | process.env.CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS || '', |
| 1920 | 10, |
| 1921 | ) |
| 1922 | if (timeoutMs > 0) { |
| 1923 | const timeout = sleep(timeoutMs).then(() => 'timeout' as const) |
| 1924 | const result = await Promise.race([pluginInstallPromise, timeout]) |
| 1925 | if (result === 'timeout') { |
| 1926 | logError( |
| 1927 | new Error( |
| 1928 | `CLAUDE_CODE_SYNC_PLUGIN_INSTALL: plugin installation timed out after ${timeoutMs}ms`, |
| 1929 | ), |
| 1930 | ) |
| 1931 | logEvent('ncode_sync_plugin_install_timeout', { |
| 1932 | timeout_ms: timeoutMs, |
| 1933 | }) |
| 1934 | } |
| 1935 | } else { |
| 1936 | await pluginInstallPromise |
| 1937 | } |
| 1938 | pluginInstallPromise = null |
| 1939 | |
| 1940 | // Refresh commands, agents, and hooks now that plugins are installed |
| 1941 | await refreshPluginState() |
| 1942 | |
| 1943 | // Set up hot-reload for plugin hooks now that the initial install is done. |
| 1944 | // In sync-install mode, setup.ts skips this to avoid racing with the install. |
| 1945 | const { setupPluginHookHotReload } = await import( |
| 1946 | '../utils/plugins/loadPluginHooks.js' |
| 1947 | ) |
| 1948 | setupPluginHookHotReload() |
| 1949 | } |
| 1950 | |
| 1951 | // Only main-thread commands (agentId===undefined) — subagent |
| 1952 | // notifications are drained by the subagent's mid-turn gate in query.ts. |
| 1953 | // Defined outside the try block so it's accessible in the post-finally |
no test coverage detected