perf(tui): spawn python gateway before loading @hermes/ink

Before: entry.tsx imports @hermes/ink (394KB bundle) + App + GatewayClient
in declaration order, then calls `gw.start()` at ~T=220ms. Python fork +
server.py import starts then.

After: only `GatewayClient` is statically imported (5ms, node builtins
only). `gw.start()` fires at ~T=5ms. @hermes/ink + App load in parallel
via `Promise.all(import(...))`. Python gets ~215ms of free runway to do
its own module import before node even finishes loading.

Net: session.info arrives ~150ms earlier in cold start. First React frame
timing is unchanged (still ~240ms — still gated by ink+app imports).

Removed a previously-tried warm-thread in server.py that pre-imported
`run_agent` in the background. Measured variance showed occasional
5-10s outliers (GIL thrashing); median gain was <100ms. Not worth the
non-determinism.
This commit is contained in:
Brooklyn Nicholson 2026-04-16 15:21:49 -05:00
parent f3920fec0b
commit 2d693c865c

View file

@ -1,7 +1,8 @@
#!/usr/bin/env node
import { render } from '@hermes/ink'
import { App } from './app.js'
// Import order matters for cold start: `GatewayClient` has only node-builtin
// deps (<20ms), so spawning the python gateway before loading @hermes/ink
// + App (~200ms combined) gives python ~200ms of free parallel time to run
// its own module imports instead of starting those after node is done.
import { GatewayClient } from './gatewayClient.js'
if (!process.stdin.isTTY) {
@ -11,6 +12,7 @@ if (!process.stdin.isTTY) {
const gw = new GatewayClient()
gw.start()
render(<App gw={gw} />, {
exitOnCtrlC: false
})
const [{ render }, { App }] = await Promise.all([import('@hermes/ink'), import('./app.js')])
render(<App gw={gw} />, { exitOnCtrlC: false })