()
| 1863 | }) |
| 1864 | |
| 1865 | const run = async () => { |
| 1866 | if (running) { |
| 1867 | return |
| 1868 | } |
| 1869 | |
| 1870 | running = true |
| 1871 | runPhase = undefined |
| 1872 | notifySessionStateChanged('running') |
| 1873 | idleTimeout.stop() |
| 1874 | |
| 1875 | headlessProfilerCheckpoint('run_entry') |
| 1876 | // TODO(custom-tool-refactor): Should move to the init message, like browser |
| 1877 | |
| 1878 | await updateSdkMcp() |
| 1879 | headlessProfilerCheckpoint('after_updateSdkMcp') |
| 1880 | |
| 1881 | // Resolve deferred plugin installation (CLAUDE_CODE_SYNC_PLUGIN_INSTALL). |
| 1882 | // The promise was started eagerly so installation overlaps with other init. |
| 1883 | // Awaiting here guarantees plugins are available before the first ask(). |
| 1884 | // If CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS is set, races against that |
| 1885 | // deadline and proceeds without plugins on timeout (logging an error). |
| 1886 | if (pluginInstallPromise) { |
| 1887 | const timeoutMs = parseInt( |
| 1888 | process.env.CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS || '', |
| 1889 | 10, |
| 1890 | ) |
| 1891 | if (timeoutMs > 0) { |
| 1892 | const timeout = sleep(timeoutMs).then(() => 'timeout' as const) |
| 1893 | const result = await Promise.race([pluginInstallPromise, timeout]) |
| 1894 | if (result === 'timeout') { |
| 1895 | logError( |
| 1896 | new Error( |
| 1897 | `CLAUDE_CODE_SYNC_PLUGIN_INSTALL: plugin installation timed out after ${timeoutMs}ms`, |
| 1898 | ), |
| 1899 | ) |
| 1900 | logEvent('tengu_sync_plugin_install_timeout', { |
| 1901 | timeout_ms: timeoutMs, |
| 1902 | }) |
| 1903 | } |
| 1904 | } else { |
| 1905 | await pluginInstallPromise |
| 1906 | } |
| 1907 | pluginInstallPromise = null |
| 1908 | |
| 1909 | // Refresh commands, agents, and hooks now that plugins are installed |
| 1910 | await refreshPluginState() |
| 1911 | |
| 1912 | // Set up hot-reload for plugin hooks now that the initial install is done. |
| 1913 | // In sync-install mode, setup.ts skips this to avoid racing with the install. |
| 1914 | const { setupPluginHookHotReload } = await import( |
| 1915 | '../utils/plugins/loadPluginHooks.js' |
| 1916 | ) |
| 1917 | setupPluginHookHotReload() |
| 1918 | } |
| 1919 | |
| 1920 | // Only main-thread commands (agentId===undefined) — subagent |
| 1921 | // notifications are drained by the subagent's mid-turn gate in query.ts. |
| 1922 | // Defined outside the try block so it's accessible in the post-finally |
no test coverage detected