Merge feat/opentui-native-engine into feat/opentui-memory-window

Brings the memory-window branch current with the base PR: multi-click already
in; adds OSC window title/notifications, +10 tree-sitter languages, /sessions
this-directory grouping + TUI cwd persistence, and the node26-fnm-discovery +
launch-cwd fix. All ui-opentui/gateway/launcher additions; windowing files are
ours alone.

# Conflicts:
#	ui-opentui/src/entry/main.tsx
This commit is contained in:
alt-glitch 2026-06-13 18:05:11 +05:30
commit 2a28bbcc72
61 changed files with 19867 additions and 8 deletions

View file

@ -0,0 +1,526 @@
{
"meta": {
"cell": "gate",
"ui": "ink",
"config": "ink",
"mode": "digest",
"rep": 0,
"run_id": "mqaeu4s1-5kii",
"utc": "2026-06-12T04:12:13.105Z",
"sha": "f0ec24ad5",
"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": "/home/daimon/github/worktrees/hermes-agent/quiet-quill/hermes-agent/bench/.cache/fixture-300.ndjson",
"msgs": 300,
"sha256": "ac81e975c299da7d50cfda7fc0fee33c356bd31a4e17a7f0f8e49905e205051a"
},
"sample_every": 100,
"mode_params": {},
"ui_pid": 131301,
"gw_pid": 131317,
"cgroup": null,
"load_avg_at_start": [
0.86,
3.18,
2.1
],
"instrumented": false
},
"samples": [
{
"kind": "periodic",
"t_ms": 28,
"msgs": null,
"events": null,
"pty_bytes": 0,
"pty_writes": 0,
"rss_kb": 57968,
"pss_kb": 32819,
"private_dirty_kb": 20836,
"vmhwm_kb": 58048,
"utime_ticks": 1,
"stime_ticks": 1
},
{
"kind": "periodic",
"t_ms": 1036,
"msgs": null,
"events": null,
"pty_bytes": 6685,
"pty_writes": 11,
"rss_kb": 110740,
"pss_kb": 71656,
"private_dirty_kb": 52440,
"vmhwm_kb": 110740,
"utime_ticks": 24,
"stime_ticks": 3
},
{
"kind": "boundary",
"t_ms": 1490,
"msgs": 100,
"events": 355,
"pty_bytes": 10222,
"pty_writes": 14,
"rss_kb": 130316,
"pss_kb": 91080,
"private_dirty_kb": 71084,
"vmhwm_kb": 130432,
"utime_ticks": 34,
"stime_ticks": 4
},
{
"kind": "boundary",
"t_ms": 1541,
"msgs": 200,
"events": 738,
"pty_bytes": 18217,
"pty_writes": 17,
"rss_kb": 158520,
"pss_kb": 119167,
"private_dirty_kb": 99160,
"vmhwm_kb": 158596,
"utime_ticks": 44,
"stime_ticks": 4
},
{
"kind": "boundary",
"t_ms": 1566,
"msgs": 300,
"events": 1051,
"pty_bytes": 21202,
"pty_writes": 18,
"rss_kb": 170104,
"pss_kb": 130703,
"private_dirty_kb": 110680,
"vmhwm_kb": 170108,
"utime_ticks": 48,
"stime_ticks": 5
},
{
"kind": "done",
"t_ms": 1567,
"msgs": 300,
"events": 1051,
"pty_bytes": 21202,
"pty_writes": 18,
"rss_kb": 170108,
"pss_kb": 130707,
"private_dirty_kb": 110684,
"vmhwm_kb": 170108,
"utime_ticks": 49,
"stime_ticks": 5
},
{
"kind": "periodic",
"t_ms": 2043,
"msgs": 300,
"events": null,
"pty_bytes": 32782,
"pty_writes": 26,
"rss_kb": 176524,
"pss_kb": 137064,
"private_dirty_kb": 117036,
"vmhwm_kb": 182800,
"utime_ticks": 61,
"stime_ticks": 5
},
{
"kind": "periodic",
"t_ms": 3052,
"msgs": 300,
"events": null,
"pty_bytes": 32872,
"pty_writes": 28,
"rss_kb": 176536,
"pss_kb": 137076,
"private_dirty_kb": 117048,
"vmhwm_kb": 182800,
"utime_ticks": 61,
"stime_ticks": 5
},
{
"kind": "periodic",
"t_ms": 4067,
"msgs": 300,
"events": null,
"pty_bytes": 32962,
"pty_writes": 30,
"rss_kb": 176544,
"pss_kb": 137084,
"private_dirty_kb": 117056,
"vmhwm_kb": 182800,
"utime_ticks": 61,
"stime_ticks": 5
},
{
"kind": "periodic",
"t_ms": 5068,
"msgs": 300,
"events": null,
"pty_bytes": 33052,
"pty_writes": 32,
"rss_kb": 176552,
"pss_kb": 137092,
"private_dirty_kb": 117064,
"vmhwm_kb": 182800,
"utime_ticks": 61,
"stime_ticks": 5
},
{
"kind": "periodic",
"t_ms": 6077,
"msgs": 300,
"events": null,
"pty_bytes": 33142,
"pty_writes": 34,
"rss_kb": 176560,
"pss_kb": 137100,
"private_dirty_kb": 117072,
"vmhwm_kb": 182800,
"utime_ticks": 61,
"stime_ticks": 5
},
{
"kind": "periodic",
"t_ms": 7081,
"msgs": 300,
"events": null,
"pty_bytes": 33232,
"pty_writes": 36,
"rss_kb": 176572,
"pss_kb": 137112,
"private_dirty_kb": 117084,
"vmhwm_kb": 182800,
"utime_ticks": 62,
"stime_ticks": 5
},
{
"kind": "periodic",
"t_ms": 8087,
"msgs": 300,
"events": null,
"pty_bytes": 33322,
"pty_writes": 38,
"rss_kb": 176580,
"pss_kb": 137120,
"private_dirty_kb": 117092,
"vmhwm_kb": 182800,
"utime_ticks": 62,
"stime_ticks": 5
},
{
"kind": "periodic",
"t_ms": 9101,
"msgs": 300,
"events": null,
"pty_bytes": 33412,
"pty_writes": 40,
"rss_kb": 176584,
"pss_kb": 137124,
"private_dirty_kb": 117096,
"vmhwm_kb": 182800,
"utime_ticks": 62,
"stime_ticks": 5
},
{
"kind": "periodic",
"t_ms": 10106,
"msgs": 300,
"events": null,
"pty_bytes": 33502,
"pty_writes": 42,
"rss_kb": 176584,
"pss_kb": 137124,
"private_dirty_kb": 117096,
"vmhwm_kb": 182800,
"utime_ticks": 63,
"stime_ticks": 5
},
{
"kind": "periodic",
"t_ms": 11112,
"msgs": 300,
"events": null,
"pty_bytes": 33622,
"pty_writes": 44,
"rss_kb": 176596,
"pss_kb": 137136,
"private_dirty_kb": 117108,
"vmhwm_kb": 182800,
"utime_ticks": 63,
"stime_ticks": 5
},
{
"kind": "periodic",
"t_ms": 12114,
"msgs": 300,
"events": null,
"pty_bytes": 33731,
"pty_writes": 46,
"rss_kb": 176600,
"pss_kb": 137140,
"private_dirty_kb": 117112,
"vmhwm_kb": 182800,
"utime_ticks": 63,
"stime_ticks": 5
},
{
"kind": "periodic",
"t_ms": 13116,
"msgs": 300,
"events": null,
"pty_bytes": 33821,
"pty_writes": 48,
"rss_kb": 176604,
"pss_kb": 137144,
"private_dirty_kb": 117116,
"vmhwm_kb": 182800,
"utime_ticks": 63,
"stime_ticks": 5
},
{
"kind": "periodic",
"t_ms": 14124,
"msgs": 300,
"events": null,
"pty_bytes": 33911,
"pty_writes": 50,
"rss_kb": 176620,
"pss_kb": 137160,
"private_dirty_kb": 117132,
"vmhwm_kb": 182800,
"utime_ticks": 63,
"stime_ticks": 5
},
{
"kind": "periodic",
"t_ms": 15135,
"msgs": 300,
"events": null,
"pty_bytes": 34001,
"pty_writes": 52,
"rss_kb": 177056,
"pss_kb": 137596,
"private_dirty_kb": 117568,
"vmhwm_kb": 182800,
"utime_ticks": 64,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 16143,
"msgs": 300,
"events": null,
"pty_bytes": 34091,
"pty_writes": 54,
"rss_kb": 177064,
"pss_kb": 137604,
"private_dirty_kb": 117576,
"vmhwm_kb": 182800,
"utime_ticks": 64,
"stime_ticks": 6
},
{
"kind": "final",
"t_ms": 16679,
"msgs": 300,
"events": 1051,
"pty_bytes": 34181,
"pty_writes": 56,
"rss_kb": 177064,
"pss_kb": 137604,
"private_dirty_kb": 117576,
"vmhwm_kb": 182800,
"utime_ticks": 64,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 17152,
"msgs": 300,
"events": null,
"pty_bytes": 44856,
"pty_writes": 63,
"rss_kb": 183504,
"pss_kb": 144044,
"private_dirty_kb": 124016,
"vmhwm_kb": 183504,
"utime_ticks": 68,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 18156,
"msgs": 300,
"events": null,
"pty_bytes": 48502,
"pty_writes": 66,
"rss_kb": 186292,
"pss_kb": 146832,
"private_dirty_kb": 126804,
"vmhwm_kb": 186292,
"utime_ticks": 70,
"stime_ticks": 6
}
],
"events": [
{
"kind": "rpc",
"method": "config.get",
"t_ms": 227
},
{
"kind": "rpc",
"method": "commands.catalog",
"t_ms": 227
},
{
"kind": "rpc",
"method": "setup.status",
"t_ms": 227
},
{
"kind": "rpc",
"method": "session.create",
"t_ms": 252
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 252
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 252
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 252
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 1739
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 3251
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 4738
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 5242
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 6250
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 7758
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 9249
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 10254
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 10758
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 12263
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 13746
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 15259
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 15259
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 16743
},
{
"kind": "rpc",
"method": "terminal.resize",
"t_ms": 16794
},
{
"kind": "rpc",
"method": "terminal.resize",
"t_ms": 17198
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 18254
}
],
"summary": {
"result": "completed",
"cap_hit": false,
"cap_hit_basis": null,
"at_messages": null,
"exit": {
"exitCode": 0,
"signal": 0,
"t": 18291
},
"stream_done": true,
"msgs_streamed": 300,
"events_streamed": 1051,
"pty_bytes_total": 48954,
"pty_data_callbacks": 71,
"first_byte_ms": 99,
"session_create_ms": 252,
"stream_start_ms": 1439,
"vmhwm_kb": 186292,
"cg_peak": null,
"drain_max_loop_lag_ms": 8,
"drain_lag_violations": 0,
"drain_ok": true,
"digest": "0f62155086e89590f62e059cc7bcf0851b6c88dac942a8f2303ac905481dea48"
},
"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 │ 16s │ ✓ 15s │ 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 │ 16s │ ✓ 15s │ voice off ─ …es-agent (…i-native-engine)❯768"
}

View file

@ -0,0 +1,526 @@
{
"meta": {
"cell": "gate",
"ui": "ink",
"config": "ink",
"mode": "digest",
"rep": 1,
"run_id": "mqaeuqq6-w5nl",
"utc": "2026-06-12T04:12:41.551Z",
"sha": "f0ec24ad5",
"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": "/home/daimon/github/worktrees/hermes-agent/quiet-quill/hermes-agent/bench/.cache/fixture-300.ndjson",
"msgs": 300,
"sha256": "ac81e975c299da7d50cfda7fc0fee33c356bd31a4e17a7f0f8e49905e205051a"
},
"sample_every": 100,
"mode_params": {},
"ui_pid": 132910,
"gw_pid": 132934,
"cgroup": null,
"load_avg_at_start": [
1.38,
3.07,
2.1
],
"instrumented": false
},
"samples": [
{
"kind": "periodic",
"t_ms": 26,
"msgs": null,
"events": null,
"pty_bytes": 0,
"pty_writes": 0,
"rss_kb": 57172,
"pss_kb": 31971,
"private_dirty_kb": 20056,
"vmhwm_kb": 57172,
"utime_ticks": 1,
"stime_ticks": 0
},
{
"kind": "periodic",
"t_ms": 1029,
"msgs": null,
"events": null,
"pty_bytes": 6665,
"pty_writes": 12,
"rss_kb": 110572,
"pss_kb": 71418,
"private_dirty_kb": 52096,
"vmhwm_kb": 110572,
"utime_ticks": 24,
"stime_ticks": 3
},
{
"kind": "boundary",
"t_ms": 1456,
"msgs": 100,
"events": 355,
"pty_bytes": 10756,
"pty_writes": 15,
"rss_kb": 123240,
"pss_kb": 83980,
"private_dirty_kb": 63976,
"vmhwm_kb": 123256,
"utime_ticks": 29,
"stime_ticks": 3
},
{
"kind": "boundary",
"t_ms": 1530,
"msgs": 200,
"events": 738,
"pty_bytes": 17854,
"pty_writes": 17,
"rss_kb": 153588,
"pss_kb": 114117,
"private_dirty_kb": 94008,
"vmhwm_kb": 153612,
"utime_ticks": 42,
"stime_ticks": 5
},
{
"kind": "boundary",
"t_ms": 1582,
"msgs": 300,
"events": 1051,
"pty_bytes": 21780,
"pty_writes": 18,
"rss_kb": 183452,
"pss_kb": 143933,
"private_dirty_kb": 123808,
"vmhwm_kb": 183828,
"utime_ticks": 53,
"stime_ticks": 6
},
{
"kind": "done",
"t_ms": 1585,
"msgs": 300,
"events": 1051,
"pty_bytes": 21780,
"pty_writes": 18,
"rss_kb": 184052,
"pss_kb": 144533,
"private_dirty_kb": 124408,
"vmhwm_kb": 184368,
"utime_ticks": 53,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 2033,
"msgs": 300,
"events": null,
"pty_bytes": 33500,
"pty_writes": 22,
"rss_kb": 183660,
"pss_kb": 144141,
"private_dirty_kb": 124016,
"vmhwm_kb": 187952,
"utime_ticks": 65,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 3039,
"msgs": 300,
"events": null,
"pty_bytes": 33590,
"pty_writes": 24,
"rss_kb": 183680,
"pss_kb": 144161,
"private_dirty_kb": 124036,
"vmhwm_kb": 187952,
"utime_ticks": 65,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 4045,
"msgs": 300,
"events": null,
"pty_bytes": 33680,
"pty_writes": 26,
"rss_kb": 183684,
"pss_kb": 144164,
"private_dirty_kb": 124040,
"vmhwm_kb": 187952,
"utime_ticks": 66,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 5046,
"msgs": 300,
"events": null,
"pty_bytes": 33770,
"pty_writes": 28,
"rss_kb": 183684,
"pss_kb": 144165,
"private_dirty_kb": 124040,
"vmhwm_kb": 187952,
"utime_ticks": 66,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 6047,
"msgs": 300,
"events": null,
"pty_bytes": 33860,
"pty_writes": 30,
"rss_kb": 183688,
"pss_kb": 144169,
"private_dirty_kb": 124044,
"vmhwm_kb": 187952,
"utime_ticks": 66,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 7057,
"msgs": 300,
"events": null,
"pty_bytes": 33950,
"pty_writes": 32,
"rss_kb": 183688,
"pss_kb": 144169,
"private_dirty_kb": 124044,
"vmhwm_kb": 187952,
"utime_ticks": 67,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 8065,
"msgs": 300,
"events": null,
"pty_bytes": 34040,
"pty_writes": 34,
"rss_kb": 183692,
"pss_kb": 144173,
"private_dirty_kb": 124048,
"vmhwm_kb": 187952,
"utime_ticks": 67,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 9067,
"msgs": 300,
"events": null,
"pty_bytes": 34130,
"pty_writes": 36,
"rss_kb": 183692,
"pss_kb": 144173,
"private_dirty_kb": 124048,
"vmhwm_kb": 187952,
"utime_ticks": 67,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 10078,
"msgs": 300,
"events": null,
"pty_bytes": 34220,
"pty_writes": 38,
"rss_kb": 183692,
"pss_kb": 144173,
"private_dirty_kb": 124048,
"vmhwm_kb": 187952,
"utime_ticks": 67,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 11085,
"msgs": 300,
"events": null,
"pty_bytes": 34340,
"pty_writes": 40,
"rss_kb": 183700,
"pss_kb": 144181,
"private_dirty_kb": 124056,
"vmhwm_kb": 187952,
"utime_ticks": 67,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 12088,
"msgs": 300,
"events": null,
"pty_bytes": 34449,
"pty_writes": 42,
"rss_kb": 183700,
"pss_kb": 144181,
"private_dirty_kb": 124056,
"vmhwm_kb": 187952,
"utime_ticks": 68,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 13092,
"msgs": 300,
"events": null,
"pty_bytes": 34539,
"pty_writes": 44,
"rss_kb": 183700,
"pss_kb": 144181,
"private_dirty_kb": 124056,
"vmhwm_kb": 187952,
"utime_ticks": 68,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 14098,
"msgs": 300,
"events": null,
"pty_bytes": 34629,
"pty_writes": 46,
"rss_kb": 183700,
"pss_kb": 144181,
"private_dirty_kb": 124056,
"vmhwm_kb": 187952,
"utime_ticks": 68,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 15104,
"msgs": 300,
"events": null,
"pty_bytes": 34719,
"pty_writes": 48,
"rss_kb": 183700,
"pss_kb": 144181,
"private_dirty_kb": 124056,
"vmhwm_kb": 187952,
"utime_ticks": 68,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 16112,
"msgs": 300,
"events": null,
"pty_bytes": 34809,
"pty_writes": 50,
"rss_kb": 183700,
"pss_kb": 144181,
"private_dirty_kb": 124056,
"vmhwm_kb": 187952,
"utime_ticks": 68,
"stime_ticks": 7
},
{
"kind": "final",
"t_ms": 16668,
"msgs": 300,
"events": 1051,
"pty_bytes": 34854,
"pty_writes": 51,
"rss_kb": 183704,
"pss_kb": 144185,
"private_dirty_kb": 124060,
"vmhwm_kb": 187952,
"utime_ticks": 68,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 17123,
"msgs": 300,
"events": null,
"pty_bytes": 45574,
"pty_writes": 58,
"rss_kb": 186688,
"pss_kb": 147169,
"private_dirty_kb": 127044,
"vmhwm_kb": 187952,
"utime_ticks": 75,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 18132,
"msgs": 300,
"events": null,
"pty_bytes": 49220,
"pty_writes": 61,
"rss_kb": 186700,
"pss_kb": 147181,
"private_dirty_kb": 127056,
"vmhwm_kb": 187952,
"utime_ticks": 76,
"stime_ticks": 7
}
],
"events": [
{
"kind": "rpc",
"method": "config.get",
"t_ms": 225
},
{
"kind": "rpc",
"method": "commands.catalog",
"t_ms": 225
},
{
"kind": "rpc",
"method": "setup.status",
"t_ms": 225
},
{
"kind": "rpc",
"method": "session.create",
"t_ms": 225
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 225
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 225
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 225
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 1729
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 3237
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 4743
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 5243
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 6227
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 7734
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 9245
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 10228
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 10733
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 12238
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 13744
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 15229
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 15229
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 16737
},
{
"kind": "rpc",
"method": "terminal.resize",
"t_ms": 16789
},
{
"kind": "rpc",
"method": "terminal.resize",
"t_ms": 17172
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 18234
}
],
"summary": {
"result": "completed",
"cap_hit": false,
"cap_hit_basis": null,
"at_messages": null,
"exit": {
"exitCode": 0,
"signal": 0,
"t": 18281
},
"stream_done": true,
"msgs_streamed": 300,
"events_streamed": 1051,
"pty_bytes_total": 49672,
"pty_data_callbacks": 74,
"first_byte_ms": 101,
"session_create_ms": 225,
"stream_start_ms": 1428,
"vmhwm_kb": 187952,
"cg_peak": null,
"drain_max_loop_lag_ms": 4,
"drain_lag_violations": 0,
"drain_ok": true,
"digest": "6a921c7981a564ea92adf567c2e55945775a32f52325bb0f05db395af665ba04"
},
"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 │ 16s │ ✓ 15s │ voice off ─ …es-agent (…i-native-engine)❯│┃7Excepteur 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 │ 17s │ ✓ 15s │ voice off ─ …es-agent (…i-native-engine)❯68"
}

View file

@ -0,0 +1,526 @@
{
"meta": {
"cell": "gate",
"ui": "ink",
"config": "ink",
"mode": "digest",
"rep": 0,
"run_id": "mqaf74nt-gznn",
"utc": "2026-06-12T04:22:19.481Z",
"sha": "f0ec24ad5",
"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": "/home/daimon/github/worktrees/hermes-agent/quiet-quill/hermes-agent/bench/.cache/fixture-300.ndjson",
"msgs": 300,
"sha256": "ac81e975c299da7d50cfda7fc0fee33c356bd31a4e17a7f0f8e49905e205051a"
},
"sample_every": 100,
"mode_params": {},
"ui_pid": 165899,
"gw_pid": 165908,
"cgroup": null,
"load_avg_at_start": [
0.29,
1.01,
1.47
],
"instrumented": false
},
"samples": [
{
"kind": "periodic",
"t_ms": 27,
"msgs": null,
"events": null,
"pty_bytes": 0,
"pty_writes": 0,
"rss_kb": 47056,
"pss_kb": 21937,
"private_dirty_kb": 9948,
"vmhwm_kb": 47056,
"utime_ticks": 1,
"stime_ticks": 0
},
{
"kind": "periodic",
"t_ms": 1033,
"msgs": null,
"events": null,
"pty_bytes": 6685,
"pty_writes": 12,
"rss_kb": 111652,
"pss_kb": 74425,
"private_dirty_kb": 53188,
"vmhwm_kb": 111652,
"utime_ticks": 24,
"stime_ticks": 3
},
{
"kind": "boundary",
"t_ms": 1461,
"msgs": 100,
"events": 355,
"pty_bytes": 10768,
"pty_writes": 15,
"rss_kb": 124184,
"pss_kb": 86902,
"private_dirty_kb": 64996,
"vmhwm_kb": 124312,
"utime_ticks": 29,
"stime_ticks": 4
},
{
"kind": "boundary",
"t_ms": 1538,
"msgs": 200,
"events": 738,
"pty_bytes": 17881,
"pty_writes": 17,
"rss_kb": 158184,
"pss_kb": 120650,
"private_dirty_kb": 98616,
"vmhwm_kb": 158236,
"utime_ticks": 42,
"stime_ticks": 5
},
{
"kind": "boundary",
"t_ms": 1563,
"msgs": 300,
"events": 1051,
"pty_bytes": 21807,
"pty_writes": 18,
"rss_kb": 173012,
"pss_kb": 135435,
"private_dirty_kb": 113380,
"vmhwm_kb": 173300,
"utime_ticks": 48,
"stime_ticks": 6
},
{
"kind": "done",
"t_ms": 1565,
"msgs": 300,
"events": 1051,
"pty_bytes": 21807,
"pty_writes": 18,
"rss_kb": 173764,
"pss_kb": 136187,
"private_dirty_kb": 114132,
"vmhwm_kb": 174096,
"utime_ticks": 48,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 2042,
"msgs": 300,
"events": null,
"pty_bytes": 33524,
"pty_writes": 22,
"rss_kb": 180232,
"pss_kb": 142623,
"private_dirty_kb": 120600,
"vmhwm_kb": 187988,
"utime_ticks": 62,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 3047,
"msgs": 300,
"events": null,
"pty_bytes": 33614,
"pty_writes": 24,
"rss_kb": 180236,
"pss_kb": 142627,
"private_dirty_kb": 120604,
"vmhwm_kb": 187988,
"utime_ticks": 63,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 4051,
"msgs": 300,
"events": null,
"pty_bytes": 33704,
"pty_writes": 26,
"rss_kb": 180240,
"pss_kb": 142631,
"private_dirty_kb": 120608,
"vmhwm_kb": 187988,
"utime_ticks": 63,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 5056,
"msgs": 300,
"events": null,
"pty_bytes": 33794,
"pty_writes": 28,
"rss_kb": 180244,
"pss_kb": 142635,
"private_dirty_kb": 120612,
"vmhwm_kb": 187988,
"utime_ticks": 63,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 6075,
"msgs": 300,
"events": null,
"pty_bytes": 33884,
"pty_writes": 30,
"rss_kb": 180248,
"pss_kb": 142639,
"private_dirty_kb": 120616,
"vmhwm_kb": 187988,
"utime_ticks": 63,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 7088,
"msgs": 300,
"events": null,
"pty_bytes": 33974,
"pty_writes": 32,
"rss_kb": 180248,
"pss_kb": 142639,
"private_dirty_kb": 120616,
"vmhwm_kb": 187988,
"utime_ticks": 64,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 8107,
"msgs": 300,
"events": null,
"pty_bytes": 34064,
"pty_writes": 34,
"rss_kb": 180256,
"pss_kb": 142647,
"private_dirty_kb": 120624,
"vmhwm_kb": 187988,
"utime_ticks": 64,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 9124,
"msgs": 300,
"events": null,
"pty_bytes": 34154,
"pty_writes": 36,
"rss_kb": 180256,
"pss_kb": 142647,
"private_dirty_kb": 120624,
"vmhwm_kb": 187988,
"utime_ticks": 64,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 10131,
"msgs": 300,
"events": null,
"pty_bytes": 34244,
"pty_writes": 38,
"rss_kb": 180264,
"pss_kb": 142655,
"private_dirty_kb": 120632,
"vmhwm_kb": 187988,
"utime_ticks": 64,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 11142,
"msgs": 300,
"events": null,
"pty_bytes": 34364,
"pty_writes": 40,
"rss_kb": 180264,
"pss_kb": 142655,
"private_dirty_kb": 120632,
"vmhwm_kb": 187988,
"utime_ticks": 64,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 12155,
"msgs": 300,
"events": null,
"pty_bytes": 34473,
"pty_writes": 42,
"rss_kb": 180264,
"pss_kb": 142655,
"private_dirty_kb": 120632,
"vmhwm_kb": 187988,
"utime_ticks": 65,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 13165,
"msgs": 300,
"events": null,
"pty_bytes": 34563,
"pty_writes": 44,
"rss_kb": 180264,
"pss_kb": 142655,
"private_dirty_kb": 120632,
"vmhwm_kb": 187988,
"utime_ticks": 65,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 14172,
"msgs": 300,
"events": null,
"pty_bytes": 34653,
"pty_writes": 46,
"rss_kb": 180264,
"pss_kb": 142576,
"private_dirty_kb": 120632,
"vmhwm_kb": 187988,
"utime_ticks": 65,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 15184,
"msgs": 300,
"events": null,
"pty_bytes": 34743,
"pty_writes": 48,
"rss_kb": 180264,
"pss_kb": 142655,
"private_dirty_kb": 120632,
"vmhwm_kb": 187988,
"utime_ticks": 65,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 16197,
"msgs": 300,
"events": null,
"pty_bytes": 34833,
"pty_writes": 50,
"rss_kb": 180268,
"pss_kb": 142659,
"private_dirty_kb": 120636,
"vmhwm_kb": 187988,
"utime_ticks": 65,
"stime_ticks": 6
},
{
"kind": "final",
"t_ms": 16672,
"msgs": 300,
"events": 1051,
"pty_bytes": 34923,
"pty_writes": 52,
"rss_kb": 180276,
"pss_kb": 142667,
"private_dirty_kb": 120644,
"vmhwm_kb": 187988,
"utime_ticks": 66,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 17209,
"msgs": 300,
"events": null,
"pty_bytes": 45598,
"pty_writes": 58,
"rss_kb": 181952,
"pss_kb": 144343,
"private_dirty_kb": 122320,
"vmhwm_kb": 187988,
"utime_ticks": 72,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 18215,
"msgs": 300,
"events": null,
"pty_bytes": 49244,
"pty_writes": 61,
"rss_kb": 182028,
"pss_kb": 144420,
"private_dirty_kb": 122396,
"vmhwm_kb": 187988,
"utime_ticks": 74,
"stime_ticks": 7
}
],
"events": [
{
"kind": "rpc",
"method": "config.get",
"t_ms": 228
},
{
"kind": "rpc",
"method": "commands.catalog",
"t_ms": 228
},
{
"kind": "rpc",
"method": "setup.status",
"t_ms": 253
},
{
"kind": "rpc",
"method": "session.create",
"t_ms": 253
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 253
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 253
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 253
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 1762
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 3246
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 4753
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 5256
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 6249
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 7746
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 9247
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 10257
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 10764
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 12254
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 13740
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 15258
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 15258
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 16752
},
{
"kind": "rpc",
"method": "terminal.resize",
"t_ms": 16777
},
{
"kind": "rpc",
"method": "terminal.resize",
"t_ms": 17182
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 18263
}
],
"summary": {
"result": "completed",
"cap_hit": false,
"cap_hit_basis": null,
"at_messages": null,
"exit": {
"exitCode": 0,
"signal": 0,
"t": 18286
},
"stream_done": true,
"msgs_streamed": 300,
"events_streamed": 1051,
"pty_bytes_total": 49696,
"pty_data_callbacks": 67,
"first_byte_ms": 115,
"session_create_ms": 253,
"stream_start_ms": 1435,
"vmhwm_kb": 187988,
"cg_peak": null,
"drain_max_loop_lag_ms": 3,
"drain_lag_violations": 0,
"drain_ok": true,
"digest": "0f62155086e89590f62e059cc7bcf0851b6c88dac942a8f2303ac905481dea48"
},
"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 │ 16s │ ✓ 15s │ 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 │ 16s │ ✓ 15s │ voice off ─ …es-agent (…i-native-engine)❯768"
}

View file

@ -0,0 +1,526 @@
{
"meta": {
"cell": "gate",
"ui": "ink",
"config": "ink",
"mode": "digest",
"rep": 1,
"run_id": "mqaf7qlt-11yq",
"utc": "2026-06-12T04:22:47.921Z",
"sha": "f0ec24ad5",
"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": "/home/daimon/github/worktrees/hermes-agent/quiet-quill/hermes-agent/bench/.cache/fixture-300.ndjson",
"msgs": 300,
"sha256": "ac81e975c299da7d50cfda7fc0fee33c356bd31a4e17a7f0f8e49905e205051a"
},
"sample_every": 100,
"mode_params": {},
"ui_pid": 167362,
"gw_pid": 167375,
"cgroup": null,
"load_avg_at_start": [
0.4,
0.96,
1.44
],
"instrumented": false
},
"samples": [
{
"kind": "periodic",
"t_ms": 27,
"msgs": null,
"events": null,
"pty_bytes": 0,
"pty_writes": 0,
"rss_kb": 57124,
"pss_kb": 31720,
"private_dirty_kb": 20020,
"vmhwm_kb": 57124,
"utime_ticks": 1,
"stime_ticks": 1
},
{
"kind": "periodic",
"t_ms": 1031,
"msgs": null,
"events": null,
"pty_bytes": 6665,
"pty_writes": 9,
"rss_kb": 110124,
"pss_kb": 72684,
"private_dirty_kb": 51672,
"vmhwm_kb": 110124,
"utime_ticks": 26,
"stime_ticks": 4
},
{
"kind": "boundary",
"t_ms": 1481,
"msgs": 100,
"events": 355,
"pty_bytes": 10675,
"pty_writes": 12,
"rss_kb": 128172,
"pss_kb": 90565,
"private_dirty_kb": 68808,
"vmhwm_kb": 128256,
"utime_ticks": 34,
"stime_ticks": 5
},
{
"kind": "boundary",
"t_ms": 1557,
"msgs": 200,
"events": 738,
"pty_bytes": 18489,
"pty_writes": 14,
"rss_kb": 166540,
"pss_kb": 128783,
"private_dirty_kb": 106984,
"vmhwm_kb": 166772,
"utime_ticks": 44,
"stime_ticks": 6
},
{
"kind": "boundary",
"t_ms": 1582,
"msgs": 300,
"events": 1051,
"pty_bytes": 22560,
"pty_writes": 15,
"rss_kb": 182916,
"pss_kb": 145159,
"private_dirty_kb": 123360,
"vmhwm_kb": 182992,
"utime_ticks": 50,
"stime_ticks": 6
},
{
"kind": "done",
"t_ms": 1583,
"msgs": 300,
"events": 1051,
"pty_bytes": 22560,
"pty_writes": 15,
"rss_kb": 183032,
"pss_kb": 145275,
"private_dirty_kb": 123476,
"vmhwm_kb": 183036,
"utime_ticks": 51,
"stime_ticks": 6
},
{
"kind": "periodic",
"t_ms": 2035,
"msgs": 300,
"events": null,
"pty_bytes": 34064,
"pty_writes": 19,
"rss_kb": 187616,
"pss_kb": 149816,
"private_dirty_kb": 127996,
"vmhwm_kb": 196580,
"utime_ticks": 64,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 3044,
"msgs": 300,
"events": null,
"pty_bytes": 34154,
"pty_writes": 21,
"rss_kb": 187632,
"pss_kb": 149832,
"private_dirty_kb": 128012,
"vmhwm_kb": 196580,
"utime_ticks": 65,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 4047,
"msgs": 300,
"events": null,
"pty_bytes": 34244,
"pty_writes": 23,
"rss_kb": 187636,
"pss_kb": 149878,
"private_dirty_kb": 128016,
"vmhwm_kb": 196580,
"utime_ticks": 65,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 5054,
"msgs": 300,
"events": null,
"pty_bytes": 34334,
"pty_writes": 25,
"rss_kb": 187644,
"pss_kb": 150010,
"private_dirty_kb": 128024,
"vmhwm_kb": 196580,
"utime_ticks": 65,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 6059,
"msgs": 300,
"events": null,
"pty_bytes": 34424,
"pty_writes": 27,
"rss_kb": 187652,
"pss_kb": 150018,
"private_dirty_kb": 128032,
"vmhwm_kb": 196580,
"utime_ticks": 65,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 7064,
"msgs": 300,
"events": null,
"pty_bytes": 34514,
"pty_writes": 29,
"rss_kb": 187652,
"pss_kb": 150018,
"private_dirty_kb": 128032,
"vmhwm_kb": 196580,
"utime_ticks": 65,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 8068,
"msgs": 300,
"events": null,
"pty_bytes": 34604,
"pty_writes": 31,
"rss_kb": 187656,
"pss_kb": 150022,
"private_dirty_kb": 128036,
"vmhwm_kb": 196580,
"utime_ticks": 66,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 9073,
"msgs": 300,
"events": null,
"pty_bytes": 34694,
"pty_writes": 33,
"rss_kb": 187656,
"pss_kb": 150022,
"private_dirty_kb": 128036,
"vmhwm_kb": 196580,
"utime_ticks": 66,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 10087,
"msgs": 300,
"events": null,
"pty_bytes": 34784,
"pty_writes": 35,
"rss_kb": 187656,
"pss_kb": 150022,
"private_dirty_kb": 128036,
"vmhwm_kb": 196580,
"utime_ticks": 66,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 11096,
"msgs": 300,
"events": null,
"pty_bytes": 34904,
"pty_writes": 37,
"rss_kb": 187664,
"pss_kb": 148140,
"private_dirty_kb": 128044,
"vmhwm_kb": 196580,
"utime_ticks": 66,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 12098,
"msgs": 300,
"events": null,
"pty_bytes": 35013,
"pty_writes": 39,
"rss_kb": 187664,
"pss_kb": 145076,
"private_dirty_kb": 128044,
"vmhwm_kb": 196580,
"utime_ticks": 66,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 13098,
"msgs": 300,
"events": null,
"pty_bytes": 35103,
"pty_writes": 41,
"rss_kb": 187664,
"pss_kb": 145045,
"private_dirty_kb": 128044,
"vmhwm_kb": 196580,
"utime_ticks": 66,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 14102,
"msgs": 300,
"events": null,
"pty_bytes": 35193,
"pty_writes": 43,
"rss_kb": 187664,
"pss_kb": 145044,
"private_dirty_kb": 128044,
"vmhwm_kb": 196580,
"utime_ticks": 67,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 15111,
"msgs": 300,
"events": null,
"pty_bytes": 35283,
"pty_writes": 45,
"rss_kb": 187664,
"pss_kb": 145044,
"private_dirty_kb": 128044,
"vmhwm_kb": 196580,
"utime_ticks": 67,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 16118,
"msgs": 300,
"events": null,
"pty_bytes": 35373,
"pty_writes": 47,
"rss_kb": 187680,
"pss_kb": 145060,
"private_dirty_kb": 128060,
"vmhwm_kb": 196580,
"utime_ticks": 67,
"stime_ticks": 7
},
{
"kind": "final",
"t_ms": 16674,
"msgs": 300,
"events": 1051,
"pty_bytes": 35463,
"pty_writes": 49,
"rss_kb": 187680,
"pss_kb": 145060,
"private_dirty_kb": 128060,
"vmhwm_kb": 196580,
"utime_ticks": 67,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 17125,
"msgs": 300,
"events": null,
"pty_bytes": 46138,
"pty_writes": 56,
"rss_kb": 175904,
"pss_kb": 133284,
"private_dirty_kb": 116284,
"vmhwm_kb": 196580,
"utime_ticks": 71,
"stime_ticks": 7
},
{
"kind": "periodic",
"t_ms": 18129,
"msgs": 300,
"events": null,
"pty_bytes": 49784,
"pty_writes": 59,
"rss_kb": 177964,
"pss_kb": 135344,
"private_dirty_kb": 118344,
"vmhwm_kb": 196580,
"utime_ticks": 72,
"stime_ticks": 7
}
],
"events": [
{
"kind": "rpc",
"method": "config.get",
"t_ms": 226
},
{
"kind": "rpc",
"method": "commands.catalog",
"t_ms": 226
},
{
"kind": "rpc",
"method": "setup.status",
"t_ms": 251
},
{
"kind": "rpc",
"method": "session.create",
"t_ms": 251
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 251
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 251
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 251
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 1756
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 3243
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 4751
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 5253
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 6258
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 7766
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 9248
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 10263
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 10767
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 12272
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 13749
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 15261
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 15261
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 16771
},
{
"kind": "rpc",
"method": "terminal.resize",
"t_ms": 16796
},
{
"kind": "rpc",
"method": "terminal.resize",
"t_ms": 17197
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 18255
}
],
"summary": {
"result": "completed",
"cap_hit": false,
"cap_hit_basis": null,
"at_messages": null,
"exit": {
"exitCode": 0,
"signal": 0,
"t": 18284
},
"stream_done": true,
"msgs_streamed": 300,
"events_streamed": 1051,
"pty_bytes_total": 50236,
"pty_data_callbacks": 64,
"first_byte_ms": 110,
"session_create_ms": 251,
"stream_start_ms": 1455,
"vmhwm_kb": 196580,
"cg_peak": null,
"drain_max_loop_lag_ms": 3,
"drain_lag_violations": 0,
"drain_ok": true,
"digest": "0f62155086e89590f62e059cc7bcf0851b6c88dac942a8f2303ac905481dea48"
},
"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 │ 16s │ ✓ 15s │ 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 │ 16s │ ✓ 15s │ voice off ─ …es-agent (…i-native-engine)❯768"
}

View file

@ -0,0 +1,225 @@
{
"meta": {
"cell": "gate",
"ui": "opentui",
"config": "otui-capped",
"mode": "digest",
"rep": 0,
"run_id": "mqaf8cjn-3smp",
"utc": "2026-06-12T04:23:16.355Z",
"sha": "f0ec24ad5",
"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": "/home/daimon/github/worktrees/hermes-agent/quiet-quill/hermes-agent/bench/.cache/fixture-300.ndjson",
"msgs": 300,
"sha256": "ac81e975c299da7d50cfda7fc0fee33c356bd31a4e17a7f0f8e49905e205051a"
},
"sample_every": 100,
"mode_params": {},
"ui_pid": 168251,
"gw_pid": 168266,
"cgroup": null,
"load_avg_at_start": [
0.31,
0.92,
1.42
],
"instrumented": false
},
"samples": [
{
"kind": "periodic",
"t_ms": 25,
"msgs": null,
"events": null,
"pty_bytes": 0,
"pty_writes": 0,
"rss_kb": 54688,
"pss_kb": 25883,
"private_dirty_kb": 17496,
"vmhwm_kb": 54768,
"utime_ticks": 1,
"stime_ticks": 0
},
{
"kind": "periodic",
"t_ms": 1030,
"msgs": null,
"events": null,
"pty_bytes": 28909,
"pty_writes": 12,
"rss_kb": 104648,
"pss_kb": 61159,
"private_dirty_kb": 48340,
"vmhwm_kb": 107112,
"utime_ticks": 19,
"stime_ticks": 2
},
{
"kind": "boundary",
"t_ms": 1382,
"msgs": 100,
"events": 355,
"pty_bytes": 31409,
"pty_writes": 14,
"rss_kb": 111376,
"pss_kb": 67773,
"private_dirty_kb": 54944,
"vmhwm_kb": 111396,
"utime_ticks": 22,
"stime_ticks": 3
},
{
"kind": "boundary",
"t_ms": 1383,
"msgs": 200,
"events": 738,
"pty_bytes": 31409,
"pty_writes": 14,
"rss_kb": 111648,
"pss_kb": 68045,
"private_dirty_kb": 55216,
"vmhwm_kb": 111768,
"utime_ticks": 22,
"stime_ticks": 3
},
{
"kind": "boundary",
"t_ms": 1383,
"msgs": 300,
"events": 1051,
"pty_bytes": 31409,
"pty_writes": 14,
"rss_kb": 111880,
"pss_kb": 68277,
"private_dirty_kb": 55448,
"vmhwm_kb": 111748,
"utime_ticks": 22,
"stime_ticks": 3
},
{
"kind": "done",
"t_ms": 1384,
"msgs": 300,
"events": 1051,
"pty_bytes": 31409,
"pty_writes": 14,
"rss_kb": 111748,
"pss_kb": 68145,
"private_dirty_kb": 55316,
"vmhwm_kb": 111752,
"utime_ticks": 22,
"stime_ticks": 3
},
{
"kind": "periodic",
"t_ms": 2036,
"msgs": 300,
"events": null,
"pty_bytes": 35558,
"pty_writes": 17,
"rss_kb": 300388,
"pss_kb": 254649,
"private_dirty_kb": 240344,
"vmhwm_kb": 301568,
"utime_ticks": 109,
"stime_ticks": 11
},
{
"kind": "final",
"t_ms": 2867,
"msgs": 300,
"events": 1051,
"pty_bytes": 35630,
"pty_writes": 18,
"rss_kb": 300420,
"pss_kb": 254681,
"private_dirty_kb": 240376,
"vmhwm_kb": 301568,
"utime_ticks": 109,
"stime_ticks": 11
},
{
"kind": "periodic",
"t_ms": 3045,
"msgs": 300,
"events": null,
"pty_bytes": 46541,
"pty_writes": 22,
"rss_kb": 300480,
"pss_kb": 254741,
"private_dirty_kb": 240436,
"vmhwm_kb": 301568,
"utime_ticks": 111,
"stime_ticks": 11
},
{
"kind": "periodic",
"t_ms": 4050,
"msgs": 300,
"events": null,
"pty_bytes": 57863,
"pty_writes": 27,
"rss_kb": 300528,
"pss_kb": 254789,
"private_dirty_kb": 240484,
"vmhwm_kb": 301568,
"utime_ticks": 112,
"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": 4721
},
"stream_done": true,
"msgs_streamed": 300,
"events_streamed": 1051,
"pty_bytes_total": 62674,
"pty_data_callbacks": 32,
"first_byte_ms": 130,
"session_create_ms": 175,
"stream_start_ms": 1380,
"vmhwm_kb": 301568,
"cg_peak": null,
"drain_max_loop_lag_ms": 3,
"drain_lag_violations": 0,
"drain_ok": true,
"digest": "4c4f97e930ebbaa2f174eaf5410c4d100689c671220623ad906530d885eef948"
},
"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 │ …/quiet-quill/hermes-agent ❯ Type your message"
}

View file

@ -0,0 +1,225 @@
{
"meta": {
"cell": "gate",
"ui": "opentui",
"config": "otui-capped",
"mode": "digest",
"rep": 1,
"run_id": "mqaf8nws-3ryc",
"utc": "2026-06-12T04:23:31.084Z",
"sha": "f0ec24ad5",
"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": "/home/daimon/github/worktrees/hermes-agent/quiet-quill/hermes-agent/bench/.cache/fixture-300.ndjson",
"msgs": 300,
"sha256": "ac81e975c299da7d50cfda7fc0fee33c356bd31a4e17a7f0f8e49905e205051a"
},
"sample_every": 100,
"mode_params": {},
"ui_pid": 168675,
"gw_pid": 168684,
"cgroup": null,
"load_avg_at_start": [
0.51,
0.93,
1.42
],
"instrumented": false
},
"samples": [
{
"kind": "periodic",
"t_ms": 26,
"msgs": null,
"events": null,
"pty_bytes": 0,
"pty_writes": 0,
"rss_kb": 54724,
"pss_kb": 25927,
"private_dirty_kb": 17484,
"vmhwm_kb": 54756,
"utime_ticks": 1,
"stime_ticks": 0
},
{
"kind": "periodic",
"t_ms": 1033,
"msgs": null,
"events": null,
"pty_bytes": 28909,
"pty_writes": 13,
"rss_kb": 104792,
"pss_kb": 61265,
"private_dirty_kb": 48340,
"vmhwm_kb": 107580,
"utime_ticks": 17,
"stime_ticks": 2
},
{
"kind": "boundary",
"t_ms": 1386,
"msgs": 100,
"events": 355,
"pty_bytes": 31409,
"pty_writes": 15,
"rss_kb": 112180,
"pss_kb": 68538,
"private_dirty_kb": 55604,
"vmhwm_kb": 112212,
"utime_ticks": 22,
"stime_ticks": 3
},
{
"kind": "boundary",
"t_ms": 1387,
"msgs": 200,
"events": 738,
"pty_bytes": 31409,
"pty_writes": 15,
"rss_kb": 112424,
"pss_kb": 68782,
"private_dirty_kb": 55848,
"vmhwm_kb": 112444,
"utime_ticks": 23,
"stime_ticks": 3
},
{
"kind": "boundary",
"t_ms": 1388,
"msgs": 300,
"events": 1051,
"pty_bytes": 31409,
"pty_writes": 15,
"rss_kb": 112448,
"pss_kb": 68806,
"private_dirty_kb": 55872,
"vmhwm_kb": 112448,
"utime_ticks": 23,
"stime_ticks": 3
},
{
"kind": "done",
"t_ms": 1388,
"msgs": 300,
"events": 1051,
"pty_bytes": 31409,
"pty_writes": 15,
"rss_kb": 112448,
"pss_kb": 68806,
"private_dirty_kb": 55872,
"vmhwm_kb": 112448,
"utime_ticks": 23,
"stime_ticks": 3
},
{
"kind": "periodic",
"t_ms": 2041,
"msgs": 300,
"events": null,
"pty_bytes": 35558,
"pty_writes": 18,
"rss_kb": 274976,
"pss_kb": 229157,
"private_dirty_kb": 214852,
"vmhwm_kb": 324708,
"utime_ticks": 106,
"stime_ticks": 11
},
{
"kind": "final",
"t_ms": 2861,
"msgs": 300,
"events": 1051,
"pty_bytes": 35630,
"pty_writes": 19,
"rss_kb": 275008,
"pss_kb": 229189,
"private_dirty_kb": 214884,
"vmhwm_kb": 324708,
"utime_ticks": 107,
"stime_ticks": 11
},
{
"kind": "periodic",
"t_ms": 3049,
"msgs": 300,
"events": null,
"pty_bytes": 46541,
"pty_writes": 23,
"rss_kb": 275068,
"pss_kb": 229249,
"private_dirty_kb": 214944,
"vmhwm_kb": 324708,
"utime_ticks": 108,
"stime_ticks": 11
},
{
"kind": "periodic",
"t_ms": 4055,
"msgs": 300,
"events": null,
"pty_bytes": 57863,
"pty_writes": 28,
"rss_kb": 275112,
"pss_kb": 229288,
"private_dirty_kb": 214988,
"vmhwm_kb": 324708,
"utime_ticks": 109,
"stime_ticks": 11
}
],
"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": 4707
},
"stream_done": true,
"msgs_streamed": 300,
"events_streamed": 1051,
"pty_bytes_total": 62674,
"pty_data_callbacks": 33,
"first_byte_ms": 128,
"session_create_ms": 176,
"stream_start_ms": 1385,
"vmhwm_kb": 324708,
"cg_peak": null,
"drain_max_loop_lag_ms": 4,
"drain_lag_violations": 0,
"drain_ok": true,
"digest": "4c4f97e930ebbaa2f174eaf5410c4d100689c671220623ad906530d885eef948"
},
"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 │ …/quiet-quill/hermes-agent ❯ Type your message"
}

View file

@ -0,0 +1,854 @@
{
"meta": {
"cell": "mem2000",
"ui": "ink",
"config": "ink",
"mode": "mem",
"rep": 0,
"run_id": "mqafa3wx-7qr8",
"utc": "2026-06-12T04:24:38.481Z",
"sha": "f0ec24ad5",
"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": null,
"fixture": {
"path": "/home/daimon/github/worktrees/hermes-agent/quiet-quill/hermes-agent/bench/.cache/fixture-2000.ndjson",
"msgs": 2000,
"sha256": "645b9ee4fdaccee45172fcec8af4a0b01a5f9aeca56ce45d8e84c4276472f99c"
},
"sample_every": 100,
"mode_params": {},
"ui_pid": 170578,
"gw_pid": 170601,
"cgroup": "/sys/fs/cgroup/user.slice/user-1001.slice/session-7349.scope",
"load_avg_at_start": [
0.45,
0.83,
1.34
],
"instrumented": false
},
"samples": [
{
"kind": "periodic",
"t_ms": 26,
"msgs": null,
"events": null,
"pty_bytes": 0,
"pty_writes": 0,
"rss_kb": 29152,
"pss_kb": 10292,
"private_dirty_kb": 4420,
"vmhwm_kb": 29152,
"utime_ticks": 0,
"stime_ticks": 0,
"cg_current": 5940740096,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 1031,
"msgs": null,
"events": null,
"pty_bytes": 6190,
"pty_writes": 13,
"rss_kb": 111012,
"pss_kb": 67797,
"private_dirty_kb": 52260,
"vmhwm_kb": 111012,
"utime_ticks": 24,
"stime_ticks": 4,
"cg_current": 5941399552,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 1760,
"msgs": 100,
"events": 355,
"pty_bytes": 6411,
"pty_writes": 16,
"rss_kb": 114928,
"pss_kb": 71604,
"private_dirty_kb": 55388,
"vmhwm_kb": 114936,
"utime_ticks": 26,
"stime_ticks": 4,
"cg_current": 5940625408,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 1835,
"msgs": 200,
"events": 738,
"pty_bytes": 13769,
"pty_writes": 20,
"rss_kb": 140880,
"pss_kb": 97258,
"private_dirty_kb": 80960,
"vmhwm_kb": 140888,
"utime_ticks": 39,
"stime_ticks": 5,
"cg_current": 5940695040,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 1860,
"msgs": 300,
"events": 1051,
"pty_bytes": 17619,
"pty_writes": 21,
"rss_kb": 153592,
"pss_kb": 109970,
"private_dirty_kb": 93672,
"vmhwm_kb": 153620,
"utime_ticks": 44,
"stime_ticks": 6,
"cg_current": 5940695040,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 1937,
"msgs": 400,
"events": 1432,
"pty_bytes": 26009,
"pty_writes": 25,
"rss_kb": 172120,
"pss_kb": 128498,
"private_dirty_kb": 112200,
"vmhwm_kb": 178392,
"utime_ticks": 59,
"stime_ticks": 7,
"cg_current": 5940998144,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 1963,
"msgs": 501,
"events": 1792,
"pty_bytes": 32671,
"pty_writes": 28,
"rss_kb": 173420,
"pss_kb": 129798,
"private_dirty_kb": 113500,
"vmhwm_kb": 178392,
"utime_ticks": 62,
"stime_ticks": 7,
"cg_current": 5940809728,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2011,
"msgs": 601,
"events": 2154,
"pty_bytes": 43052,
"pty_writes": 31,
"rss_kb": 179416,
"pss_kb": 135784,
"private_dirty_kb": 119496,
"vmhwm_kb": 179440,
"utime_ticks": 71,
"stime_ticks": 7,
"cg_current": 5940842496,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2036,
"msgs": 701,
"events": 2496,
"pty_bytes": 46699,
"pty_writes": 32,
"rss_kb": 182248,
"pss_kb": 138616,
"private_dirty_kb": 122328,
"vmhwm_kb": 182248,
"utime_ticks": 74,
"stime_ticks": 7,
"cg_current": 5940842496,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 2038,
"msgs": null,
"events": null,
"pty_bytes": 46699,
"pty_writes": 32,
"rss_kb": 182284,
"pss_kb": 138652,
"private_dirty_kb": 122364,
"vmhwm_kb": 182320,
"utime_ticks": 75,
"stime_ticks": 7,
"cg_current": 5940842496,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2061,
"msgs": 801,
"events": 2857,
"pty_bytes": 50621,
"pty_writes": 33,
"rss_kb": 184112,
"pss_kb": 140480,
"private_dirty_kb": 124192,
"vmhwm_kb": 184116,
"utime_ticks": 77,
"stime_ticks": 7,
"cg_current": 5940842496,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2112,
"msgs": 901,
"events": 3245,
"pty_bytes": 58620,
"pty_writes": 35,
"rss_kb": 193376,
"pss_kb": 149741,
"private_dirty_kb": 133456,
"vmhwm_kb": 193384,
"utime_ticks": 85,
"stime_ticks": 8,
"cg_current": 5940846592,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2163,
"msgs": 1001,
"events": 3588,
"pty_bytes": 65835,
"pty_writes": 37,
"rss_kb": 197156,
"pss_kb": 153521,
"private_dirty_kb": 137236,
"vmhwm_kb": 197164,
"utime_ticks": 91,
"stime_ticks": 8,
"cg_current": 5940846592,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2188,
"msgs": 1101,
"events": 3928,
"pty_bytes": 73273,
"pty_writes": 39,
"rss_kb": 200276,
"pss_kb": 156641,
"private_dirty_kb": 140356,
"vmhwm_kb": 200276,
"utime_ticks": 95,
"stime_ticks": 8,
"cg_current": 5940842496,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2213,
"msgs": 1201,
"events": 4298,
"pty_bytes": 77046,
"pty_writes": 40,
"rss_kb": 203176,
"pss_kb": 159541,
"private_dirty_kb": 143256,
"vmhwm_kb": 203180,
"utime_ticks": 99,
"stime_ticks": 8,
"cg_current": 5940842496,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2262,
"msgs": 1300,
"events": 4659,
"pty_bytes": 88185,
"pty_writes": 43,
"rss_kb": 210112,
"pss_kb": 166477,
"private_dirty_kb": 150192,
"vmhwm_kb": 210124,
"utime_ticks": 107,
"stime_ticks": 9,
"cg_current": 5940842496,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2263,
"msgs": 1400,
"events": 5011,
"pty_bytes": 88185,
"pty_writes": 43,
"rss_kb": 210136,
"pss_kb": 166501,
"private_dirty_kb": 150216,
"vmhwm_kb": 210136,
"utime_ticks": 107,
"stime_ticks": 9,
"cg_current": 5940842496,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2438,
"msgs": 1500,
"events": 5384,
"pty_bytes": 95749,
"pty_writes": 45,
"rss_kb": 218668,
"pss_kb": 175033,
"private_dirty_kb": 158748,
"vmhwm_kb": 218672,
"utime_ticks": 127,
"stime_ticks": 9,
"cg_current": 5940731904,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2587,
"msgs": 1600,
"events": 5730,
"pty_bytes": 103499,
"pty_writes": 48,
"rss_kb": 225504,
"pss_kb": 181869,
"private_dirty_kb": 165584,
"vmhwm_kb": 225644,
"utime_ticks": 144,
"stime_ticks": 9,
"cg_current": 5940396032,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2639,
"msgs": 1700,
"events": 6100,
"pty_bytes": 111074,
"pty_writes": 51,
"rss_kb": 232268,
"pss_kb": 188633,
"private_dirty_kb": 172348,
"vmhwm_kb": 232276,
"utime_ticks": 151,
"stime_ticks": 9,
"cg_current": 5940883456,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2665,
"msgs": 1800,
"events": 6455,
"pty_bytes": 114381,
"pty_writes": 54,
"rss_kb": 235400,
"pss_kb": 191765,
"private_dirty_kb": 175480,
"vmhwm_kb": 235400,
"utime_ticks": 155,
"stime_ticks": 10,
"cg_current": 5940625408,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2715,
"msgs": 1900,
"events": 6838,
"pty_bytes": 117335,
"pty_writes": 55,
"rss_kb": 246420,
"pss_kb": 202785,
"private_dirty_kb": 186500,
"vmhwm_kb": 246440,
"utime_ticks": 163,
"stime_ticks": 10,
"cg_current": 5940895744,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2738,
"msgs": 2000,
"events": 7151,
"pty_bytes": 124683,
"pty_writes": 57,
"rss_kb": 244808,
"pss_kb": 201173,
"private_dirty_kb": 184888,
"vmhwm_kb": 249288,
"utime_ticks": 167,
"stime_ticks": 10,
"cg_current": 5941153792,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "done",
"t_ms": 2740,
"msgs": 2000,
"events": 7151,
"pty_bytes": 124683,
"pty_writes": 57,
"rss_kb": 244808,
"pss_kb": 201173,
"private_dirty_kb": 184888,
"vmhwm_kb": 249288,
"utime_ticks": 167,
"stime_ticks": 10,
"cg_current": 5941153792,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 3040,
"msgs": 2000,
"events": null,
"pty_bytes": 132349,
"pty_writes": 60,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 172,
"stime_ticks": 10,
"cg_current": 5941141504,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 4048,
"msgs": 2000,
"events": null,
"pty_bytes": 132439,
"pty_writes": 62,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 172,
"stime_ticks": 10,
"cg_current": 5942734848,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 5048,
"msgs": 2000,
"events": null,
"pty_bytes": 132529,
"pty_writes": 64,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 172,
"stime_ticks": 10,
"cg_current": 5952634880,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 6052,
"msgs": 2000,
"events": null,
"pty_bytes": 132619,
"pty_writes": 66,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 172,
"stime_ticks": 10,
"cg_current": 5955338240,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 7060,
"msgs": 2000,
"events": null,
"pty_bytes": 132709,
"pty_writes": 68,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 172,
"stime_ticks": 10,
"cg_current": 5956476928,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 8072,
"msgs": 2000,
"events": null,
"pty_bytes": 132799,
"pty_writes": 70,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 173,
"stime_ticks": 10,
"cg_current": 5959532544,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 9078,
"msgs": 2000,
"events": null,
"pty_bytes": 132889,
"pty_writes": 72,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 173,
"stime_ticks": 10,
"cg_current": 5958893568,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 10091,
"msgs": 2000,
"events": null,
"pty_bytes": 132979,
"pty_writes": 74,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 173,
"stime_ticks": 10,
"cg_current": 5964316672,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 11107,
"msgs": 2000,
"events": null,
"pty_bytes": 133099,
"pty_writes": 76,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 173,
"stime_ticks": 10,
"cg_current": 5973635072,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 12113,
"msgs": 2000,
"events": null,
"pty_bytes": 133189,
"pty_writes": 78,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 173,
"stime_ticks": 10,
"cg_current": 5948555264,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 13120,
"msgs": 2000,
"events": null,
"pty_bytes": 133298,
"pty_writes": 80,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 174,
"stime_ticks": 10,
"cg_current": 5949779968,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 14133,
"msgs": 2000,
"events": null,
"pty_bytes": 133388,
"pty_writes": 82,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 174,
"stime_ticks": 10,
"cg_current": 5949759488,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 15144,
"msgs": 2000,
"events": null,
"pty_bytes": 133478,
"pty_writes": 84,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 174,
"stime_ticks": 10,
"cg_current": 5955108864,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 16156,
"msgs": 2000,
"events": null,
"pty_bytes": 133568,
"pty_writes": 86,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 174,
"stime_ticks": 10,
"cg_current": 5958033408,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 17164,
"msgs": 2000,
"events": null,
"pty_bytes": 133658,
"pty_writes": 88,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 174,
"stime_ticks": 10,
"cg_current": 5961461760,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "final",
"t_ms": 17788,
"msgs": 2000,
"events": 7151,
"pty_bytes": 133748,
"pty_writes": 90,
"rss_kb": 246068,
"pss_kb": 202433,
"private_dirty_kb": 186148,
"vmhwm_kb": 249288,
"utime_ticks": 175,
"stime_ticks": 10,
"cg_current": 5962797056,
"cg_peak": 7893282816,
"cg_oom_kill": 0
}
],
"events": [
{
"kind": "rpc",
"method": "config.get",
"t_ms": 252
},
{
"kind": "rpc",
"method": "commands.catalog",
"t_ms": 252
},
{
"kind": "rpc",
"method": "setup.status",
"t_ms": 252
},
{
"kind": "rpc",
"method": "session.create",
"t_ms": 252
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 252
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 252
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 252
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 1758
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 3265
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 4746
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 5247
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 6251
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 7763
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 9254
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 10265
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 10746
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 12262
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 13753
},
{
"kind": "rpc",
"method": "config.get",
"t_ms": 15242
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 15268
},
{
"kind": "rpc",
"method": "session.active_list",
"t_ms": 16760
}
],
"summary": {
"result": "completed",
"cap_hit": false,
"cap_hit_basis": null,
"at_messages": null,
"exit": {
"exitCode": 0,
"signal": 0,
"t": 17801
},
"stream_done": true,
"msgs_streamed": 2000,
"events_streamed": 7151,
"pty_bytes_total": 134155,
"pty_data_callbacks": 98,
"first_byte_ms": 114,
"session_create_ms": 252,
"stream_start_ms": 1758,
"vmhwm_kb": 249288,
"cg_peak": 7893282816,
"drain_max_loop_lag_ms": 5,
"drain_lag_violations": 0,
"drain_ok": true,
"digest": null
}
}

View file

@ -0,0 +1,611 @@
{
"meta": {
"cell": "mem2000",
"ui": "opentui",
"config": "otui-capped",
"mode": "mem",
"rep": 0,
"run_id": "mqaf9blb-m7l3",
"utc": "2026-06-12T04:24:01.775Z",
"sha": "f0ec24ad5",
"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/quiet-quill/hermes-agent/bench/.cache/fixture-2000.ndjson",
"msgs": 2000,
"sha256": "645b9ee4fdaccee45172fcec8af4a0b01a5f9aeca56ce45d8e84c4276472f99c"
},
"sample_every": 100,
"mode_params": {},
"ui_pid": 169517,
"gw_pid": 169526,
"cgroup": "/sys/fs/cgroup/user.slice/user-1001.slice/session-7349.scope",
"load_avg_at_start": [
0.73,
0.93,
1.39
],
"instrumented": false
},
"samples": [
{
"kind": "periodic",
"t_ms": 28,
"msgs": null,
"events": null,
"pty_bytes": 0,
"pty_writes": 0,
"rss_kb": 31864,
"pss_kb": 12337,
"private_dirty_kb": 6144,
"vmhwm_kb": 31744,
"utime_ticks": 1,
"stime_ticks": 0,
"cg_current": 6002446336,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 1037,
"msgs": null,
"events": null,
"pty_bytes": 28909,
"pty_writes": 10,
"rss_kb": 104788,
"pss_kb": 60315,
"private_dirty_kb": 48340,
"vmhwm_kb": 107352,
"utime_ticks": 18,
"stime_ticks": 1,
"cg_current": 5998682112,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 1695,
"msgs": 100,
"events": 355,
"pty_bytes": 31409,
"pty_writes": 12,
"rss_kb": 112868,
"pss_kb": 68274,
"private_dirty_kb": 56296,
"vmhwm_kb": 113072,
"utime_ticks": 22,
"stime_ticks": 1,
"cg_current": 5998612480,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 1696,
"msgs": 200,
"events": 738,
"pty_bytes": 31409,
"pty_writes": 12,
"rss_kb": 113124,
"pss_kb": 68530,
"private_dirty_kb": 56552,
"vmhwm_kb": 112992,
"utime_ticks": 22,
"stime_ticks": 1,
"cg_current": 5998616576,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 1697,
"msgs": 300,
"events": 1051,
"pty_bytes": 31409,
"pty_writes": 12,
"rss_kb": 113120,
"pss_kb": 68526,
"private_dirty_kb": 56548,
"vmhwm_kb": 113188,
"utime_ticks": 22,
"stime_ticks": 1,
"cg_current": 5998616576,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 2046,
"msgs": null,
"events": null,
"pty_bytes": 31409,
"pty_writes": 12,
"rss_kb": 227468,
"pss_kb": 180839,
"private_dirty_kb": 167776,
"vmhwm_kb": 227468,
"utime_ticks": 77,
"stime_ticks": 7,
"cg_current": 5999181824,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2097,
"msgs": 400,
"events": 1432,
"pty_bytes": 32463,
"pty_writes": 13,
"rss_kb": 229936,
"pss_kb": 183307,
"private_dirty_kb": 170244,
"vmhwm_kb": 229936,
"utime_ticks": 82,
"stime_ticks": 7,
"cg_current": 5998927872,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2148,
"msgs": 501,
"events": 1792,
"pty_bytes": 36002,
"pty_writes": 14,
"rss_kb": 231552,
"pss_kb": 184810,
"private_dirty_kb": 171680,
"vmhwm_kb": 231560,
"utime_ticks": 88,
"stime_ticks": 8,
"cg_current": 5998673920,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2325,
"msgs": 601,
"events": 2154,
"pty_bytes": 38789,
"pty_writes": 16,
"rss_kb": 268004,
"pss_kb": 221200,
"private_dirty_kb": 208008,
"vmhwm_kb": 268056,
"utime_ticks": 117,
"stime_ticks": 8,
"cg_current": 5998878720,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2573,
"msgs": 701,
"events": 2496,
"pty_bytes": 41517,
"pty_writes": 18,
"rss_kb": 307192,
"pss_kb": 260272,
"private_dirty_kb": 247072,
"vmhwm_kb": 342248,
"utime_ticks": 165,
"stime_ticks": 11,
"cg_current": 5998968832,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2805,
"msgs": 801,
"events": 2857,
"pty_bytes": 42840,
"pty_writes": 19,
"rss_kb": 337800,
"pss_kb": 290880,
"private_dirty_kb": 277680,
"vmhwm_kb": 342248,
"utime_ticks": 192,
"stime_ticks": 12,
"cg_current": 5998862336,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 3050,
"msgs": null,
"events": null,
"pty_bytes": 44316,
"pty_writes": 20,
"rss_kb": 366384,
"pss_kb": 319464,
"private_dirty_kb": 306264,
"vmhwm_kb": 366472,
"utime_ticks": 219,
"stime_ticks": 14,
"cg_current": 5998555136,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3102,
"msgs": 901,
"events": 3245,
"pty_bytes": 47642,
"pty_writes": 22,
"rss_kb": 368804,
"pss_kb": 321880,
"private_dirty_kb": 308684,
"vmhwm_kb": 368840,
"utime_ticks": 225,
"stime_ticks": 14,
"cg_current": 5998551040,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3104,
"msgs": 1001,
"events": 3588,
"pty_bytes": 47642,
"pty_writes": 22,
"rss_kb": 368896,
"pss_kb": 321972,
"private_dirty_kb": 308776,
"vmhwm_kb": 368916,
"utime_ticks": 226,
"stime_ticks": 14,
"cg_current": 5998551040,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3106,
"msgs": 1101,
"events": 3928,
"pty_bytes": 47642,
"pty_writes": 22,
"rss_kb": 368928,
"pss_kb": 322004,
"private_dirty_kb": 308808,
"vmhwm_kb": 368948,
"utime_ticks": 226,
"stime_ticks": 14,
"cg_current": 5998551040,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3107,
"msgs": 1201,
"events": 4298,
"pty_bytes": 47642,
"pty_writes": 22,
"rss_kb": 368968,
"pss_kb": 322044,
"private_dirty_kb": 308848,
"vmhwm_kb": 368988,
"utime_ticks": 226,
"stime_ticks": 14,
"cg_current": 5998551040,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3125,
"msgs": 1300,
"events": 4659,
"pty_bytes": 47642,
"pty_writes": 22,
"rss_kb": 371680,
"pss_kb": 324756,
"private_dirty_kb": 311560,
"vmhwm_kb": 371776,
"utime_ticks": 228,
"stime_ticks": 14,
"cg_current": 5999067136,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3127,
"msgs": 1400,
"events": 5011,
"pty_bytes": 47642,
"pty_writes": 22,
"rss_kb": 371916,
"pss_kb": 324992,
"private_dirty_kb": 311796,
"vmhwm_kb": 372020,
"utime_ticks": 228,
"stime_ticks": 14,
"cg_current": 5999067136,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3128,
"msgs": 1500,
"events": 5384,
"pty_bytes": 47642,
"pty_writes": 22,
"rss_kb": 372140,
"pss_kb": 325216,
"private_dirty_kb": 312020,
"vmhwm_kb": 372188,
"utime_ticks": 228,
"stime_ticks": 14,
"cg_current": 5999067136,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 4032,
"msgs": 1600,
"events": 5730,
"pty_bytes": 50803,
"pty_writes": 24,
"rss_kb": 535276,
"pss_kb": 488352,
"private_dirty_kb": 475156,
"vmhwm_kb": 535284,
"utime_ticks": 350,
"stime_ticks": 20,
"cg_current": 5998948352,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 4059,
"msgs": null,
"events": null,
"pty_bytes": 50803,
"pty_writes": 24,
"rss_kb": 540160,
"pss_kb": 493236,
"private_dirty_kb": 480040,
"vmhwm_kb": 540220,
"utime_ticks": 353,
"stime_ticks": 21,
"cg_current": 5998952448,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 4612,
"msgs": 1700,
"events": 6100,
"pty_bytes": 54799,
"pty_writes": 26,
"rss_kb": 564292,
"pss_kb": 517368,
"private_dirty_kb": 504172,
"vmhwm_kb": 564368,
"utime_ticks": 410,
"stime_ticks": 22,
"cg_current": 5999460352,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 5011,
"msgs": 1800,
"events": 6455,
"pty_bytes": 56349,
"pty_writes": 27,
"rss_kb": 591676,
"pss_kb": 544752,
"private_dirty_kb": 531556,
"vmhwm_kb": 591676,
"utime_ticks": 453,
"stime_ticks": 24,
"cg_current": 5982150656,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 5061,
"msgs": null,
"events": null,
"pty_bytes": 56349,
"pty_writes": 27,
"rss_kb": 591676,
"pss_kb": 544752,
"private_dirty_kb": 531556,
"vmhwm_kb": 591676,
"utime_ticks": 458,
"stime_ticks": 24,
"cg_current": 5982121984,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 5638,
"msgs": 1900,
"events": 6838,
"pty_bytes": 60037,
"pty_writes": 29,
"rss_kb": 621532,
"pss_kb": 574608,
"private_dirty_kb": 561412,
"vmhwm_kb": 621532,
"utime_ticks": 518,
"stime_ticks": 25,
"cg_current": 5983272960,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 6066,
"msgs": null,
"events": null,
"pty_bytes": 61205,
"pty_writes": 30,
"rss_kb": 654116,
"pss_kb": 607192,
"private_dirty_kb": 593996,
"vmhwm_kb": 654116,
"utime_ticks": 562,
"stime_ticks": 28,
"cg_current": 5983830016,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 6317,
"msgs": 2000,
"events": 7151,
"pty_bytes": 62172,
"pty_writes": 31,
"rss_kb": 656244,
"pss_kb": 609320,
"private_dirty_kb": 596124,
"vmhwm_kb": 656244,
"utime_ticks": 588,
"stime_ticks": 28,
"cg_current": 5982822400,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "done",
"t_ms": 6320,
"msgs": 2000,
"events": 7151,
"pty_bytes": 62172,
"pty_writes": 31,
"rss_kb": 656244,
"pss_kb": 609320,
"private_dirty_kb": 596124,
"vmhwm_kb": 656244,
"utime_ticks": 588,
"stime_ticks": 28,
"cg_current": 5982822400,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 7067,
"msgs": 2000,
"events": null,
"pty_bytes": 66708,
"pty_writes": 35,
"rss_kb": 686948,
"pss_kb": 640024,
"private_dirty_kb": 626828,
"vmhwm_kb": 686948,
"utime_ticks": 662,
"stime_ticks": 30,
"cg_current": 5959647232,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 8074,
"msgs": 2000,
"events": null,
"pty_bytes": 66780,
"pty_writes": 36,
"rss_kb": 686956,
"pss_kb": 640032,
"private_dirty_kb": 626836,
"vmhwm_kb": 686956,
"utime_ticks": 663,
"stime_ticks": 30,
"cg_current": 5944725504,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "final",
"t_ms": 8123,
"msgs": 2000,
"events": 7151,
"pty_bytes": 66780,
"pty_writes": 36,
"rss_kb": 686956,
"pss_kb": 640032,
"private_dirty_kb": 626836,
"vmhwm_kb": 686956,
"utime_ticks": 663,
"stime_ticks": 30,
"cg_current": 5945589760,
"cg_peak": 7893282816,
"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": 202
}
],
"summary": {
"result": "completed",
"cap_hit": false,
"cap_hit_basis": null,
"at_messages": null,
"exit": {
"exitCode": 0,
"signal": 0,
"t": 9039
},
"stream_done": true,
"msgs_streamed": 2000,
"events_streamed": 7151,
"pty_bytes_total": 71875,
"pty_data_callbacks": 40,
"first_byte_ms": 145,
"session_create_ms": 177,
"stream_start_ms": 1694,
"vmhwm_kb": 686956,
"cg_peak": 7893282816,
"drain_max_loop_lag_ms": 10,
"drain_lag_violations": 0,
"drain_ok": true,
"digest": null
}
}

View file

@ -0,0 +1,594 @@
{
"meta": {
"cell": "mem2000",
"ui": "opentui",
"config": "otui-uncapped",
"mode": "mem",
"rep": 0,
"run_id": "mqaf9qba-ewsf",
"utc": "2026-06-12T04:24:20.854Z",
"sha": "f0ec24ad5",
"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/quiet-quill/hermes-agent/bench/.cache/fixture-2000.ndjson",
"msgs": 2000,
"sha256": "645b9ee4fdaccee45172fcec8af4a0b01a5f9aeca56ce45d8e84c4276472f99c"
},
"sample_every": 100,
"mode_params": {},
"ui_pid": 170071,
"gw_pid": 170080,
"cgroup": "/sys/fs/cgroup/user.slice/user-1001.slice/session-7349.scope",
"load_avg_at_start": [
0.65,
0.9,
1.38
],
"instrumented": false
},
"samples": [
{
"kind": "periodic",
"t_ms": 26,
"msgs": null,
"events": null,
"pty_bytes": 0,
"pty_writes": 0,
"rss_kb": 35020,
"pss_kb": 14276,
"private_dirty_kb": 7704,
"vmhwm_kb": 35152,
"utime_ticks": 0,
"stime_ticks": 0,
"cg_current": 5950107648,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 1034,
"msgs": null,
"events": null,
"pty_bytes": 28909,
"pty_writes": 12,
"rss_kb": 104312,
"pss_kb": 59828,
"private_dirty_kb": 47852,
"vmhwm_kb": 106556,
"utime_ticks": 18,
"stime_ticks": 3,
"cg_current": 5950238720,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 1712,
"msgs": 100,
"events": 355,
"pty_bytes": 31409,
"pty_writes": 14,
"rss_kb": 111620,
"pss_kb": 67014,
"private_dirty_kb": 55036,
"vmhwm_kb": 111736,
"utime_ticks": 23,
"stime_ticks": 3,
"cg_current": 5950959616,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 1713,
"msgs": 200,
"events": 738,
"pty_bytes": 31409,
"pty_writes": 14,
"rss_kb": 111884,
"pss_kb": 67278,
"private_dirty_kb": 55300,
"vmhwm_kb": 111980,
"utime_ticks": 23,
"stime_ticks": 3,
"cg_current": 5950959616,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 1714,
"msgs": 300,
"events": 1051,
"pty_bytes": 31409,
"pty_writes": 14,
"rss_kb": 112096,
"pss_kb": 67490,
"private_dirty_kb": 55512,
"vmhwm_kb": 112180,
"utime_ticks": 23,
"stime_ticks": 3,
"cg_current": 5950734336,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 1991,
"msgs": 400,
"events": 1432,
"pty_bytes": 32746,
"pty_writes": 15,
"rss_kb": 198544,
"pss_kb": 151903,
"private_dirty_kb": 138840,
"vmhwm_kb": 198556,
"utime_ticks": 67,
"stime_ticks": 7,
"cg_current": 5950930944,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 2040,
"msgs": null,
"events": null,
"pty_bytes": 32746,
"pty_writes": 15,
"rss_kb": 213612,
"pss_kb": 166971,
"private_dirty_kb": 153908,
"vmhwm_kb": 213620,
"utime_ticks": 74,
"stime_ticks": 8,
"cg_current": 5950930944,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2217,
"msgs": 501,
"events": 1792,
"pty_bytes": 36888,
"pty_writes": 17,
"rss_kb": 236192,
"pss_kb": 189448,
"private_dirty_kb": 176308,
"vmhwm_kb": 236244,
"utime_ticks": 96,
"stime_ticks": 9,
"cg_current": 5955866624,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2416,
"msgs": 601,
"events": 2154,
"pty_bytes": 41430,
"pty_writes": 20,
"rss_kb": 276612,
"pss_kb": 229795,
"private_dirty_kb": 216604,
"vmhwm_kb": 276624,
"utime_ticks": 127,
"stime_ticks": 11,
"cg_current": 5965406208,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2616,
"msgs": 701,
"events": 2496,
"pty_bytes": 42632,
"pty_writes": 21,
"rss_kb": 347020,
"pss_kb": 300087,
"private_dirty_kb": 286888,
"vmhwm_kb": 361648,
"utime_ticks": 167,
"stime_ticks": 14,
"cg_current": 5960589312,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 2893,
"msgs": 801,
"events": 2857,
"pty_bytes": 45204,
"pty_writes": 23,
"rss_kb": 377020,
"pss_kb": 330087,
"private_dirty_kb": 316888,
"vmhwm_kb": 377020,
"utime_ticks": 200,
"stime_ticks": 15,
"cg_current": 5960548352,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 3043,
"msgs": null,
"events": null,
"pty_bytes": 46835,
"pty_writes": 24,
"rss_kb": 403704,
"pss_kb": 356768,
"private_dirty_kb": 343572,
"vmhwm_kb": 403784,
"utime_ticks": 216,
"stime_ticks": 17,
"cg_current": 5960396800,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3193,
"msgs": 901,
"events": 3245,
"pty_bytes": 48189,
"pty_writes": 25,
"rss_kb": 407960,
"pss_kb": 361024,
"private_dirty_kb": 347828,
"vmhwm_kb": 407960,
"utime_ticks": 232,
"stime_ticks": 17,
"cg_current": 5960396800,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3568,
"msgs": 1001,
"events": 3588,
"pty_bytes": 53177,
"pty_writes": 28,
"rss_kb": 439108,
"pss_kb": 392172,
"private_dirty_kb": 378976,
"vmhwm_kb": 439108,
"utime_ticks": 276,
"stime_ticks": 19,
"cg_current": 5960798208,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3572,
"msgs": 1101,
"events": 3928,
"pty_bytes": 53177,
"pty_writes": 28,
"rss_kb": 439872,
"pss_kb": 392936,
"private_dirty_kb": 379740,
"vmhwm_kb": 439876,
"utime_ticks": 277,
"stime_ticks": 19,
"cg_current": 5960798208,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3575,
"msgs": 1201,
"events": 4298,
"pty_bytes": 53177,
"pty_writes": 28,
"rss_kb": 439880,
"pss_kb": 392944,
"private_dirty_kb": 379748,
"vmhwm_kb": 439880,
"utime_ticks": 277,
"stime_ticks": 19,
"cg_current": 5960798208,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3577,
"msgs": 1300,
"events": 4659,
"pty_bytes": 53177,
"pty_writes": 28,
"rss_kb": 439884,
"pss_kb": 392948,
"private_dirty_kb": 379752,
"vmhwm_kb": 439888,
"utime_ticks": 278,
"stime_ticks": 19,
"cg_current": 5961322496,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3579,
"msgs": 1400,
"events": 5011,
"pty_bytes": 53177,
"pty_writes": 28,
"rss_kb": 439908,
"pss_kb": 392972,
"private_dirty_kb": 379776,
"vmhwm_kb": 439908,
"utime_ticks": 279,
"stime_ticks": 19,
"cg_current": 5961322496,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3581,
"msgs": 1500,
"events": 5384,
"pty_bytes": 53177,
"pty_writes": 28,
"rss_kb": 440100,
"pss_kb": 393164,
"private_dirty_kb": 379968,
"vmhwm_kb": 440128,
"utime_ticks": 279,
"stime_ticks": 19,
"cg_current": 5961322496,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3584,
"msgs": 1600,
"events": 5730,
"pty_bytes": 53177,
"pty_writes": 28,
"rss_kb": 440320,
"pss_kb": 393384,
"private_dirty_kb": 380188,
"vmhwm_kb": 440368,
"utime_ticks": 280,
"stime_ticks": 19,
"cg_current": 5961097216,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3586,
"msgs": 1700,
"events": 6100,
"pty_bytes": 53177,
"pty_writes": 28,
"rss_kb": 440576,
"pss_kb": 393640,
"private_dirty_kb": 380444,
"vmhwm_kb": 440656,
"utime_ticks": 280,
"stime_ticks": 19,
"cg_current": 5961097216,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 3588,
"msgs": 1800,
"events": 6455,
"pty_bytes": 53177,
"pty_writes": 28,
"rss_kb": 440800,
"pss_kb": 393864,
"private_dirty_kb": 380668,
"vmhwm_kb": 440888,
"utime_ticks": 280,
"stime_ticks": 19,
"cg_current": 5961097216,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 4047,
"msgs": null,
"events": null,
"pty_bytes": 53177,
"pty_writes": 28,
"rss_kb": 590428,
"pss_kb": 543492,
"private_dirty_kb": 530296,
"vmhwm_kb": 590784,
"utime_ticks": 362,
"stime_ticks": 24,
"cg_current": 5960552448,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 4423,
"msgs": 1900,
"events": 6838,
"pty_bytes": 54671,
"pty_writes": 29,
"rss_kb": 632240,
"pss_kb": 585304,
"private_dirty_kb": 572108,
"vmhwm_kb": 632296,
"utime_ticks": 399,
"stime_ticks": 25,
"cg_current": 5960572928,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "boundary",
"t_ms": 4826,
"msgs": 2000,
"events": 7151,
"pty_bytes": 55609,
"pty_writes": 30,
"rss_kb": 667328,
"pss_kb": 620392,
"private_dirty_kb": 607196,
"vmhwm_kb": 667348,
"utime_ticks": 441,
"stime_ticks": 28,
"cg_current": 5960556544,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "done",
"t_ms": 4830,
"msgs": 2000,
"events": 7151,
"pty_bytes": 55609,
"pty_writes": 30,
"rss_kb": 667564,
"pss_kb": 620628,
"private_dirty_kb": 607432,
"vmhwm_kb": 667572,
"utime_ticks": 441,
"stime_ticks": 28,
"cg_current": 5960556544,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 5054,
"msgs": 2000,
"events": null,
"pty_bytes": 55609,
"pty_writes": 30,
"rss_kb": 692220,
"pss_kb": 645284,
"private_dirty_kb": 632088,
"vmhwm_kb": 692220,
"utime_ticks": 464,
"stime_ticks": 31,
"cg_current": 5940076544,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 6060,
"msgs": 2000,
"events": null,
"pty_bytes": 63491,
"pty_writes": 36,
"rss_kb": 710296,
"pss_kb": 663360,
"private_dirty_kb": 650164,
"vmhwm_kb": 710296,
"utime_ticks": 548,
"stime_ticks": 32,
"cg_current": 5940490240,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "final",
"t_ms": 6700,
"msgs": 2000,
"events": 7151,
"pty_bytes": 63491,
"pty_writes": 36,
"rss_kb": 710296,
"pss_kb": 663360,
"private_dirty_kb": 650164,
"vmhwm_kb": 710296,
"utime_ticks": 548,
"stime_ticks": 32,
"cg_current": 5940080640,
"cg_peak": 7893282816,
"cg_oom_kill": 0
},
{
"kind": "periodic",
"t_ms": 7066,
"msgs": 2000,
"events": null,
"pty_bytes": 68466,
"pty_writes": 38,
"rss_kb": 673924,
"pss_kb": 626988,
"private_dirty_kb": 613792,
"vmhwm_kb": 709544,
"utime_ticks": 574,
"stime_ticks": 36,
"cg_current": 5940109312,
"cg_peak": 7893282816,
"cg_oom_kill": 0
}
],
"events": [
{
"kind": "rpc",
"method": "session.create",
"t_ms": 202
},
{
"kind": "rpc",
"method": "startup.catalog",
"t_ms": 202
},
{
"kind": "rpc",
"method": "model.options",
"t_ms": 202
}
],
"summary": {
"result": "completed",
"cap_hit": false,
"cap_hit_basis": null,
"at_messages": null,
"exit": {
"exitCode": 0,
"signal": 0,
"t": 7613
},
"stream_done": true,
"msgs_streamed": 2000,
"events_streamed": 7151,
"pty_bytes_total": 68586,
"pty_data_callbacks": 40,
"first_byte_ms": 146,
"session_create_ms": 202,
"stream_start_ms": 1686,
"vmhwm_kb": 709544,
"cg_peak": 7893282816,
"drain_max_loop_lag_ms": 19,
"drain_lag_violations": 1,
"drain_ok": false,
"digest": null
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1672,12 +1672,64 @@ def _node_version_tuple(node_bin: str) -> tuple[int, int, int] | None:
return None
def _fnm_node26_candidates() -> list[str]:
"""Node binaries from fnm's installed versions, newest first.
fnm keeps each version at ``<FNM_DIR>/node-versions/v<X.Y.Z>/installation/
bin/node`` (default ``FNM_DIR``: ``$XDG_DATA_HOME/fnm`` or ``~/.local/share/
fnm``; macOS Homebrew also uses ``~/Library/Application Support/fnm``). When
the *active* node is older than 26.3 — e.g. the user's fnm default is on
v25 — the right 26.x is still installed and usable; surface it so OpenTUI
works without the user re-aliasing their global default. Version-sorted so
the newest qualifying node wins.
"""
roots: list[Path] = []
fnm_dir = os.environ.get("FNM_DIR")
if fnm_dir:
roots.append(Path(fnm_dir))
xdg = os.environ.get("XDG_DATA_HOME")
if xdg:
roots.append(Path(xdg) / "fnm")
roots.append(Path.home() / ".local" / "share" / "fnm")
roots.append(Path.home() / "Library" / "Application Support" / "fnm")
seen: set[Path] = set()
found: list[tuple[tuple[int, int, int], str]] = []
for root in roots:
versions_dir = root / "node-versions"
if versions_dir in seen or not versions_dir.is_dir():
continue
seen.add(versions_dir)
try:
entries = list(versions_dir.iterdir())
except OSError:
continue
for entry in entries:
node_bin = entry / "installation" / "bin" / "node"
if not (node_bin.is_file() and os.access(node_bin, os.X_OK)):
continue
# Trust the directory name for sorting; the real probe happens in
# the caller (a renamed/symlinked dir still gets version-checked).
name = entry.name.lstrip("v").split("-", 1)[0]
parts = name.split(".")
try:
ver = (int(parts[0]), int(parts[1]), int(parts[2]))
except (IndexError, ValueError):
ver = (0, 0, 0)
found.append((ver, str(node_bin)))
found.sort(key=lambda pair: pair[0], reverse=True)
return [path for _, path in found]
def _node26_bin_or_none() -> str | None:
"""Resolve a Node >= 26.3.0 binary (no exit — a probe), or ``None``.
``HERMES_NODE`` override > ``node`` on PATH, each gated on version >= 26.3.0.
OpenTUI's native renderer loads via the experimental ``node:ffi`` API that only
exists on Node 26.3+, so an older Node is treated as "not available".
Order: ``HERMES_NODE`` override > ``node`` on PATH > newest fnm-installed
version. Each is gated on the real ``--version`` being >= 26.3.0. OpenTUI's
native renderer loads via the experimental ``node:ffi`` API that only exists
on Node 26.3+, so an older Node is treated as "not available" — but an
installed-yet-inactive 26.x (common when fnm's default is on an older line)
is discovered and used so the engine still launches.
"""
candidates: list[str] = []
env_node = os.environ.get("HERMES_NODE")
@ -1686,6 +1738,7 @@ def _node26_bin_or_none() -> str | None:
path = shutil.which("node")
if path:
candidates.append(path)
candidates.extend(_fnm_node26_candidates())
for cand in candidates:
ver = _node_version_tuple(cand)
if ver is not None and ver >= NODE26_MIN_VERSION:
@ -2122,6 +2175,14 @@ def _launch_tui(
)
env.setdefault("HERMES_PYTHON", sys.executable)
env.setdefault("HERMES_CWD", os.getcwd())
# The TUI subprocess is launched with cwd=<engine package dir> (so its
# build/resolution works), which means the gateway it spawns would otherwise
# auto-detect THAT dir as the workspace (chrome bar showed "ui-opentui" no
# matter where you ran hermes). TERMINAL_CWD is the gateway's canonical
# launch-dir channel (_completion_cwd) — set it to the real cwd here so the
# session, chrome bar, and terminal tool all anchor to where you actually
# are. Worktree mode overrides it to the worktree path below.
env.setdefault("TERMINAL_CWD", os.getcwd())
env.setdefault("NODE_ENV", "development" if tui_dev else "production")
wt_info = None

View file

@ -0,0 +1,69 @@
"""Node-26 resolution for the OpenTUI engine + the launch-cwd channel.
Regression coverage for two ways the local TUI silently fell back to Ink /
showed the wrong directory:
1. fnm's active/default node was on an older line (v25) while a usable v26.3
sat installed-but-inactive — ``_node26_bin_or_none`` only checked
``HERMES_NODE`` + ``which node`` and so reported "no node 26" → OpenTUI
unavailable → Ink fallback.
2. ``TERMINAL_CWD`` (the gateway's launch-dir channel) was only exported in
worktree mode, so a normal launch let the gateway auto-detect the engine's
own package dir as the workspace.
"""
import os
import stat
import pytest
import hermes_cli.main as main_mod
def _fake_node(path, version: str) -> None:
"""Write a stub `node` that prints `version` for `--version`."""
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(f'#!/bin/sh\necho "{version}"\n')
path.chmod(path.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
class TestFnmNode26Discovery:
def test_discovers_inactive_v26_when_default_is_older(self, tmp_path, monkeypatch):
"""A v26.3 installed under fnm is found even when PATH `node` is v25."""
fnm_dir = tmp_path / "fnm"
for ver in ("24.11.0", "25.9.0", "26.3.0"):
_fake_node(fnm_dir / "node-versions" / f"v{ver}" / "installation" / "bin" / "node", f"v{ver}")
monkeypatch.setenv("FNM_DIR", str(fnm_dir))
monkeypatch.delenv("HERMES_NODE", raising=False)
# PATH node is the too-old default (v25).
monkeypatch.setattr(main_mod.shutil, "which", lambda _b: str(
fnm_dir / "node-versions" / "v25.9.0" / "installation" / "bin" / "node"
))
resolved = main_mod._node26_bin_or_none()
assert resolved is not None
assert "v26.3.0" in resolved # newest qualifying, not the v25 default
def test_candidates_sorted_newest_first(self, tmp_path, monkeypatch):
fnm_dir = tmp_path / "fnm"
for ver in ("26.1.0", "26.4.0", "25.0.0"):
_fake_node(fnm_dir / "node-versions" / f"v{ver}" / "installation" / "bin" / "node", f"v{ver}")
monkeypatch.setenv("FNM_DIR", str(fnm_dir))
cands = main_mod._fnm_node26_candidates()
# Directory-name order: 26.4 before 26.1 before 25.0.
idx = [next(i for i, c in enumerate(cands) if f"v{v}" in c) for v in ("26.4.0", "26.1.0", "25.0.0")]
assert idx == sorted(idx)
def test_no_fnm_dir_is_empty_not_error(self, tmp_path, monkeypatch):
monkeypatch.setenv("FNM_DIR", str(tmp_path / "does-not-exist"))
monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "xdg-none"))
monkeypatch.setattr(main_mod.Path, "home", classmethod(lambda cls: tmp_path / "home-none"))
assert main_mod._fnm_node26_candidates() == []
def test_hermes_node_still_wins(self, tmp_path, monkeypatch):
"""An explicit HERMES_NODE >= 26.3 takes precedence over fnm discovery."""
explicit = tmp_path / "explicit" / "node"
_fake_node(explicit, "v26.5.0")
monkeypatch.setenv("HERMES_NODE", str(explicit))
monkeypatch.setattr(main_mod.shutil, "which", lambda _b: None)
assert main_mod._node26_bin_or_none() == str(explicit)

View file

@ -3804,6 +3804,51 @@ def test_session_info_includes_mcp_servers(monkeypatch):
assert info["mcp_servers"] == fake_status
def test_session_info_includes_session_title(monkeypatch):
"""session.info carries the live session title (window-title chrome).
Resolution order mirrors _session_live_title: DB row wins, a queued
pending_title fills in before the row exists, "" until either lands.
"""
agent = types.SimpleNamespace(tools=[], model="m", provider="p")
# No session at all -> "" (and never a crash).
assert server._session_info(agent, None)["title"] == ""
# pending_title before the DB row exists.
session = {"session_key": "k1", "pending_title": "rename the moon"}
monkeypatch.setattr(server, "_get_db", lambda: None)
assert server._session_info(agent, session)["title"] == "rename the moon"
# DB row wins over pending.
fake_db = types.SimpleNamespace(get_session_title=lambda key: "db title")
monkeypatch.setattr(server, "_get_db", lambda: fake_db)
assert server._session_info(agent, session)["title"] == "db title"
def test_emit_title_refresh_pushes_session_info(monkeypatch):
"""_emit_title_refresh emits a session.info for a live session and is a
silent no-op for unknown/agent-less sessions (it runs on the auto-title
worker thread -- it must never raise)."""
events = []
monkeypatch.setattr(
server, "_emit", lambda event_type, sid, payload: events.append((event_type, sid, payload))
)
monkeypatch.setattr(server, "_session_info", lambda agent, session: {"title": "t"})
agent = types.SimpleNamespace(tools=[], model="m", provider="p")
monkeypatch.setitem(server._sessions, "title-test", {"agent": agent, "session_key": "k"})
server._emit_title_refresh("title-test")
assert events == [("session.info", "title-test", {"title": "t"})]
# Unknown sid / no agent -> no emission, no exception.
server._emit_title_refresh("missing-sid")
monkeypatch.setitem(server._sessions, "agentless", {"session_key": "k2"})
server._emit_title_refresh("agentless")
assert len(events) == 1
# ---------------------------------------------------------------------------
# History-mutating commands must reject while session.running is True.
# Without these guards, prompt.submit's post-run history write either

View file

@ -2373,6 +2373,17 @@ def _session_info(agent, session: dict | None = None) -> dict:
yolo = bool(_YOLO_MODE_FROZEN) or session_yolo or _get_approval_mode() == "off"
except Exception:
yolo = False
# Session title (DB row, falling back to a not-yet-applied pending_title).
# Drives client window-title chrome (OSC 0/2 in the native TUI); "" until
# the first exchange titles the session.
title = ""
if session is not None:
try:
title = _session_live_title(
session, str(session.get("session_key") or "")
)
except Exception:
title = ""
info: dict = {
"model": getattr(agent, "model", ""),
"provider": getattr(agent, "provider", ""),
@ -2385,6 +2396,7 @@ def _session_info(agent, session: dict | None = None) -> dict:
"cwd": cwd,
"branch": _git_branch_for_cwd(cwd),
"personality": str(personality or ""),
"title": title,
"running": bool((session or {}).get("running")),
"desktop_contract": DESKTOP_BACKEND_CONTRACT,
"version": "",
@ -4399,6 +4411,22 @@ def _session_live_title(session: dict, key: str) -> str:
return title
def _emit_title_refresh(sid: str) -> None:
"""Push a session.info refresh after a title change (pending-title
application, auto-title landing, or a session.title rename) so clients'
window-title chrome updates immediately. Thread-safe (auto-title calls
this from its worker thread; _emit serializes on the stdout lock).
Never raises."""
try:
session = _sessions.get(sid)
agent = (session or {}).get("agent")
if session is None or agent is None:
return
_emit("session.info", sid, _session_info(agent, session))
except Exception:
pass
def _session_live_item(sid: str, session: dict, current_sid: str = "") -> dict:
key = str(session.get("session_key") or sid)
agent = session.get("agent")
@ -4624,15 +4652,18 @@ def _(rid, params: dict) -> dict:
title = (params.get("title", "") or "").strip()
if not title:
return _err(rid, 4021, "title required")
sid = str(params.get("session_id") or "")
try:
if db.set_session_title(key, title):
session["pending_title"] = None
_emit_title_refresh(sid)
return _ok(rid, {"pending": False, "title": title})
# rowcount == 0 can mean "same value" as well as "missing row".
# Queue only when the session row truly does not exist yet.
existing_row = db.get_session(key)
if existing_row:
session["pending_title"] = None
_emit_title_refresh(sid)
return _ok(
rid,
{
@ -4641,6 +4672,7 @@ def _(rid, params: dict) -> dict:
},
)
session["pending_title"] = title
_emit_title_refresh(sid)
return _ok(rid, {"pending": True, "title": title})
except ValueError as e:
return _err(rid, 4022, str(e))
@ -5997,10 +6029,20 @@ def _run_prompt_submit(rid, sid: str, session: dict, text: Any) -> None:
text,
raw,
session.get("history", []),
# Auto-title lands on a background thread — refresh
# session.info when it does so clients' window-title
# chrome (OSC 0/2) updates without waiting for the
# next turn. _emit is stdout-lock-guarded (thread-safe).
title_callback=lambda _title: _emit_title_refresh(sid),
)
except Exception:
pass
# The pending title (applied synchronously above) is visible NOW —
# refresh session.info so window-title chrome picks it up.
if status == "complete" and _pending:
_emit_title_refresh(sid)
# CLI parity: when voice-mode TTS is on, speak the agent reply
# (cli.py:_voice_speak_response). Only the final text — tool
# calls / reasoning already stream separately and would be

View file

@ -0,0 +1,56 @@
[
(string)
(raw_string)
(heredoc_body)
(heredoc_start)
] @string
(command_name) @function
(variable_name) @property
[
"case"
"do"
"done"
"elif"
"else"
"esac"
"export"
"fi"
"for"
"function"
"if"
"in"
"select"
"then"
"unset"
"until"
"while"
] @keyword
(comment) @comment
(function_definition name: (word) @function)
(file_descriptor) @number
[
(command_substitution)
(process_substitution)
(expansion)
]@embedded
[
"$"
"&&"
">"
">>"
"<"
"|"
] @operator
(
(command (_) @constant)
(#match? @constant "^-")
)

Binary file not shown.

View file

@ -0,0 +1,81 @@
(identifier) @variable
((identifier) @constant
(#match? @constant "^[A-Z][A-Z\\d_]*$"))
"break" @keyword
"case" @keyword
"const" @keyword
"continue" @keyword
"default" @keyword
"do" @keyword
"else" @keyword
"enum" @keyword
"extern" @keyword
"for" @keyword
"if" @keyword
"inline" @keyword
"return" @keyword
"sizeof" @keyword
"static" @keyword
"struct" @keyword
"switch" @keyword
"typedef" @keyword
"union" @keyword
"volatile" @keyword
"while" @keyword
"#define" @keyword
"#elif" @keyword
"#else" @keyword
"#endif" @keyword
"#if" @keyword
"#ifdef" @keyword
"#ifndef" @keyword
"#include" @keyword
(preproc_directive) @keyword
"--" @operator
"-" @operator
"-=" @operator
"->" @operator
"=" @operator
"!=" @operator
"*" @operator
"&" @operator
"&&" @operator
"+" @operator
"++" @operator
"+=" @operator
"<" @operator
"==" @operator
">" @operator
"||" @operator
"." @delimiter
";" @delimiter
(string_literal) @string
(system_lib_string) @string
(null) @constant
(number_literal) @number
(char_literal) @number
(field_identifier) @property
(statement_identifier) @label
(type_identifier) @type
(primitive_type) @type
(sized_type_specifier) @type
(call_expression
function: (identifier) @function)
(call_expression
function: (field_expression
field: (field_identifier) @function))
(function_declarator
declarator: (identifier) @function)
(preproc_function_def
name: (identifier) @function.special)
(comment) @comment

Binary file not shown.

View file

@ -0,0 +1,64 @@
(comment) @comment
(tag_name) @tag
(nesting_selector) @tag
(universal_selector) @tag
"~" @operator
">" @operator
"+" @operator
"-" @operator
"*" @operator
"/" @operator
"=" @operator
"^=" @operator
"|=" @operator
"~=" @operator
"$=" @operator
"*=" @operator
"and" @operator
"or" @operator
"not" @operator
"only" @operator
(attribute_selector (plain_value) @string)
(pseudo_element_selector (tag_name) @attribute)
(pseudo_class_selector (class_name) @attribute)
(class_name) @property
(id_name) @property
(namespace_name) @property
(property_name) @property
(feature_name) @property
(attribute_name) @attribute
(function_name) @function
((property_name) @variable
(#match? @variable "^--"))
((plain_value) @variable
(#match? @variable "^--"))
"@media" @keyword
"@import" @keyword
"@charset" @keyword
"@namespace" @keyword
"@supports" @keyword
"@keyframes" @keyword
(at_keyword) @keyword
(to) @keyword
(from) @keyword
(important) @keyword
(string_value) @string
(color_value) @string.special
(integer_value) @number
(float_value) @number
(unit) @type
"#" @punctuation.delimiter
"," @punctuation.delimiter
":" @punctuation.delimiter

Binary file not shown.

View file

@ -0,0 +1,123 @@
; Function calls
(call_expression
function: (identifier) @function)
(call_expression
function: (identifier) @function.builtin
(#match? @function.builtin "^(append|cap|close|complex|copy|delete|imag|len|make|new|panic|print|println|real|recover)$"))
(call_expression
function: (selector_expression
field: (field_identifier) @function.method))
; Function definitions
(function_declaration
name: (identifier) @function)
(method_declaration
name: (field_identifier) @function.method)
; Identifiers
(type_identifier) @type
(field_identifier) @property
(identifier) @variable
; Operators
[
"--"
"-"
"-="
":="
"!"
"!="
"..."
"*"
"*"
"*="
"/"
"/="
"&"
"&&"
"&="
"%"
"%="
"^"
"^="
"+"
"++"
"+="
"<-"
"<"
"<<"
"<<="
"<="
"="
"=="
">"
">="
">>"
">>="
"|"
"|="
"||"
"~"
] @operator
; Keywords
[
"break"
"case"
"chan"
"const"
"continue"
"default"
"defer"
"else"
"fallthrough"
"for"
"func"
"go"
"goto"
"if"
"import"
"interface"
"map"
"package"
"range"
"return"
"select"
"struct"
"switch"
"type"
"var"
] @keyword
; Literals
[
(interpreted_string_literal)
(raw_string_literal)
(rune_literal)
] @string
(escape_sequence) @escape
[
(int_literal)
(float_literal)
(imaginary_literal)
] @number
[
(true)
(false)
(nil)
(iota)
] @constant.builtin
(comment) @comment

Binary file not shown.

View file

@ -0,0 +1,13 @@
(tag_name) @tag
(erroneous_end_tag_name) @tag.error
(doctype) @constant
(attribute_name) @attribute
(attribute_value) @string
(comment) @comment
[
"<"
">"
"</"
"/>"
] @punctuation.bracket

Binary file not shown.

View file

@ -0,0 +1,16 @@
(pair
key: (_) @string.special.key)
(string) @string
(number) @number
[
(null)
(true)
(false)
] @constant.builtin
(escape_sequence) @escape
(comment) @comment

Binary file not shown.

View file

@ -0,0 +1,15 @@
{
"comment": "Tree-sitter grammars vendored beyond @opentui/core's bundled 5 (ts/js/markdown/markdown_inline/zig). Source of truth for scripts/update-parsers.mjs (vendoring) and src/boundary/parsers.ts (boot registration). Curated in docs/plans/opentui-syntax-highlighting-languages.md — cpp deliberately dropped (3.28 MB, ~half the bundle). Aliases are belt-and-braces: core's extToFiletype/infoStringToFiletype already normalize py->python etc., but a literal alias filetype reaching the client still resolves.",
"parsers": [
{ "filetype": "python", "aliases": ["py"], "org": "tree-sitter", "tag": "v0.23.6" },
{ "filetype": "rust", "aliases": ["rs"], "org": "tree-sitter", "tag": "v0.23.2" },
{ "filetype": "go", "aliases": [], "org": "tree-sitter", "tag": "v0.23.4" },
{ "filetype": "bash", "aliases": ["sh", "shell", "zsh"], "org": "tree-sitter", "tag": "v0.23.3" },
{ "filetype": "json", "aliases": [], "org": "tree-sitter", "tag": "v0.24.8" },
{ "filetype": "c", "aliases": ["h"], "org": "tree-sitter", "tag": "v0.23.5" },
{ "filetype": "html", "aliases": [], "org": "tree-sitter", "tag": "v0.23.2" },
{ "filetype": "css", "aliases": [], "org": "tree-sitter", "tag": "v0.23.2" },
{ "filetype": "yaml", "aliases": ["yml"], "org": "tree-sitter-grammars", "tag": "v0.7.1" },
{ "filetype": "toml", "aliases": [], "org": "tree-sitter-grammars", "tag": "v0.7.0" }
]
}

View file

@ -0,0 +1,137 @@
; Identifier naming conventions
(identifier) @variable
((identifier) @constructor
(#match? @constructor "^[A-Z]"))
((identifier) @constant
(#match? @constant "^[A-Z][A-Z_]*$"))
; Function calls
(decorator) @function
(decorator
(identifier) @function)
(call
function: (attribute attribute: (identifier) @function.method))
(call
function: (identifier) @function)
; Builtin functions
((call
function: (identifier) @function.builtin)
(#match?
@function.builtin
"^(abs|all|any|ascii|bin|bool|breakpoint|bytearray|bytes|callable|chr|classmethod|compile|complex|delattr|dict|dir|divmod|enumerate|eval|exec|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|isinstance|issubclass|iter|len|list|locals|map|max|memoryview|min|next|object|oct|open|ord|pow|print|property|range|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|vars|zip|__import__)$"))
; Function definitions
(function_definition
name: (identifier) @function)
(attribute attribute: (identifier) @property)
(type (identifier) @type)
; Literals
[
(none)
(true)
(false)
] @constant.builtin
[
(integer)
(float)
] @number
(comment) @comment
(string) @string
(escape_sequence) @escape
(interpolation
"{" @punctuation.special
"}" @punctuation.special) @embedded
[
"-"
"-="
"!="
"*"
"**"
"**="
"*="
"/"
"//"
"//="
"/="
"&"
"&="
"%"
"%="
"^"
"^="
"+"
"->"
"+="
"<"
"<<"
"<<="
"<="
"<>"
"="
":="
"=="
">"
">="
">>"
">>="
"|"
"|="
"~"
"@="
"and"
"in"
"is"
"not"
"or"
"is not"
"not in"
] @operator
[
"as"
"assert"
"async"
"await"
"break"
"class"
"continue"
"def"
"del"
"elif"
"else"
"except"
"exec"
"finally"
"for"
"from"
"global"
"if"
"import"
"lambda"
"nonlocal"
"pass"
"print"
"raise"
"return"
"try"
"while"
"with"
"yield"
"match"
"case"
] @keyword

Binary file not shown.

View file

@ -0,0 +1,161 @@
; Identifiers
(type_identifier) @type
(primitive_type) @type.builtin
(field_identifier) @property
; Identifier conventions
; Assume all-caps names are constants
((identifier) @constant
(#match? @constant "^[A-Z][A-Z\\d_]+$'"))
; Assume uppercase names are enum constructors
((identifier) @constructor
(#match? @constructor "^[A-Z]"))
; Assume that uppercase names in paths are types
((scoped_identifier
path: (identifier) @type)
(#match? @type "^[A-Z]"))
((scoped_identifier
path: (scoped_identifier
name: (identifier) @type))
(#match? @type "^[A-Z]"))
((scoped_type_identifier
path: (identifier) @type)
(#match? @type "^[A-Z]"))
((scoped_type_identifier
path: (scoped_identifier
name: (identifier) @type))
(#match? @type "^[A-Z]"))
; Assume all qualified names in struct patterns are enum constructors. (They're
; either that, or struct names; highlighting both as constructors seems to be
; the less glaring choice of error, visually.)
(struct_pattern
type: (scoped_type_identifier
name: (type_identifier) @constructor))
; Function calls
(call_expression
function: (identifier) @function)
(call_expression
function: (field_expression
field: (field_identifier) @function.method))
(call_expression
function: (scoped_identifier
"::"
name: (identifier) @function))
(generic_function
function: (identifier) @function)
(generic_function
function: (scoped_identifier
name: (identifier) @function))
(generic_function
function: (field_expression
field: (field_identifier) @function.method))
(macro_invocation
macro: (identifier) @function.macro
"!" @function.macro)
; Function definitions
(function_item (identifier) @function)
(function_signature_item (identifier) @function)
(line_comment) @comment
(block_comment) @comment
(line_comment (doc_comment)) @comment.documentation
(block_comment (doc_comment)) @comment.documentation
"(" @punctuation.bracket
")" @punctuation.bracket
"[" @punctuation.bracket
"]" @punctuation.bracket
"{" @punctuation.bracket
"}" @punctuation.bracket
(type_arguments
"<" @punctuation.bracket
">" @punctuation.bracket)
(type_parameters
"<" @punctuation.bracket
">" @punctuation.bracket)
"::" @punctuation.delimiter
":" @punctuation.delimiter
"." @punctuation.delimiter
"," @punctuation.delimiter
";" @punctuation.delimiter
(parameter (identifier) @variable.parameter)
(lifetime (identifier) @label)
"as" @keyword
"async" @keyword
"await" @keyword
"break" @keyword
"const" @keyword
"continue" @keyword
"default" @keyword
"dyn" @keyword
"else" @keyword
"enum" @keyword
"extern" @keyword
"fn" @keyword
"for" @keyword
"gen" @keyword
"if" @keyword
"impl" @keyword
"in" @keyword
"let" @keyword
"loop" @keyword
"macro_rules!" @keyword
"match" @keyword
"mod" @keyword
"move" @keyword
"pub" @keyword
"raw" @keyword
"ref" @keyword
"return" @keyword
"static" @keyword
"struct" @keyword
"trait" @keyword
"type" @keyword
"union" @keyword
"unsafe" @keyword
"use" @keyword
"where" @keyword
"while" @keyword
"yield" @keyword
(crate) @keyword
(mutable_specifier) @keyword
(use_list (self) @keyword)
(scoped_use_list (self) @keyword)
(scoped_identifier (self) @keyword)
(super) @keyword
(self) @variable.builtin
(char_literal) @string
(string_literal) @string
(raw_string_literal) @string
(boolean_literal) @constant.builtin
(integer_literal) @constant.builtin
(float_literal) @constant.builtin
(escape_sequence) @escape
(attribute_item) @attribute
(inner_attribute_item) @attribute
"*" @operator
"&" @operator
"'" @operator

Binary file not shown.

View file

@ -0,0 +1,53 @@
; Properties
;-----------
(bare_key) @type
(quoted_key) @string
(pair
(bare_key)) @property
(pair
(dotted_key
(bare_key) @property))
; Literals
;---------
(boolean) @boolean
(comment) @comment
(string) @string
[
(integer)
(float)
] @number
[
(offset_date_time)
(local_date_time)
(local_date)
(local_time)
] @string.special
; Punctuation
;------------
[
"."
","
] @punctuation.delimiter
"=" @operator
[
"["
"]"
"[["
"]]"
"{"
"}"
] @punctuation.bracket

Binary file not shown.

View file

@ -0,0 +1,79 @@
(boolean_scalar) @boolean
(null_scalar) @constant.builtin
[
(double_quote_scalar)
(single_quote_scalar)
(block_scalar)
(string_scalar)
] @string
[
(integer_scalar)
(float_scalar)
] @number
(comment) @comment
[
(anchor_name)
(alias_name)
] @label
(tag) @type
[
(yaml_directive)
(tag_directive)
(reserved_directive)
] @attribute
(block_mapping_pair
key: (flow_node
[
(double_quote_scalar)
(single_quote_scalar)
] @property))
(block_mapping_pair
key: (flow_node
(plain_scalar
(string_scalar) @property)))
(flow_mapping
(_
key: (flow_node
[
(double_quote_scalar)
(single_quote_scalar)
] @property)))
(flow_mapping
(_
key: (flow_node
(plain_scalar
(string_scalar) @property))))
[
","
"-"
":"
">"
"?"
"|"
] @punctuation.delimiter
[
"["
"]"
"{"
"}"
] @punctuation.bracket
[
"*"
"&"
"---"
"..."
] @punctuation.special

Binary file not shown.

View file

@ -16,11 +16,15 @@ import { createCliRenderer } from '@opentui/core'
import { render } from '@opentui/solid'
import { installMultiClickSelection } from '../src/boundary/multiClickSelect.ts'
import { registerVendoredParsers } from '../src/boundary/parsers.ts'
import { createSessionStore } from '../src/logic/store.ts'
import { App } from '../src/view/App.tsx'
import { ThemeProvider } from '../src/view/theme.tsx'
import { materialize } from './fixture.ts'
// Same grammar registration as the live entry so fixture code blocks highlight.
registerVendoredParsers()
const TOTAL = Number.parseInt(process.env.DEMO_TOTAL ?? '', 10) || 200
const store = createSessionStore()

View file

@ -0,0 +1,71 @@
/**
* Vendor the extra Tree-sitter grammars listed in parsers/manifest.json into
* parsers/<filetype>/ (wasm + highlights.scm). Plain Node fetch — no Bun
* (core's update-assets.js is bun-shebanged; its download logic is just
* fetch+fs, so we do the same two writes ourselves and keep the generated
* import-module out of the esbuild bundle entirely — registration reads the
* vendored files by PATH at runtime, see src/boundary/parsers.ts).
*
* Idempotent: existing valid files are kept unless --force. Validates wasm
* magic and a non-empty query so a bad download can never be committed.
*
* node scripts/update-parsers.mjs [--force]
*
* The vendored files are COMMITTED (build inputs, like @opentui/core's own
* assets/) — builds and offline machines never re-download.
*/
import { Buffer } from 'node:buffer'
import { mkdir, readFile, writeFile } from 'node:fs/promises'
import { dirname, join, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const parsersDir = join(root, 'parsers')
const force = process.argv.includes('--force')
const manifest = JSON.parse(await readFile(join(parsersDir, 'manifest.json'), 'utf8'))
const wasmUrl = p =>
`https://github.com/${p.org}/tree-sitter-${p.filetype}/releases/download/${p.tag}/tree-sitter-${p.filetype}.wasm`
const scmUrl = p =>
`https://raw.githubusercontent.com/${p.org}/tree-sitter-${p.filetype}/${p.tag}/queries/highlights.scm`
async function fetchBytes(url) {
const response = await globalThis.fetch(url)
if (!response.ok) throw new Error(`${response.status} ${response.statusText} for ${url}`)
return Buffer.from(await response.arrayBuffer())
}
async function haveValid(path, validate) {
try {
return validate(await readFile(path))
} catch {
return false
}
}
const isWasm = bytes => bytes.length > 8 && bytes.subarray(0, 4).toString('latin1') === '\0asm'
const isQuery = bytes => bytes.length > 0 && bytes.toString('utf8').trim().length > 0
let downloaded = 0
for (const parser of manifest.parsers) {
const dir = join(parsersDir, parser.filetype)
await mkdir(dir, { recursive: true })
const targets = [
{ name: `tree-sitter-${parser.filetype}.wasm`, url: wasmUrl(parser), validate: isWasm },
{ name: 'highlights.scm', url: scmUrl(parser), validate: isQuery }
]
for (const target of targets) {
const path = join(dir, target.name)
if (!force && (await haveValid(path, target.validate))) {
console.log(`✓ kept ${parser.filetype}/${target.name}`)
continue
}
const bytes = await fetchBytes(target.url)
if (!target.validate(bytes)) throw new Error(`validation failed for ${target.url}`)
await writeFile(path, bytes)
downloaded += 1
console.log(`↓ fetched ${parser.filetype}/${target.name} (${(bytes.length / 1024).toFixed(0)} KB)`)
}
}
console.log(`done — ${downloaded} file(s) fetched, ${manifest.parsers.length} grammars vendored`)

View file

@ -0,0 +1,93 @@
/**
* Extra Tree-sitter grammar registration — the syntax-highlighting language
* expansion (docs/plans/opentui-syntax-highlighting-languages.md).
*
* @opentui/core@0.4.0 bundles only 5 grammars (ts/js/markdown/markdown_inline/
* zig); everything else rendered plain text. The cure is the public
* `addDefaultParsers()` API + the grammars vendored under `parsers/<filetype>/`
* (committed; refresh via `node scripts/update-parsers.mjs`).
*
* Why paths, not the generated import-module: core's `updateAssets` generates
* a Bun-flavored module (`import(... { with: { type: "file" } })`) that esbuild
* can't bundle; its own Node fallback resolves plain file paths anyway, and
* `FiletypeParserOptions.wasm`/`queries.highlights` accept local paths
* directly. So registration just points at the vendored files — resolved at
* RUNTIME by walking up from this module to the package root, which works from
* both the esbuild bundle (dist/main.js → ../parsers) and vitest's src tree
* (src/boundary → ../../parsers).
*
* Must run BEFORE the first `<code>`/`<markdown>` mount (they grab the global
* tree-sitter client lazily) — the entry imports + calls this at module load,
* ahead of renderer acquisition. Total: a missing assets dir degrades to
* core's bundled set (plain text for the extras), never a throw.
*/
import { existsSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { addDefaultParsers } from '@opentui/core'
import manifest from '../../parsers/manifest.json'
import { getLog } from './log.ts'
interface ManifestParser {
readonly filetype: string
readonly aliases: readonly string[]
}
/** The registered parser configs (exported shape for tests/diagnostics). */
export interface RegisteredParser {
filetype: string
aliases?: string[]
wasm: string
queries: { highlights: string[] }
}
/** Walk up from this module (bundle: dist/…; tests: src/boundary/…) to the
* package root's vendored `parsers/` dir. */
function findParsersDir(): string | undefined {
let dir = dirname(fileURLToPath(import.meta.url))
for (let hop = 0; hop < 5; hop += 1) {
const candidate = join(dir, 'parsers')
if (existsSync(join(candidate, 'manifest.json'))) return candidate
dir = dirname(dir)
}
return undefined
}
/** Build the parser configs for every vendored grammar whose files exist. */
export function vendoredParsers(parsersDir: string | undefined = findParsersDir()): RegisteredParser[] {
if (!parsersDir) return []
const configs: RegisteredParser[] = []
for (const parser of (manifest as { parsers: ManifestParser[] }).parsers) {
const wasm = join(parsersDir, parser.filetype, `tree-sitter-${parser.filetype}.wasm`)
const highlights = join(parsersDir, parser.filetype, 'highlights.scm')
if (!existsSync(wasm) || !existsSync(highlights)) continue
configs.push({
filetype: parser.filetype,
...(parser.aliases.length ? { aliases: [...parser.aliases] } : {}),
wasm,
queries: { highlights: [highlights] }
})
}
return configs
}
/** Register the vendored grammars with core's global default-parser list.
* Returns what was registered (empty on any failure — plain-text fallback). */
export function registerVendoredParsers(): RegisteredParser[] {
try {
const parsers = vendoredParsers()
if (!parsers.length) {
getLog().warn('parsers', 'no vendored tree-sitter grammars found — extras render plain', {})
return []
}
addDefaultParsers(parsers)
return parsers
} catch (cause) {
getLog().warn('parsers', 'tree-sitter registration failed — extras render plain', {
cause: String(cause)
})
return []
}
}

View file

@ -52,6 +52,9 @@ export const SessionInfoPatchSchema = Schema.Struct({
fast: opt(Bool),
cwd: opt(Str),
branch: opt(Str),
// session title ("" until the first exchange titles it) — drives the
// terminal window-title chrome (OSC 0/2 via renderer.setTerminalTitle).
title: opt(Str),
running: opt(Bool),
// status-bar chrome extras (Epic 1.3): update banner, profile badge, MCP count.
// `update_behind` is null on the wire until the async update check resolves.

View file

@ -0,0 +1,106 @@
/**
* Terminal chrome seam — window title (OSC 0/2) + desktop notifications
* (OSC 9/99/777) through the renderer's native output path.
*
* Why the renderer and not process.stdout: the zig side owns the terminal —
* `setTerminalTitle` is a native FFI call and `writeOut` serializes raw
* control bytes with frame presentation (core itself uses it for OSC 111),
* so chrome writes can never tear a frame.
*
* Focus suppression: core parses mode-1004 focus reports (`ESC[I`/`ESC[O`)
* and re-emits them as renderer `focus`/`blur` events — notifications are
* skipped while the terminal reports focused (you're already looking at it).
* Terminals that never report focus leave the state at the assumed-focused
* initial value… which would swallow every notification, so the FIRST blur
* is what arms suppression: until a blur arrives we treat focus as unknown
* and notify unconditionally (worst case: a redundant ping while focused).
*
* Everything here is total — chrome must never throw into the render loop
* or a teardown path.
*/
import type { CliRenderer } from '@opentui/core'
import type { TermNotification } from '../logic/termChrome.ts'
import {
notifyEnabled,
notifySequences,
TITLE_STACK_RESTORE,
TITLE_STACK_SAVE,
windowTitleFor
} from '../logic/termChrome.ts'
import { getLog } from './log.ts'
/** What the view layer needs from the chrome seam (DI-friendly for tests). */
export interface TerminalChromeSeam {
/** Set the window title from the session title (undefined → generic). */
readonly setTitle: (sessionTitle: string | undefined) => void
/** Announce "waiting on you" to the hosting terminal (no-op while focused). */
readonly notify: (notification: TermNotification) => void
}
/** The renderer surface the seam writes through (runtime-verified shapes). */
interface RendererSeam {
setTerminalTitle(title: string): void
writeOut(chunk: string): void
on(event: 'focus' | 'blur', listener: () => void): unknown
once(event: 'destroy', listener: () => void): unknown
readonly isDestroyed: boolean
}
/** Install the chrome seam on a live renderer. Idempotent per renderer use —
* the entry calls it once, right next to the render bridge. */
export function installTerminalChrome(renderer: CliRenderer): TerminalChromeSeam {
const seam = renderer as unknown as RendererSeam
const notificationsOn = notifyEnabled()
// unknown (null) until the terminal proves it reports focus; then boolean.
let focused: boolean | null = null
try {
seam.on('focus', () => {
focused = true
})
seam.on('blur', () => {
focused = false
})
} catch (cause) {
getLog().warn('chrome', 'focus tracking unavailable', { cause: String(cause) })
}
// Bracket our title ownership: save the user's title now, restore on quit.
// Best-effort — terminals without the XTWINOPS title stack ignore both.
writeRaw(seam, TITLE_STACK_SAVE)
seam.once('destroy', () => writeRaw(seam, TITLE_STACK_RESTORE, { evenIfDestroyed: true }))
let lastTitle = ''
return {
setTitle: sessionTitle => {
const title = windowTitleFor(sessionTitle)
if (title === lastTitle) return
lastTitle = title
try {
if (!seam.isDestroyed) seam.setTerminalTitle(title)
} catch (cause) {
getLog().warn('chrome', 'setTerminalTitle failed', { cause: String(cause) })
}
},
notify: notification => {
if (!notificationsOn || focused === true) return
for (const sequence of notifySequences(notification)) writeRaw(seam, sequence)
}
}
}
/** Raw control write through the renderer; falls back to process.stdout when
* the renderer is already gone (the title-stack restore on destroy — at that
* point there is no frame left to tear). */
function writeRaw(seam: RendererSeam, chunk: string, options?: { evenIfDestroyed?: boolean }): void {
try {
if (!seam.isDestroyed) {
seam.writeOut(chunk)
return
}
if (options?.evenIfDestroyed) process.stdout.write(chunk)
} catch (cause) {
getLog().warn('chrome', 'control write failed', { cause: String(cause) })
}
}

View file

@ -30,10 +30,11 @@ import { GatewayService, type GatewayServiceShape } from '../boundary/gateway/Ga
import { liveGatewayLayer } from '../boundary/gateway/liveGateway.ts'
import { getLog } from '../boundary/log.ts'
import { startMemlog } from '../boundary/memlog.ts'
import { registerVendoredParsers } from '../boundary/parsers.ts'
import { acquireRenderer } from '../boundary/renderer.ts'
import { makeAppLayer } from '../boundary/runtime.ts'
import { nthAssistantResponse } from '../logic/copy.ts'
import { envFlag } from '../logic/env.ts'
import { envFlag, launchCwd } from '../logic/env.ts'
import { createPromptHistory, dirHistoryPersister, loadDirHistory } from '../logic/history.ts'
import { createPasteStore } from '../logic/pastes.ts'
import { mapResumeHistory } from '../logic/resume.ts'
@ -48,6 +49,12 @@ import {
} from '../logic/slash.ts'
import { createSessionStore, type SessionStore } from '../logic/store.ts'
import { App } from '../view/App.tsx'
import { TerminalChrome } from '../view/terminalChrome.tsx'
// Syntax-highlighting language expansion: register the vendored tree-sitter
// grammars (python/rust/go/bash/json/c/html/css/yaml/toml) before the first
// <code>/<markdown> mount initializes the global tree-sitter client.
registerVendoredParsers()
import type { SessionPickerOps } from '../view/overlays/sessionPicker.tsx'
import { ThemeProvider } from '../view/theme.tsx'
import { makeFakeGatewayLayer, type FakeGatewayController } from './fakeGateway.ts'
@ -174,7 +181,18 @@ const postSessionSetup = (gateway: GatewayServiceShape, store: SessionStore, sid
const createFreshSession = (gateway: GatewayServiceShape, store: SessionStore, input: TuiInput) =>
Effect.gen(function* () {
const created = yield* gateway.request<{ session_id?: string; info?: Record<string, unknown> }>('session.create', {
cols: input.cols
cols: input.cols,
// The launch directory IS the workspace choice in a terminal (you cd'd
// here) — passing it makes the gateway treat it as explicit, so the
// session row gets a persisted cwd on first message, the chrome bar shows
// the right dir, and /sessions groups this directory's sessions first.
// NOT process.cwd(): the hermes launcher runs this engine with cwd set to
// its own package dir (ui-opentui), so process.cwd() would be the engine
// dir. The launcher exports the REAL launch dir as HERMES_CWD / the
// gateway's TERMINAL_CWD; prefer those, falling back to process.cwd()
// only when launched standalone (smokes/dev). (Desktop omits cwd — its
// launch dir is meaningless; see _ensure_session_db_row.)
cwd: launchCwd()
})
const sid = created?.session_id ?? gateway.sessionId()
if (!sid) {
@ -542,6 +560,7 @@ export const run = Effect.fn('Tui.run')(function* (input: TuiInput) {
() => (
<KeymapProvider keymap={keymap}>
<ThemeProvider theme={() => store.state.theme}>
<TerminalChrome store={store} />
<App
store={store}
onSubmit={submit}

View file

@ -76,3 +76,28 @@ export function envComposerRows(value: string | undefined): number {
export function envOutputUnlimited(value: string | undefined): boolean {
return envOutputLines(value) === Number.POSITIVE_INFINITY
}
/**
* The session's launch directory for `session.create`'s `cwd` param.
*
* The hermes launcher runs the OpenTUI engine with its process cwd set to the
* engine's own package dir, so `process.cwd()` is NOT where the user ran
* hermes. The launcher exports the real launch dir as `HERMES_CWD` (and the
* gateway's `TERMINAL_CWD`); prefer those. Falls back to `process.cwd()` only
* for standalone launches (smokes/dev) where no launcher set them, and returns
* `undefined` when even that is empty so the gateway resolves its own default.
*/
export function launchCwd(env: { readonly [k: string]: string | undefined } = process.env): string | undefined {
// First NON-BLANK of the launcher's vars (?? would keep a blank HERMES_CWD
// and never reach TERMINAL_CWD).
for (const value of [env.HERMES_CWD, env.TERMINAL_CWD]) {
const trimmed = (value ?? '').trim()
if (trimmed) return trimmed
}
try {
const cwd = process.cwd().trim()
return cwd || undefined
} catch {
return undefined
}
}

View file

@ -163,6 +163,36 @@ export function filterSessions(query: string, rows: readonly SessionRow[]): Sess
return fuzzyFilter(query, rows, sessionFields)
}
// ── this-directory grouping ───────────────────────────────────────────────
/** Path equality for cwd grouping: trim + drop trailing slashes. Pure string
* work (no fs) — rows carry the gateway's already-absolute paths. */
export function normalizeCwd(path: string | undefined): string {
return (path ?? '').trim().replace(/\/+$/, '')
}
/** Display order with sessions started in the CURRENT directory first.
*
* Browse mode only: while a search query is active the fuzzy score owns the
* order (relevance beats locality), so `hereCount` is 0 and rows pass through.
* Stable within both groups (each keeps the gateway's recency order). The
* view renders section captions off `hereCount`; selection math is untouched
* because this just reorders the one flat list.
*/
export function orderRowsForCwd(
rows: SessionRow[],
currentCwd: string | undefined,
query: string
): { rows: SessionRow[]; hereCount: number } {
const here = normalizeCwd(currentCwd)
if (!here || query.trim()) return { hereCount: 0, rows }
const local: SessionRow[] = []
const elsewhere: SessionRow[] = []
for (const row of rows) (normalizeCwd(row.cwd) === here ? local : elsewhere).push(row)
if (!local.length) return { hereCount: 0, rows }
return { hereCount: local.length, rows: [...local, ...elsewhere] }
}
// ── key routing (pattern: completionMenu.ts routeMenuKey) ────────────────
export interface SessionPickerKeyContext {

View file

@ -181,6 +181,9 @@ export interface SessionInfo {
fast?: boolean
cwd?: string
branch?: string
/** Session title (auto-titled after the first exchange / renamed via the
* picker) — drives the terminal window-title chrome; unset until titled. */
title?: string
running?: boolean
contextUsed?: number
contextMax?: number
@ -324,6 +327,7 @@ function infoPatchFrom(d: SessionInfoPatchDecoded): Partial<SessionInfo> {
if (d.fast !== undefined) patch.fast = d.fast
if (d.cwd) patch.cwd = d.cwd
if (d.branch) patch.branch = d.branch
if (d.title) patch.title = d.title
if (d.running !== undefined) patch.running = d.running
// prefer the nested usage.context_* numbers, else the top-level fallback.
const used = d.usage?.context_used ?? d.context_used

View file

@ -0,0 +1,105 @@
/**
* Terminal chrome logic — window-title text and desktop-notification OSC
* sequences. Pure string work (no OpenTUI imports); the boundary shim
* (`boundary/termChrome.ts`) owns the renderer writes and focus tracking.
*
* Title: OSC 0/2 content is set natively via `renderer.setTerminalTitle`
* (the zig side emits the escape) — this module only SHAPES the text:
* `"{session title} — Hermes"` once the gateway titles the session,
* `"Hermes Agent"` until then.
*
* Notifications: emitted when the TUI starts waiting on the user (blocking
* prompt, turn complete). Three dialects, terminals ignore what they don't
* speak:
* OSC 9 `ESC ] 9 ; message BEL` (iTerm2 / ConEmu / wezterm)
* OSC 99 `ESC ] 99 ; i=hermes ; title ST` (kitty desktop-notification
* protocol — `p` defaults to title, `d` defaults to done)
* OSC 777 `ESC ] 777 ; notify ; title ; body BEL` (urxvt / foot)
*/
const ESC = '\u001b'
const BEL = '\u0007'
const ST = `${ESC}\\`
/** Strip control chars (C0/C1, incl. ESC/BEL) so user text can never
* terminate or splice an escape sequence; collapse runs of whitespace;
* cap the length. */
export function sanitizeOscText(text: string, max = 120): string {
const clean = (text ?? '')
// eslint-disable-next-line no-control-regex
.replace(/[\u0000-\u001f\u007f-\u009f]/g, ' ')
.replace(/\s+/g, ' ')
.trim()
return clean.length > max ? clean.slice(0, Math.max(1, max - 1)) + '…' : clean
}
/** The window-title string: session title when known, Ink-era generic otherwise. */
export function windowTitleFor(sessionTitle: string | undefined): string {
const title = sanitizeOscText(sessionTitle ?? '', 80)
return title ? `${title} — Hermes` : 'Hermes Agent'
}
/** A notification's two text parts (body optional). */
export interface TermNotification {
readonly title: string
readonly body?: string
}
/** The raw escape sequences announcing `n` to the hosting terminal. OSC 9 and
* 777 conventionally terminate with BEL; kitty's OSC 99 spec uses ST.
* Semicolons are swapped out of the OSC 777 fields (its field separator). */
export function notifySequences(n: TermNotification): string[] {
const title = sanitizeOscText(n.title)
const body = sanitizeOscText(n.body ?? '')
if (!title) return []
const combined = body ? `${title}: ${body}` : title
const f777 = (s: string) => s.replace(/;/g, ',')
return [
`${ESC}]9;${combined}${BEL}`,
`${ESC}]99;i=hermes;${combined}${ST}`,
`${ESC}]777;notify;${f777(title)};${f777(body || title)}${BEL}`
]
}
/** The XTWINOPS title-stack pushes/pops bracketing our title ownership: save
* the user's title on install, restore it on teardown (terminals without the
* stack ignore these — they just keep our last title, same as today). */
export const TITLE_STACK_SAVE = `${ESC}[22;0t`
export const TITLE_STACK_RESTORE = `${ESC}[23;0t`
/** What to announce for a blocking prompt, by kind. Kinds arrive from the
* store's ActivePrompt union; unknown kinds get the generic line so a new
* prompt type can never silently drop notifications. */
export function promptNotification(kind: string): TermNotification {
switch (kind) {
case 'clarify':
return { title: 'Hermes', body: 'needs an answer to continue' }
case 'approval':
return { title: 'Hermes', body: 'wants approval to run a command' }
case 'sudo':
return { title: 'Hermes', body: 'needs your sudo password' }
case 'secret':
return { title: 'Hermes', body: 'needs a secret/API key' }
case 'confirm':
return { title: 'Hermes', body: 'is asking you to confirm' }
default:
return { title: 'Hermes', body: 'is waiting for your input' }
}
}
/** Turn-complete announcement. */
export const TURN_COMPLETE_NOTIFICATION: TermNotification = {
title: 'Hermes',
body: 'finished — awaiting your input'
}
/**
* `HERMES_TUI_NOTIFY` kill-switch (TUI-only env var, same family as
* HERMES_TUI_TOOL_OUTPUT_LINES): unset/anything-else = on, `0`/`false`/`off`
* = no notification sequences are ever written. The window title is NOT
* gated by this — it's chrome, not interruption.
*/
export function notifyEnabled(env: { readonly [k: string]: string | undefined } = process.env): boolean {
const raw = (env.HERMES_TUI_NOTIFY ?? '').trim().toLowerCase()
return raw !== '0' && raw !== 'false' && raw !== 'off'
}

View file

@ -1,6 +1,6 @@
import { describe, expect, test } from 'vitest'
import { envFlag, envOutputLines, envOutputUnlimited } from '../logic/env.ts'
import { envFlag, envOutputLines, envOutputUnlimited, launchCwd } from '../logic/env.ts'
describe('envFlag', () => {
test('recognizes truthy values regardless of case/whitespace', () => {
@ -66,3 +66,18 @@ describe('envOutputLines (HERMES_TUI_TOOL_OUTPUT_LINES)', () => {
expect(envOutputUnlimited('200')).toBe(false)
})
})
describe('launchCwd (session.create cwd)', () => {
test('prefers HERMES_CWD (real launch dir the hermes launcher exports)', () => {
expect(launchCwd({ HERMES_CWD: '/home/u/proj', TERMINAL_CWD: '/other' })).toBe('/home/u/proj')
})
test('falls back to TERMINAL_CWD when HERMES_CWD is unset/blank', () => {
expect(launchCwd({ TERMINAL_CWD: '/home/u/wt' })).toBe('/home/u/wt')
expect(launchCwd({ HERMES_CWD: ' ', TERMINAL_CWD: '/home/u/wt' })).toBe('/home/u/wt')
})
test('falls back to process.cwd() (non-empty) when no launcher env set', () => {
expect(launchCwd({})).toBe(process.cwd())
})
})

View file

@ -0,0 +1,74 @@
/**
* Vendored tree-sitter grammar registration (syntax-highlighting language
* expansion). Layers:
* 1. config: every manifest grammar has valid vendored assets (wasm magic,
* non-empty query) and the built configs point at existing absolute paths.
* 2. resolution: core's filetype maps route our curated extensions/fence
* labels to the registered filetype ids.
* Visual color is live-smoke territory (highlighting settles async — see
* codeBlock.tsx); these tests pin the wiring that makes it possible.
*/
import { readFileSync } from 'node:fs'
import { isAbsolute } from 'node:path'
import { extToFiletype, infoStringToFiletype, pathToFiletype } from '@opentui/core'
import { describe, expect, test } from 'vitest'
import { registerVendoredParsers, vendoredParsers } from '../boundary/parsers.ts'
const EXPECTED = ['python', 'rust', 'go', 'bash', 'json', 'c', 'html', 'css', 'yaml', 'toml']
describe('vendored grammar configs', () => {
test('all 10 curated grammars resolve with existing absolute asset paths', () => {
const configs = vendoredParsers()
expect(configs.map(c => c.filetype).sort()).toEqual([...EXPECTED].sort())
for (const config of configs) {
expect(isAbsolute(config.wasm)).toBe(true)
expect(isAbsolute(config.queries.highlights[0]!)).toBe(true)
}
})
test('vendored wasm files carry the wasm magic; queries are non-empty', () => {
for (const config of vendoredParsers()) {
const wasm = readFileSync(config.wasm)
expect(wasm.subarray(0, 4).toString('latin1'), config.filetype).toBe('\0asm')
const query = readFileSync(config.queries.highlights[0]!, 'utf8')
expect(query.trim().length, config.filetype).toBeGreaterThan(0)
}
})
test('registerVendoredParsers registers and reports the full set', () => {
const registered = registerVendoredParsers()
expect(registered.map(r => r.filetype).sort()).toEqual([...EXPECTED].sort())
})
test('a missing assets dir degrades to empty (plain-text fallback), no throw', () => {
expect(vendoredParsers('/nonexistent/parsers')).toEqual([])
})
})
describe('filetype routing into the registered ids', () => {
test('tool-body path extensions resolve to curated filetypes', () => {
expect(pathToFiletype('a/b/script.py')).toBe('python')
expect(pathToFiletype('src/main.rs')).toBe('rust')
expect(pathToFiletype('cmd/main.go')).toBe('go')
expect(pathToFiletype('run.sh')).toBe('bash')
expect(pathToFiletype('conf.yaml')).toBe('yaml')
expect(pathToFiletype('conf.yml')).toBe('yaml')
expect(pathToFiletype('Cargo.toml')).toBe('toml')
expect(pathToFiletype('lib.c')).toBe('c')
expect(pathToFiletype('lib.h')).toBe('c')
expect(pathToFiletype('index.html')).toBe('html')
expect(pathToFiletype('style.css')).toBe('css')
expect(pathToFiletype('package.json')).toBe('json')
})
test('markdown fence labels resolve to curated filetypes (3b — injections)', () => {
expect(infoStringToFiletype('python')).toBe('python')
expect(infoStringToFiletype('py')).toBe('python')
expect(infoStringToFiletype('rust')).toBe('rust')
expect(infoStringToFiletype('sh')).toBe('bash')
expect(infoStringToFiletype('yaml title=x.yml')).toBe('yaml')
expect(extToFiletype('zsh')).toBe('bash')
})
})

View file

@ -22,7 +22,9 @@ import {
SESSION_TABS,
tabAccepts,
tailTruncate,
type SessionRow
type SessionRow,
normalizeCwd,
orderRowsForCwd
} from '../logic/sessionPicker.ts'
const row = (over: Partial<SessionRow>): SessionRow => ({
@ -254,3 +256,28 @@ describe('slash entry points', () => {
expect(resolveSessionArg(rows, '')).toBeUndefined()
})
})
describe('orderRowsForCwd — pure edges', () => {
const row = (id: string, cwd?: string) =>
({ cwd, id, lastActive: 0, messageCount: 0, preview: '', source: 'tui', startedAt: 0, title: id }) as never
test('normalizeCwd trims and strips trailing slashes; empty-safe', () => {
expect(normalizeCwd(' /a/b// ')).toBe('/a/b')
expect(normalizeCwd(undefined)).toBe('')
expect(normalizeCwd(' ')).toBe('')
})
test('identity passthrough when nothing matches or no cwd known', () => {
const rows = [row('a', '/x'), row('b')]
expect(orderRowsForCwd(rows, undefined, '')).toEqual({ hereCount: 0, rows })
expect(orderRowsForCwd(rows, '/nowhere', '')).toEqual({ hereCount: 0, rows })
expect(orderRowsForCwd(rows, '/x', 'query')).toEqual({ hereCount: 0, rows })
})
test('stable partition: here first, recency kept within groups', () => {
const rows = [row('a', '/y'), row('b', '/x'), row('c'), row('d', '/x/')]
const out = orderRowsForCwd(rows, '/x', '')
expect(out.hereCount).toBe(2)
expect(out.rows.map(r => (r as { id: string }).id)).toEqual(['b', 'd', 'a', 'c'])
})
})

View file

@ -74,6 +74,8 @@ interface MountOptions {
sessions?: FakeSession[]
truncated?: boolean
initialTab?: 'recent' | 'cron' | 'gateways' | 'all'
/** The TUI's cwd — drives the this-directory-first grouping. */
currentCwd?: string
/** Per-id peek delay (ms) — drives the stale-cancellation test. */
peekDelay?: (id: string) => number
/** Paged list: serve `sessions` windowed by offset/limit instead of whole. */
@ -118,6 +120,7 @@ async function mountPicker(options: MountOptions = {}): Promise<Harness> {
<SessionPicker
ops={ops}
initialTab={options.initialTab ?? 'recent'}
currentCwd={() => options.currentCwd}
peekDebounceMs={20}
onResume={id => resumed.push(id)}
onClose={() => (closed.value = true)}
@ -356,3 +359,65 @@ describe('SessionPicker — pagination', () => {
}
})
})
describe('this-directory grouping', () => {
const CWD = '/home/u/projects/alpha'
const GROUPED: FakeSession[] = [
{ cwd: '/elsewhere/beta', id: 'g1', last_active: NOW_S - 60, message_count: 5, source: 'tui', title: 'beta work' },
{ cwd: CWD, id: 'g2', last_active: NOW_S - 120, message_count: 8, source: 'tui', title: 'alpha here' },
{ id: 'g3', last_active: NOW_S - 180, message_count: 2, source: 'cli', title: 'no cwd at all' },
{
cwd: CWD + '/',
id: 'g4',
last_active: NOW_S - 240,
message_count: 4,
source: 'tui',
title: 'alpha trailing slash'
}
]
test('sessions started in the current cwd group first under a caption', async () => {
const h = await mountPicker({ currentCwd: CWD, sessions: GROUPED })
try {
const frame = h.probe.frame()
const at = (needle: string) => frame.indexOf(needle)
expect(at('▾ this directory (2)')).toBeGreaterThanOrEqual(0)
expect(at('▾ other directories')).toBeGreaterThan(at('▾ this directory (2)'))
// here-rows (incl. trailing-slash normalization) above the caption split,
// elsewhere rows below it; selection starts on the first here-row.
expect(at('alpha here')).toBeLessThan(at('▾ other directories'))
expect(at('alpha trailing slash')).toBeLessThan(at('▾ other directories'))
expect(at('beta work')).toBeGreaterThan(at('▾ other directories'))
expect(at('no cwd at all')).toBeGreaterThan(at('▾ other directories'))
expect(frame).toContain('❯ alpha here')
// Enter resumes the first here-session
h.probe.keys.pressEnter()
await h.probe.settle()
expect(h.resumed).toEqual(['g2'])
} finally {
h.probe.destroy()
}
})
test('search drops the grouping (fuzzy relevance owns the order)', async () => {
const h = await mountPicker({ currentCwd: CWD, sessions: GROUPED })
try {
await h.probe.keys.typeText('beta')
await h.probe.settle()
const frame = await h.probe.waitForFrame(f => f.includes('beta work'))
expect(frame).not.toContain('▾ this directory')
expect(frame).not.toContain('▾ other directories')
} finally {
h.probe.destroy()
}
})
test('no current cwd (or no matches) → plain recency list, no captions', async () => {
const h = await mountPicker({ sessions: GROUPED })
try {
expect(h.probe.frame()).not.toContain('▾ this directory')
} finally {
h.probe.destroy()
}
})
})

View file

@ -0,0 +1,149 @@
/**
* Terminal chrome: OSC 0/2 window title + OSC 9/99/777 waiting-on-you
* notifications. Layers:
* 1. pure: title shaping, OSC sanitization, the three notification
* dialect sequences, the prompt-kind copy, the env kill-switch
* (logic/termChrome.ts).
* 2. wiring: <TerminalChrome> with an injected fake seam over a real
* store — the title tracks session.info, prompts and turn-completion
* edges notify exactly once, initial state stays silent.
*/
import { createRoot } from 'solid-js'
import { describe, expect, test } from 'vitest'
import type { TerminalChromeSeam } from '../boundary/termChrome.ts'
import { createSessionStore } from '../logic/store.ts'
import {
notifyEnabled,
notifySequences,
promptNotification,
sanitizeOscText,
TURN_COMPLETE_NOTIFICATION,
windowTitleFor
} from '../logic/termChrome.ts'
import { TerminalChrome } from '../view/terminalChrome.tsx'
const ESC = '\u001b'
const BEL = '\u0007'
describe('windowTitleFor — title shaping', () => {
test('generic until the session is titled', () => {
expect(windowTitleFor(undefined)).toBe('Hermes Agent')
expect(windowTitleFor('')).toBe('Hermes Agent')
expect(windowTitleFor(' ')).toBe('Hermes Agent')
})
test('session title gets the — Hermes suffix', () => {
expect(windowTitleFor('fix the flaky tests')).toBe('fix the flaky tests — Hermes')
})
test('long titles are capped', () => {
const long = 'x'.repeat(200)
expect(windowTitleFor(long).length).toBeLessThanOrEqual(80 + ' — Hermes'.length)
expect(windowTitleFor(long)).toContain('…')
})
})
describe('sanitizeOscText — escape-splice safety', () => {
test('control chars (incl. ESC/BEL) can never splice a sequence', () => {
expect(sanitizeOscText(`evil${ESC}]0;pwned${BEL}title`)).toBe('evil ]0;pwned title')
})
test('whitespace collapses; ends trimmed', () => {
expect(sanitizeOscText(' a\n\tb ')).toBe('a b')
})
})
describe('notifySequences — the three dialects', () => {
test('emits OSC 9, kitty OSC 99, and OSC 777', () => {
const [osc9, osc99, osc777] = notifySequences({ title: 'Hermes', body: 'finished' })
expect(osc9).toBe(`${ESC}]9;Hermes: finished${BEL}`)
expect(osc99).toBe(`${ESC}]99;i=hermes;Hermes: finished${ESC}\\`)
expect(osc777).toBe(`${ESC}]777;notify;Hermes;finished${BEL}`)
})
test('semicolons cannot splice OSC 777 fields', () => {
const [, , osc777] = notifySequences({ title: 'a;b', body: 'c;d' })
expect(osc777).toBe(`${ESC}]777;notify;a,b;c,d${BEL}`)
})
test('an empty title produces nothing', () => {
expect(notifySequences({ title: ' ' })).toEqual([])
})
})
describe('promptNotification + env gate', () => {
test('every known prompt kind has copy; unknown kinds fall back', () => {
for (const kind of ['clarify', 'approval', 'sudo', 'secret', 'confirm', 'someday-new']) {
const n = promptNotification(kind)
expect(n.title).toBe('Hermes')
expect(n.body).toBeTruthy()
}
expect(promptNotification('someday-new').body).toBe('is waiting for your input')
})
test('HERMES_TUI_NOTIFY=0/false/off disables; default on', () => {
expect(notifyEnabled({})).toBe(true)
expect(notifyEnabled({ HERMES_TUI_NOTIFY: '1' })).toBe(true)
expect(notifyEnabled({ HERMES_TUI_NOTIFY: '0' })).toBe(false)
expect(notifyEnabled({ HERMES_TUI_NOTIFY: 'false' })).toBe(false)
expect(notifyEnabled({ HERMES_TUI_NOTIFY: 'off' })).toBe(false)
})
})
describe('<TerminalChrome> wiring — store edges drive the seam', () => {
function mount() {
const store = createSessionStore()
const titles: Array<string | undefined> = []
const notifications: string[] = []
const seam: TerminalChromeSeam = {
setTitle: t => titles.push(t),
notify: n => notifications.push(n.body ?? n.title)
}
const dispose = createRoot(d => {
TerminalChrome({ chrome: seam, store })
return d
})
return { dispose, notifications, store, titles }
}
test('sets the generic title immediately, then tracks session.info title', () => {
const { dispose, store, titles } = mount()
try {
expect(titles).toEqual([undefined]) // boot → windowTitleFor(undefined) inside the seam
store.apply({ type: 'session.info', payload: { title: 'rename the moon' } })
expect(titles).toEqual([undefined, 'rename the moon'])
} finally {
dispose()
}
})
test('a blocking prompt notifies once; initial state is silent', () => {
const { dispose, notifications, store } = mount()
try {
expect(notifications).toEqual([])
store.apply({
type: 'clarify.request',
payload: { choices: null, question: 'which one?', request_id: 'r1' }
})
expect(notifications).toEqual(['needs an answer to continue'])
// clearing the prompt does not notify again
store.clearPrompt()
expect(notifications).toEqual(['needs an answer to continue'])
} finally {
dispose()
}
})
test('turn completion (running true→false) notifies; boot idle does not', () => {
const { dispose, notifications, store } = mount()
try {
store.apply({ type: 'message.start' })
expect(notifications).toEqual([])
store.apply({ type: 'message.complete', payload: { text: 'done' } })
expect(notifications).toEqual([TURN_COMPLETE_NOTIFICATION.body])
} finally {
dispose()
}
})
})

View file

@ -156,6 +156,7 @@ export function App(props: AppProps) {
<SessionPicker
ops={props.sessionOps ?? NOOP_OPS}
initialTab={sp().tab}
currentCwd={() => props.store.state.info.cwd}
onResume={resume}
onClose={closeSessionPicker}
/>

View file

@ -34,6 +34,7 @@ import { visibleRows, type PickerRow } from '../../logic/fuzzy.ts'
import {
filterSessions,
listParamsFor,
orderRowsForCwd,
mapSessionRows,
relativeTime,
routeSessionPickerKey,
@ -79,6 +80,9 @@ export function SessionPicker(props: {
onResume: (sessionId: string) => void
onClose: () => void
initialTab?: SessionTabId
/** The TUI's working directory — sessions started here group first while
* browsing (no effect during search). */
currentCwd?: () => string | undefined
/** Test seam: override the peek debounce (default PEEK_DEBOUNCE_MS). */
peekDebounceMs?: number
}) {
@ -134,7 +138,12 @@ export function SessionPicker(props: {
})
)
const filtered = createMemo(() => filterSessions(query(), rows()))
// Display order: this-directory sessions first while browsing (design ask
// 2026-06-12); search keeps pure fuzzy relevance. One flat list, so the
// selection/windowing math below is order-agnostic.
const ordered = createMemo(() => orderRowsForCwd(filterSessions(query(), rows()), props.currentCwd?.(), query()))
const filtered = createMemo(() => ordered().rows)
const hereCount = createMemo(() => ordered().hereCount)
createEffect(on(query, () => setSel(0), { defer: true }))
// "load more" pseudo-row: selectable index === filtered().length. Offered
@ -371,6 +380,12 @@ export function SessionPicker(props: {
{row =>
row.kind === 'item' && row.item ? (
<box style={{ flexDirection: 'column' }} onMouseDown={() => setSel(row.index)}>
<Show when={hereCount() > 0 && row.index === 0}>
<text fg={theme().color.muted}>{` ▾ this directory (${hereCount()})`}</text>
</Show>
<Show when={hereCount() > 0 && row.index === hereCount()}>
<text fg={theme().color.muted}>{' ▾ other directories'}</text>
</Show>
<text bg={row.index === sel() ? theme().color.selectionBg : 'transparent'}>
<span style={{ fg: row.index === sel() ? theme().color.text : theme().color.muted }}>
{row.index === sel() ? '❯ ' : ' '}

View file

@ -0,0 +1,61 @@
/**
* <TerminalChrome> — render-nothing wiring between store state and the
* terminal-chrome seam (window title + waiting-on-you notifications).
*
* title: tracks `info.title` — "Hermes Agent" until the gateway titles
* the session (first-exchange auto-title / picker rename), then
* "{title} — Hermes". Runs immediately on mount so the generic
* title is set at boot.
* notify: fires on the EDGES the user must act on —
* · a blocking prompt appearing (clarify/approval/sudo/secret/
* confirm — `state.prompt` undefined → defined), and
* · a turn finishing (`info.running` true → false).
* Both deferred (no notification for the initial state) and
* de-duplicated by the seam's focus gate.
*
* The seam is injectable for headless tests; production resolves it from
* the live renderer via useRenderer().
*/
import { useRenderer } from '@opentui/solid'
import { createEffect, on } from 'solid-js'
import { installTerminalChrome, type TerminalChromeSeam } from '../boundary/termChrome.ts'
import type { createSessionStore } from '../logic/store.ts'
import { promptNotification, TURN_COMPLETE_NOTIFICATION } from '../logic/termChrome.ts'
type Store = ReturnType<typeof createSessionStore>
export function TerminalChrome(props: { store: Store; chrome?: TerminalChromeSeam }) {
// Injected seam (tests) skips useRenderer() — headless mounts have no
// opentui context to read.
const chrome = props.chrome ?? installTerminalChrome(useRenderer())
// Window title — immediate (sets the generic title at boot) and reactive.
createEffect(() => {
chrome.setTitle(props.store.state.info.title)
})
// Blocking prompt appeared → the agent is waiting on the user.
createEffect(
on(
() => props.store.state.prompt,
(prompt, previous) => {
if (prompt && !previous) chrome.notify(promptNotification(prompt.kind))
},
{ defer: true }
)
)
// Turn finished → control is back with the user.
createEffect(
on(
() => props.store.state.info.running,
(running, previous) => {
if (previous === true && running === false) chrome.notify(TURN_COMPLETE_NOTIFICATION)
},
{ defer: true }
)
)
return null
}