mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-08 03:01:47 +00:00
fix(tui): tighten cold-start edge cases after review
Clean up the remaining review nits: - let the deferred @hermes/ink import retry after a transient failure instead of memoizing a rejected promise forever - keep memory-monitor in-flight state inside a finally so future exceptions cannot suppress that memory level indefinitely - use read_raw_config for the TUI MCP cold-start probe instead of full load_config() - keep input.detect_drop for explicit relative path prefixes (./ and ../) while preserving the no-RPC fast path for ordinary plain prompts Tests: - python -m py_compile tui_gateway/server.py tui_gateway/entry.py - cd ui-tui && npm run type-check && npm run build - scripts/run_tests.sh tests/tui_gateway/test_protocol.py::test_sess_found tests/tools/test_code_execution_modes.py tests/tools/test_code_execution.py - cd ui-tui && npm test -- --run src/__tests__/useSessionLifecycle.test.ts src/__tests__/useConfigSync.test.ts
This commit is contained in:
parent
72a3af63d4
commit
88a9efdb1a
3 changed files with 29 additions and 23 deletions
|
|
@ -175,8 +175,8 @@ def main():
|
||||||
# loaded once by ``_config_mtime`` elsewhere) and only pay the import
|
# loaded once by ``_config_mtime`` elsewhere) and only pay the import
|
||||||
# cost when there's actually MCP work to do.
|
# cost when there's actually MCP work to do.
|
||||||
try:
|
try:
|
||||||
from hermes_cli.config import load_config
|
from hermes_cli.config import read_raw_config
|
||||||
_mcp_servers = (load_config() or {}).get("mcp_servers")
|
_mcp_servers = (read_raw_config() or {}).get("mcp_servers")
|
||||||
_has_mcp_servers = isinstance(_mcp_servers, dict) and len(_mcp_servers) > 0
|
_has_mcp_servers = isinstance(_mcp_servers, dict) and len(_mcp_servers) > 0
|
||||||
except Exception:
|
except Exception:
|
||||||
# Be conservative: if we can't decide, fall back to the old
|
# Be conservative: if we can't decide, fall back to the old
|
||||||
|
|
|
||||||
|
|
@ -127,9 +127,9 @@ export function useSubmission(opts: UseSubmissionOptions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plain prompts are the common path and should not pay an extra RPC
|
// Plain prompts are the common path and should not pay an extra RPC
|
||||||
// before prompt.submit. File-drop detection can still run for inputs
|
// before prompt.submit. File-drop detection still runs for absolute,
|
||||||
// that contain an absolute/tilde path or file:// URI.
|
// tilde, file://, and explicit relative paths.
|
||||||
if (!looksLikeSlashCommand(text) && !/(?:^|\s)(?:file:\/\/|~\/|\/)[^\s]+/.test(text)) {
|
if (!looksLikeSlashCommand(text) && !/(?:^|\s)(?:file:\/\/|~\/|\.?\.\/|\/)[^\s]+/.test(text)) {
|
||||||
return startSubmit(text, expand(text), showUserMessage)
|
return startSubmit(text, expand(text), showUserMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,16 @@ async function _ensureEvictInkCaches(): Promise<(level: 'all' | 'half') => unkno
|
||||||
return _evictInkCaches
|
return _evictInkCaches
|
||||||
}
|
}
|
||||||
|
|
||||||
_evictInkCachesPromise ??= import('@hermes/ink').then(mod => {
|
_evictInkCachesPromise ??= import('@hermes/ink')
|
||||||
_evictInkCaches = mod.evictInkCaches as (level: 'all' | 'half') => unknown
|
.then(mod => {
|
||||||
|
_evictInkCaches = mod.evictInkCaches as (level: 'all' | 'half') => unknown
|
||||||
|
|
||||||
return _evictInkCaches
|
return _evictInkCaches
|
||||||
})
|
})
|
||||||
|
.catch(err => {
|
||||||
|
_evictInkCachesPromise = null
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
|
||||||
return _evictInkCachesPromise
|
return _evictInkCachesPromise
|
||||||
}
|
}
|
||||||
|
|
@ -80,21 +85,22 @@ export function startMemoryMonitor({
|
||||||
// by the time a tick fires 10s after launch the app has already loaded
|
// by the time a tick fires 10s after launch the app has already loaded
|
||||||
// the same module, so this resolves instantly from the ESM cache.
|
// the same module, so this resolves instantly from the ESM cache.
|
||||||
try {
|
try {
|
||||||
const evictInkCaches = await _ensureEvictInkCaches()
|
try {
|
||||||
evictInkCaches(level === 'critical' ? 'all' : 'half')
|
const evictInkCaches = await _ensureEvictInkCaches()
|
||||||
} catch {
|
evictInkCaches(level === 'critical' ? 'all' : 'half')
|
||||||
// Best-effort: if the dynamic import fails for any reason we still
|
} catch {
|
||||||
// continue to the heap dump below so the user gets diagnostics.
|
// Best-effort: if the dynamic import fails for any reason we still
|
||||||
|
// continue to the heap dump below so the user gets diagnostics.
|
||||||
|
}
|
||||||
|
|
||||||
|
dumped.add(level)
|
||||||
|
const dump = await performHeapDump(level === 'critical' ? 'auto-critical' : 'auto-high').catch(() => null)
|
||||||
|
const snap: MemorySnapshot = { heapUsed, level, rss }
|
||||||
|
|
||||||
|
;(level === 'critical' ? onCritical : onHigh)?.(snap, dump)
|
||||||
|
} finally {
|
||||||
|
inFlight.delete(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
dumped.add(level)
|
|
||||||
const dump = await performHeapDump(level === 'critical' ? 'auto-critical' : 'auto-high').catch(() => null)
|
|
||||||
|
|
||||||
inFlight.delete(level)
|
|
||||||
|
|
||||||
const snap: MemorySnapshot = { heapUsed, level, rss }
|
|
||||||
|
|
||||||
;(level === 'critical' ? onCritical : onHigh)?.(snap, dump)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handle = setInterval(() => void tick(), intervalMs)
|
const handle = setInterval(() => void tick(), intervalMs)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue