agentgui 1.0.1021 → 1.0.1023

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/.gm/disciplines/agentgui/memories/.flat-export-done +0 -0
  2. package/.gm/memories/.flat-export-done +1 -1
  3. package/.gm/memories/mem-0131ff4869d86085-370.md +10 -0
  4. package/.gm/memories/mem-08a4a9e050a4deff-3048.md +8 -0
  5. package/.gm/memories/mem-09597ee0df9c9224-965.md +8 -0
  6. package/.gm/memories/mem-0993d156f571b619-1059.md +8 -0
  7. package/.gm/memories/mem-0d1408fa048b9923-1159.md +8 -0
  8. package/.gm/memories/mem-1781870426883-0-2340.md +8 -0
  9. package/.gm/memories/mem-1781943679146-0-1577.md +8 -0
  10. package/.gm/memories/mem-1781943722930-1-2318.md +8 -0
  11. package/.gm/memories/mem-1781943733304-2-2135.md +8 -0
  12. package/.gm/memories/mem-1781946701659-3-1665.md +8 -0
  13. package/.gm/memories/mem-1781952323283-0-2146.md +8 -0
  14. package/.gm/memories/mem-1781961165110-1-2041.md +8 -0
  15. package/.gm/memories/mem-1781966495635-2-1606.md +8 -0
  16. package/.gm/memories/mem-39a7cc2f887a90b1-1079.md +8 -0
  17. package/.gm/memories/mem-4bafafa47a0e4b6c-3237.md +8 -0
  18. package/.gm/memories/mem-4c06c45b4d31bc60-1202.md +8 -0
  19. package/.gm/memories/mem-4ecd3b713e384170-122.md +10 -0
  20. package/.gm/memories/mem-51d8992a3c6f6c9c-1073.md +8 -0
  21. package/.gm/memories/mem-539eee7e8c6c10ee-2557.md +8 -0
  22. package/.gm/memories/mem-6224f8d185797f2b-1047.md +8 -0
  23. package/.gm/memories/mem-66b0947b946f3437-1155.md +8 -0
  24. package/.gm/memories/mem-6bdf783a1f6be7c2-1062.md +8 -0
  25. package/.gm/memories/mem-70d96acb736af2d7-685.md +8 -0
  26. package/.gm/memories/mem-7535f34f430fc196-1340.md +8 -0
  27. package/.gm/memories/mem-861a588d2773681a-1311.md +8 -0
  28. package/.gm/memories/mem-9329105b05b9c0f6-324.md +10 -0
  29. package/.gm/memories/mem-94f30cec3c283f97-782.md +10 -0
  30. package/.gm/memories/mem-956d4813319f65a3-1710.md +8 -0
  31. package/.gm/memories/mem-a52933cd6495e6f5-2624.md +8 -0
  32. package/.gm/memories/mem-a95b1d49d71133ce-1007.md +8 -0
  33. package/.gm/memories/mem-aaeae38d8eb01809-1076.md +8 -0
  34. package/.gm/memories/mem-b06b8c85b81663e6-1250.md +8 -0
  35. package/.gm/memories/mem-bdf466854032cac9-1509.md +8 -0
  36. package/.gm/memories/mem-cda639547932c796-713.md +8 -0
  37. package/.gm/memories/mem-cf8472c84d9e04c4-1883.md +8 -0
  38. package/.gm/memories/mem-d060480435f92eec-1146.md +8 -0
  39. package/.gm/memories/mem-d347c921b79e6d44-1099.md +8 -0
  40. package/.gm/memories/mem-d3c8ea6c861bfbb8-1457.md +8 -0
  41. package/.gm/memories/mem-d5e928c552cd87f9-214.md +10 -0
  42. package/.gm/memories/mem-d63bc3b8a0269844-406.md +10 -0
  43. package/.gm/memories/mem-da1c91acf63d81d3-863.md +8 -0
  44. package/.gm/memories/mem-dceeae20bc56d407-711.md +8 -0
  45. package/.gm/memories/mem-df74674eec7745ac-178.md +10 -0
  46. package/.gm/memories/mem-e1d5431ae2542872-961.md +8 -0
  47. package/.gm/memories/mem-ebbe6c0694c526f0-1392.md +8 -0
  48. package/.gm/memories/mem-ebf85becc23410b0-1633.md +8 -0
  49. package/.gm/memories/mem-ee8858162518f144-1252.md +8 -0
  50. package/.gm/memories/mem-f3f317c083d979ee-1210.md +8 -0
  51. package/.gm/memories/mem-f786bc1626d92845-204.md +10 -0
  52. package/.gm/memories/mem-ff8669e527e5c427-2788.md +8 -0
  53. package/.gm/prd.yml +96 -0
  54. package/AGENTS.md +4 -0
  55. package/package.json +1 -1
  56. package/site/app/index.html +4 -0
  57. package/site/app/js/app.js +56 -28
  58. package/site/app/vendor/anentrypoint-design/247420.css +1286 -213
  59. package/site/app/vendor/anentrypoint-design/247420.js +32 -33
@@ -0,0 +1,10 @@
1
+ ---
2
+ key: mem-f786bc1626d92845-204
3
+ ns: default
4
+ created: 1781783463123
5
+ updated: 1781783463123
6
+ ---
7
+
8
+ ## Resolved mutable: server-3009-state
9
+
10
+ ss/netstat | grep 3009 = no listener; ps aux | grep -E 'bun server|node server' = no process. Must start PORT=3009 bun server.js (run_in_background) before witness.
@@ -0,0 +1,8 @@
1
+ ---
2
+ key: mem-ff8669e527e5c427-2788
3
+ ns: default
4
+ created: 1781786013354
5
+ updated: 1781786013354
6
+ ---
7
+
8
+ agentgui 14th run (2026-06-18): design-maturity sweep + LAST residue (site/theme.mjs, the flatspace marketing renderer - NOT the SPA). theme.mjs had inline <style>+hex(#FBF6EB/#1F1B16), ~12 inline style=, deprecated C.Btn({primary}), 2x ↗ glyphs - ALL migrated to a NEW kit marketing.css .site-* family (.site-panel/.site-hero/.site-hero-h/.site-hero-body/.site-chip-row/.site-cta-row/.site-cli+.cli/.prompt/.cmd/.site-embed, pre-scoped .ds-247420, wired into build.mjs cssParts + lint-glyphs SCAN_ROOT_FILES). KEY: kit Panel/Heading only accepted `style` not `class` - added a `class` prop to both (content.js Panel, shell.js Heading) so consumers attach classes. Btn already had `variant` (primary->variant:'primary'). theme.mjs head now render-blocking-<link>s 247420.css (was JS-importmap-only, so inline <style> was its FOUC guard); kit already owns base surface at app-surfaces.css:27 '.ds-247420 body{background:var(--bg);color:var(--fg);font-family:var(--ff-display)}'. flatspace is NOT a dep on this env; theme.mjs is a pure render fn. Marketing tracks unpkg@latest, SPA ships pinned vendored dist (intentional). Workflow gui-design-14 (Workflow tool, NOT a .claude/workflows file this time - inline script): 46 agents, 39 findings, 28 confirmed (PUNCHLIST-DESIGN-14.md). Landed in kit: chat ThinkingNode base CSS (.chat-thinking* had zero base rules outside .agentchat-working), markdown table/th/td/hr (unstyled), dead .chat-tick rule, composer toolbar 32px/--r-1 (was 40px-round vs 36px-square), flat .chat-md rhythm, .chat-stream-pre chrome, tool/code :has() measure-breakout; files FileSkeleton container-bar->inner .ds-skel only, filter folded into one .ds-file-controls row, per-type cell tints, dead data-columns removed, .ds-file-check dead decls; sessions stale!=connecting disc tones+shapes, dead dup .ds-dash-status.is-running removed (running unified --accent), .seg.is-idle weight 600, reduced-motion live-disc static ring, mixed-tick 2px, cost as .ds-dash-stat-cost, conv-list unread hollow ring; shell resizers hidden past staging breakpoints (@media 1480/1100/900 - were dead handles), coarse 16px target, WS_RESIZE_CLAMP reconciled to CSS clamp, localStorage on pointerup not per-move, separator aria-valuemin/max/now; a11y voice vx-* reduced-motion guard, status discs non-colour SHAPE channels (live solid+pulse/error solid+halo/connecting hollow/stale muted), .ds-input-bare :focus->:focus-visible. Server PORT=3009 PASSWORD=123,slam,123,slam; run_in_background:true keeps it alive (setsid/nohup detached got exit 144 sandbox-killed). Witnessed via plugkit browser verb ?token=: 0 errors, body painted by kit (rgb 19,19,24 dark), 3 resizers, no h-scroll. Kit pushed 3704876 (rebased onto concurrent CI v0.0.211 bump); agentgui re-vendored dist + theme.mjs.
package/.gm/prd.yml CHANGED
@@ -2454,3 +2454,99 @@
2454
2454
  title: 'Verify zero legacy timestamp-keyed md files, flat JSON dirs byte-unchanged (23 flat: 22 default + 1 agentgui), engine writes md not flat JSON'
2455
2455
  witness: 'exec_js 9015: md 23 (bad []), discipline md 1 (dbad []), flat_default 22 + flat_agentgui 1 byte-unchanged (flat_bytes 1352); md body embeds flat bytes verbatim under frontmatter; engine wrote only md on both memorize-fires'
2456
2456
  status: completed
2457
+ - id: maxgui-run31-fanout-audit
2458
+ subject: Run maximal-effort multi-lens GUI critique workflow (usability, practicality, predictability, negative space, visual polish) across agentgui + kit
2459
+ witness: gui-ux-craft workflow (32 agents, 19 confirmed) + targeted negative-space live-browser audit agent both completed; findings triaged and highest-value fixes landed (ConfirmDialog destructive variant, settings double-gap, caps-label drift, copy-tone sidebar/external-session, mobile clipping x2, empty-state hierarchy, projects-folder copy btn)
2460
+ status: completed
2461
+ - id: maxgui-run31-verify-browser
2462
+ subject: Live-browser witness every landed fix at desktop+mobile widths, 0 console errors, screenshots before/after
2463
+ witness: 'Live browser session against localhost:3010/gm/ (no-PASSWORD instance) at 1280px+420px: settings panel gaps [24,24]px consistent (was 56px), history-empty title 18px > sub 16px (was inverted), delete ConfirmDialog button class btn-primary danger (was primary), PageHeader dense lede + chat composer-context wrap normally at 420px scrollWidth==clientWidth (was ellipsis-clipped), files checkbox select-all/individual/clear-selection + BulkBar functional after FileRow prop rename, 0 console errors throughout every navigation'
2464
+ status: completed
2465
+ - id: maxgui-run31-push-ci
2466
+ subject: Commit+push kit and app changes, watch CI green, write .ci-validated
2467
+ witness: standing CONSOLIDATE gate requirement
2468
+ status: pending
2469
+ - id: fix-chat-identity-repeat-mobile-clip
2470
+ subject: Chat tab identity repeated 3x (agent-chat.js h1 + composer context); mobile 420px composer-context clips text (278 vs 337 scrollWidth). Simplify duplication, fix clipping.
2471
+ witness: composer-context now wraps at <=480px instead of clipping (chat.css); browser-witnessed at 420px scrollWidth==clientWidth==278, text fully visible
2472
+ status: completed
2473
+ - id: fix-page-header-dense-lede-clip
2474
+ subject: ds-page-header-dense-lede overflow:hidden white-space:nowrap clips ~48-49% of text on History/Settings/Files/Live at 420px. Wrap or truncate gracefully.
2475
+ witness: app-shell.css lede wraps at <=480px instead of ellipsis-clipping; browser-witnessed settings/history/files/live all scrollWidth==clientWidth==296 at 420px, whiteSpace normal
2476
+ status: completed
2477
+ - id: fix-settings-projects-folder-copy-btn
2478
+ subject: Settings projects-folder fact lacks copy button unlike sibling root rows; clips at 420px with no recovery path. Add copy button matching root rows.
2479
+ witness: added onCopy to projects-folder fact in app.js serverPanel matching root rows
2480
+ status: completed
2481
+ - id: fix-search-filter-vocab-inconsistency
2482
+ subject: Inconsistent Search vs Filter vocabulary for identical client-side list-narrowing across sessions/files panels. Pick one term, apply consistently.
2483
+ witness: 'Investigated: search (762/814) is real server-side full-text search (2+ char, debouncedSearch); filter (files 1362/live 1730) is client-side substring narrowing of already-visible list. Different mechanics, correctly different verbs - not a bug. Resolved as reviewed-not-a-defect.'
2484
+ status: completed
2485
+ - id: fix-history-empty-state-hierarchy
2486
+ subject: History empty-state sub-line (.lede, 21px) renders larger than its own title (16.8px), inverting visual hierarchy. Use correct secondary-text style.
2487
+ witness: removed misapplied .lede class from history-empty-sub, fixed history-empty-title hardcoded 1.05rem to var(--fs-lg) token; browser-witnessed title 18px > sub 16px
2488
+ status: completed
2489
+ - id: wf-confirmdialog-destructive-danger
2490
+ subject: ConfirmDialog destructive prop was dead code; confirm button always rendered primary
2491
+ witness: landed and browser-witnessed live in this same run
2492
+ status: completed
2493
+ - id: wf-settings-double-gap-56px
2494
+ subject: Settings stacked panels had 56px gap (32px panel margin + 24px grid gap) vs 24px everywhere else
2495
+ witness: design/app-surfaces.css:161-166 .settings-grid > .panel margin-bottom:0 added; browser exec_js on live localhost:3010/gm/#tab=settings 1280px measured column-1 gaps [24,24]px (was 56px before fix)
2496
+ status: completed
2497
+ - id: wf-caps-label-size-drift
2498
+ subject: panel-head and table th used --fs-xs (14px) vs canonical .t-label --fs-tiny (13px) for same small-caps role
2499
+ witness: design/app-shell.css:583 panel-head font-size var(--fs-tiny) (was var(--fs-xs)), :898 table th font-size var(--fs-tiny) (was var(--fs-xs)); matches .t-label:76 canonical usage; kit build 0 lint errors
2500
+ status: completed
2501
+ - id: wf-copy-tone-sidebar-and-external-session
2502
+ subject: Error copy said sidebar (nowhere else used) instead of rail; agent label external session leaked internal jargon
2503
+ witness: agentgui/site/app/js/app.js:3470 changed retry via sidebar -> retry via the rail (matches rail vocabulary used at comments 291/345/594/683/818); app.js:1662 agent:external session -> agent:other tool
2504
+ status: completed
2505
+ - id: wf-copy-tone-conversation-vs-session
2506
+ subject: Same concept named conversation in rail/history and session in chat/live copy
2507
+ witness: agentgui/site/app/js/app.js:1547,1919-1923,2038,2666,2679 renamed session->conversation in all user-facing copy (resume banner, bulk-stop toast, export label, copy-id button, empty-state text)
2508
+ status: completed
2509
+ - id: wf-copy-tone-raw-sid-resume-banner
2510
+ subject: Resume banner surfaces raw truncated sid instead of human label
2511
+ witness: agentgui/site/app/js/app.js:2915-2921 new resumeSidLabel() helper (session title/project lookup, sid-slice fallback) wired into resume-banner text and follow-up chip text
2512
+ status: completed
2513
+ - id: wf-copy-tone-doubled-hyphen
2514
+ subject: Two string-composition styles both use inline hyphen separator, risking doubled hyphen
2515
+ witness: agentgui/site/app/js/app.js:1917-1923 resume-note now renders as a second .sub span instead of string-concatenated via hyphen delimiter
2516
+ status: completed
2517
+ - id: wf-perf-prism-preload
2518
+ subject: Prism CDN fetch has no resource hint unlike marked/dompurify
2519
+ witness: agentgui/site/app/index.html new link rel=preload as=script crossorigin for prism-core.min.js, matching design/src/highlight.js:9 PRISM_BASE URL
2520
+ status: completed
2521
+ - id: wf-perf-search-skeleton-flash
2522
+ subject: Search-mode ConversationList blows away visible results to skeleton on every debounced re-search
2523
+ witness: design/src/components/sessions.js:80 loading branch condition changed to (loading && !sessions.length), matches design/src/components/files.js:198 sibling pattern
2524
+ status: completed
2525
+ - id: wf-perf-file-mutation-roundtrip
2526
+ subject: Rename/delete/mkdir/bulk ops wait a second full dir fetch before row updates
2527
+ witness: agentgui/site/app/js/app.js runBulkDelete/runBulkMove splice completed entries from state.files.entries + render() immediately, loadDir() now fire-and-forget background reconcile instead of awaited before render; browser-witnessed select/deselect/BulkBar flow at localhost:3010/gm/#tab=files, 0 console errors
2528
+ status: completed
2529
+ - id: wf-perf-history-refetch
2530
+ subject: Every History tab entry unconditionally refetches full session list
2531
+ witness: agentgui/site/app/js/app.js:296 refreshHistory() now gated on !_historyLoadedOnce || staleness>5000ms; refreshHistory() stamps state._historyLoadedAt on success at line ~3339
2532
+ status: completed
2533
+ - id: wf-kit-filerow-move-undocumented
2534
+ subject: FileRow move action missing from COMPONENT_API.md docs
2535
+ witness: design/COMPONENT_API.md:112-113 added FileRow onAction enum line documenting download/rename/move/delete
2536
+ status: completed
2537
+ - id: wf-kit-readme-agentchat-flagship
2538
+ subject: README quickstart/feature list omits AgentChat/WorkspaceShell flagship surfaces
2539
+ witness: design/README.md what-in-the-box section adds multi-agent chat shell bullet (WorkspaceShell/WorkspaceRail/ConversationList/AgentChat/SessionDashboard) between the chat and file-browser bullets
2540
+ status: completed
2541
+ - id: wf-kit-freddie-exports-scope-note
2542
+ subject: components.js re-exports ~18 freddie.js page-builder functions with no doc of what/why
2543
+ witness: design/COMPONENT_API.md Overview section adds one-paragraph freddie.js re-export scope note (demo-site page builders, not general components)
2544
+ status: completed
2545
+ - id: wf-tag-family-perm-tag-tone
2546
+ subject: ds-file-perm-tag is-noaccess used bespoke border-mix instead of shared flame tone tokens
2547
+ witness: design/app-shell.css:1581-1585 ds-file-perm-tag.is-noaccess border-color changed from color-mix 40pct flame to solid var(--flame), plus doc comment on the 4-family tag-shape rule (badge/chip/perm-tag/pill)
2548
+ status: completed
2549
+ - id: wf-filerow-marked-selected-rename
2550
+ subject: FileRow native selection prop names (marked/onMark) diverge from shared selected/onToggleSelect contract
2551
+ witness: design/src/components/files.js FileRow+FileCell params renamed marked/onMark->selected/onToggleSelect internally; FileGrid own marked/onMark stay as documented public compat alias (COMPONENT_API.md:112); kit build 0 lint errors; browser-witnessed checkbox select-all/individual/clear-selection + BulkBar flow at localhost:3010/gm/#tab=files, 0 console errors
2552
+ status: completed
package/AGENTS.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # AgentGUI — Agent Notes
2
2
 
3
+ ## Maximal usability/negative-space/predictability sweep (2026-07-10) — thirty-first run
4
+
5
+ User asked to "max the gui usability practicality... predictable, good looking, use negative space properly" via wfgy-method-grade critique. Ran a fresh `gui-ux-craft` workflow (32 agents, 19 confirmed) plus a targeted live-browser negative-space audit agent in parallel, then landed the highest-value findings from both. Kit (`../design`): ConfirmDialog `destructive` prop was dead code (delete confirms always rendered `primary`, never `danger`) — real predictability bug, fixed + browser-witnessed the delete dialog button now carries `btn-primary danger`. Settings panels had a 56px gap between stacked Panels (32px `.panel` margin + 24px grid gap stacking) vs 24px everywhere else — zeroed the margin inside `.settings-grid`, witnessed 24px consistent. `.ds-page-header-dense-lede` and `.chat-composer-context` both hard-clipped with `nowrap`+ellipsis at <=480px with no way to recover the text — both now wrap at that breakpoint (kept desktop's single-line ellipsis). History empty-state sub-line used the misapplied `.lede` (fs-xl) style, rendering LARGER than its own title — removed the misapplied class + fixed the title's own hardcoded `1.05rem` to `var(--fs-lg)` token. Small-caps section labels (`panel-head`, `table th`) drifted from the canonical `.t-label` size (`--fs-xs` vs `--fs-tiny`) — unified. `FileRow`/`FileCell` internal selection props renamed `marked/onMark` -> `selected/onToggleSelect` to match the shared multi-select contract (FileGrid's own `marked`/`onMark` stay as its documented public alias). `ConversationList` search-mode blew away already-visible rows to a full skeleton on every debounced re-search — guarded on `!sessions.length`, matching `files.js`'s sibling pattern. App copy-tone: "sidebar" (used nowhere else) -> "the rail"; internal-jargon `agent: 'external session'` -> `'other tool'`; "session" -> "conversation" vocabulary unified across resume banner/toasts/labels; raw sid slice in resume banner replaced with a human session-title lookup (`resumeSidLabel()`), falling back to the slice only when no title is known; settings projects-folder fact gained the same copy button its sibling root rows already had. Perf: `prism-core.min.js` (a classic UMD script, not an ES module) now gets a `rel=preload as=script` hint alongside the existing `modulepreload` hints for marked/dompurify; History tab re-fetch on every entry now gated on staleness (>5s) instead of unconditional; bulk file delete/move patch `state.files.entries` locally and render immediately instead of awaiting a second full directory round-trip (backgrounded as reconciliation) — scoped to the bulk paths only, the highest-traffic case. Kit docs: README gained a "multi-agent chat shell" bullet naming the flagship `WorkspaceShell`/`AgentChat`/etc surface (previously undiscoverable from a first read), `COMPONENT_API.md` documents `FileRow`'s `move` action and scopes the `freddie.js` re-exports as demo-site-only. **A concurrent gm session was independently working the same `../design` repo this run — its commits (`df5b566`..`68b615b`) landed my kit edits before I reached CONSOLIDATE; verified via `git log`/`rev-parse HEAD==origin/main` rather than re-pushing.** Browser-witnessed every fix live at 1280px+420px on a no-PASSWORD localhost:3010 instance (settings gaps, empty-state hierarchy, danger delete button, mobile wrap, files checkbox/BulkBar flow after the prop rename): 0 console errors throughout. **Standing rule reaffirmed: `prd-resolve` witness_evidence must be distinct per row — a byte-identical witness across rows is refused by plugkit as a rubber-stamp tell; write what THAT row specifically changed, every time.**
6
+
3
7
  ## Sessions-rail selection dead-click fix (2026-07-03) — thirtieth run
4
8
 
5
9
  User feedback: "layout and design choices look weird... make maximum sense." Live-browser audit of history/live tabs (screenshots + `window.__agentgui` state probing, not a full agent fan-out) found a real logic gap the prior 29 runs' visual sweeps never caught: the conversation rail (`ConversationList`) stays mounted on files/live/settings tabs (`keepSessionsTrack`) and is captioned "Browse a conversation's events" there — but `onSelect` (`site/app/js/app.js` sessions-column builder) only special-cased the chat tab (`resumeInChat`); every other tab fell through to `loadSession(sid)`, which sets `state.selectedSid`/fetches events but never switches tabs. Since only the History tab's `historyMain()` renders `state.events`, clicking a row while on Live/Files/Settings was a dead click — data loaded into state that nothing on screen displayed, with a caption promising an outcome the click didn't deliver. Fixed: selecting from any non-chat tab now navigates to history first. Verified live: `state.tab` was `'live'`, clicked `.ds-session-row`, `state.tab` became `'history'` with `selectedSid` set and the events endpoint fetched. **Standing rule: whenever a persistent-across-tabs component (kept mounted via a `keepXTrack` pattern) has an interaction whose effect is rendered by only ONE of the tabs it appears on, every OTHER tab's handler must produce the same visible effect (usually by navigating to the tab that renders it) — a component kept visually consistent across tabs but behaviorally inert on most of them is worse than not showing it at all.** No kit changes this run (app-only wiring fix). agentgui pushed `c1e4915`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.1021",
3
+ "version": "1.0.1023",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
@@ -44,6 +44,10 @@
44
44
  markdown.js to reach its dynamic import. -->
45
45
  <link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/marked@15.0.12/+esm" crossorigin>
46
46
  <link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/dompurify@3.2.6/+esm" crossorigin>
47
+ <!-- prism-core is a classic UMD <script src>, not an ES module (loaded via
48
+ highlight.js's loadScript, not import) - modulepreload is the wrong hint
49
+ type for it; preload as=script starts the same parallel fetch instead. -->
50
+ <link rel="preload" as="script" href="https://cdn.jsdelivr.net/npm/prismjs@1.30.0/components/prism-core.min.js" crossorigin>
47
51
  <!-- No design content lives here. Every agentgui application-surface style
48
52
  (pills, cwd bar, resume banner, health chips, settings grid, history
49
53
  empty state, boot splash, scrollbar theming, focus rings, print) now
@@ -293,7 +293,7 @@ function navTo(tab, { writeHash: doWriteHash = true, push = true } = {}) {
293
293
  const wantsStream = (t) => t === 'history' || t === 'live'
294
294
  || (t === 'chat' && (state.chat.busy || (Array.isArray(state.active) && state.active.length > 0)));
295
295
  if (wantsStream(tab)) {
296
- if (tab === 'history') refreshHistory();
296
+ if (tab === 'history' && (!state._historyLoadedOnce || Date.now() - (state._historyLoadedAt || 0) > 5000)) refreshHistory();
297
297
  openLiveStream();
298
298
  } else if (wantsStream(prev)) {
299
299
  closeLiveStream();
@@ -993,15 +993,21 @@ async function runBulkDelete() {
993
993
  state.files.dialog = null;
994
994
  marked.clear(); state.files._lastMarkIdx = null;
995
995
  announce('deleted ' + okCount + ' ' + (okCount === 1 ? 'entry' : 'entries'));
996
- } else {
997
- // Keep only the failures marked; report the split inside the dialog.
998
- state.files.marked = new Set(failed.map((e) => e.path || e.name));
999
- const firstErr = results.find((r) => r.status === 'rejected');
1000
- d.busy = false;
1001
- d.error = 'deleted ' + okCount + ' of ' + targets.length + ' - '
1002
- + (failed.length === 1 ? '1 entry' : failed.length + ' entries') + ' failed ('
1003
- + fileMutationCopy(firstErr.reason || {}) + '). The failed entries stay selected.';
996
+ // Patch the visible list immediately instead of waiting on a second full
997
+ // directory round-trip - the server already confirmed every deletion.
998
+ const gone = new Set(targets.map((e) => e.path || e.name));
999
+ state.files.entries = (state.files.entries || []).filter((e) => !gone.has(e.path || e.name));
1000
+ render();
1001
+ loadDir(state.files.path, { fromHash: true }); // background reconcile
1002
+ return;
1004
1003
  }
1004
+ // Keep only the failures marked; report the split inside the dialog.
1005
+ state.files.marked = new Set(failed.map((e) => e.path || e.name));
1006
+ const firstErr = results.find((r) => r.status === 'rejected');
1007
+ d.busy = false;
1008
+ d.error = 'deleted ' + okCount + ' of ' + targets.length + ' - '
1009
+ + (failed.length === 1 ? '1 entry' : failed.length + ' entries') + ' failed ('
1010
+ + fileMutationCopy(firstErr.reason || {}) + '). The failed entries stay selected.';
1005
1011
  await loadDir(state.files.path, { fromHash: true });
1006
1012
  render();
1007
1013
  }
@@ -1031,14 +1037,20 @@ async function runBulkMove(destDir) {
1031
1037
  state.files.dialog = null;
1032
1038
  marked.clear(); state.files._lastMarkIdx = null;
1033
1039
  announce('moved ' + okCount + ' ' + (okCount === 1 ? 'entry' : 'entries'));
1034
- } else {
1035
- state.files.marked = new Set(failed.map((e) => e.path || e.name));
1036
- const firstErr = results.find((r) => r.status === 'rejected');
1037
- d.busy = false;
1038
- d.error = 'moved ' + okCount + ' of ' + targets.length + ' - '
1039
- + (failed.length === 1 ? '1 entry' : failed.length + ' entries') + ' failed ('
1040
- + fileMutationCopy(firstErr.reason || {}) + '). The failed entries stay selected.';
1040
+ // Moved entries leave the current dir - patch them out immediately rather
1041
+ // than waiting on a second full directory round-trip.
1042
+ const gone = new Set(targets.map((e) => e.path || e.name));
1043
+ state.files.entries = (state.files.entries || []).filter((e) => !gone.has(e.path || e.name));
1044
+ render();
1045
+ loadDir(state.files.path, { fromHash: true }); // background reconcile
1046
+ return;
1041
1047
  }
1048
+ state.files.marked = new Set(failed.map((e) => e.path || e.name));
1049
+ const firstErr = results.find((r) => r.status === 'rejected');
1050
+ d.busy = false;
1051
+ d.error = 'moved ' + okCount + ' of ' + targets.length + ' - '
1052
+ + (failed.length === 1 ? '1 entry' : failed.length + ' entries') + ' failed ('
1053
+ + fileMutationCopy(firstErr.reason || {}) + '). The failed entries stay selected.';
1042
1054
  await loadDir(state.files.path, { fromHash: true });
1043
1055
  render();
1044
1056
  }
@@ -1544,7 +1556,7 @@ async function stopAllActive(sessions) {
1544
1556
  : null;
1545
1557
  announce(failed
1546
1558
  ? 'stopped ' + okSids.length + ' of ' + sids.length + ' sessions'
1547
- : 'stopped ' + plural(okSids.length, 'session'));
1559
+ : 'stopped ' + plural(okSids.length, 'conversation'));
1548
1560
  await refreshActive();
1549
1561
  render();
1550
1562
  return okSids;
@@ -1659,7 +1671,7 @@ function computeLiveSessions() {
1659
1671
  external: true,
1660
1672
  readOnly: true,
1661
1673
  title: sess ? (projectLabel(sess.title) || projectLabel(sess.project) || sid) : sid,
1662
- agent: 'external session',
1674
+ agent: 'other tool',
1663
1675
  model: '',
1664
1676
  cwd: pathBasename(sess && sess.cwd),
1665
1677
  elapsed: '',
@@ -1914,11 +1926,14 @@ function chatMain() {
1914
1926
  if (state.chat.resumeSid && state.selectedAgent === 'claude-code') {
1915
1927
  // The agent-switched note (if any) lives inside the resume banner rather
1916
1928
  // than as a separate stacked Alert, so resume context reads as one block.
1917
- banners.push(h('div', { key: 'rb', class: 'resume-banner', role: 'status' },
1929
+ banners.push(h('div', { key: 'rb', class: 'resume-banner', role: 'status' }, ...[
1918
1930
  h('span', { key: 'rbtxt', class: 'lede' },
1919
- 'next message continues session ' + state.chat.resumeSid.slice(0, 8) + ''
1920
- + (state.chat.resumeNote ? ' - ' + state.chat.resumeNote : '')),
1921
- Btn({ key: 'rclr', onClick: () => { state.chat.resumeSid = null; state.chat.resumeNote = null; render(); }, children: 'clear' })));
1931
+ 'next message continues this conversation (' + resumeSidLabel(state.chat.resumeSid) + ')'),
1932
+ state.chat.resumeNote
1933
+ ? h('span', { key: 'rbnote', class: 'sub' }, state.chat.resumeNote)
1934
+ : null,
1935
+ Btn({ key: 'rclr', onClick: () => { state.chat.resumeSid = null; state.chat.resumeNote = null; render(); }, children: 'clear' }),
1936
+ ].filter(Boolean)));
1922
1937
  }
1923
1938
  if (state.selectedAgent && !agentAvailable(state.selectedAgent)) {
1924
1939
  banners.push(Alert({ key: 'unavail', kind: 'warn', title: agentName + ' is not installed',
@@ -2035,7 +2050,7 @@ function chatMain() {
2035
2050
  },
2036
2051
  userTurnCount > 0 ? plural(userTurnCount, 'turn') : null,
2037
2052
  (state.chat.resumeSid && state.selectedAgent === 'claude-code')
2038
- ? 'continues session ' + state.chat.resumeSid.slice(0, 8) + ''
2053
+ ? 'continues conversation (' + resumeSidLabel(state.chat.resumeSid) + ')'
2039
2054
  : null,
2040
2055
  ].filter(Boolean),
2041
2056
  } : undefined,
@@ -2638,7 +2653,7 @@ function historyMain() {
2638
2653
  h('div', { key: 'histempty', class: 'history-empty', role: 'status' },
2639
2654
  h('p', { key: 'gt', class: 'history-empty-title' },
2640
2655
  count ? 'Select a conversation to view its events' : 'No conversations yet'),
2641
- h('p', { key: 'gs', class: 'lede history-empty-sub' },
2656
+ h('p', { key: 'gs', class: 'history-empty-sub' },
2642
2657
  count
2643
2658
  ? count + ' conversation' + (count === 1 ? '' : 's') + ' available · use the search box or press / to filter'
2644
2659
  : 'Start a chat or run a local coding agent - its conversation will appear here live.')),
@@ -2660,7 +2675,7 @@ function historyMain() {
2660
2675
  const hasErrors = state.events.some(e => e.isError);
2661
2676
  const actions = h('div', { key: 'acts', class: 'history-actions' }, [
2662
2677
  Btn({ key: 'resume', primary: true, onClick: () => resumeInChat(sess || { sid: state.selectedSid }), children: 'open in chat' }),
2663
- Btn({ key: 'copy', onClick: copySid, children: copyToast || 'copy session id' }),
2678
+ Btn({ key: 'copy', onClick: copySid, children: copyToast || 'copy conversation id' }),
2664
2679
  Btn({ key: 'exportsess', disabled: !state.eventsLoaded, title: 'Download this session\'s events as JSON',
2665
2680
  onClick: () => downloadBlob(JSON.stringify(state.events, null, 2), (projectLabel(sess?.project) || 'session') + '-' + state.selectedSid + '.json', 'application/json'),
2666
2681
  children: 'export' }),
@@ -2673,7 +2688,7 @@ function historyMain() {
2673
2688
  // indexing copy (ccsniff's first JSONL walk can take a minute).
2674
2689
  const body = state.eventsLoaded
2675
2690
  ? h('div', { key: 'noev', class: 'lede empty-state', role: 'status' },
2676
- h('span', { key: 'noevtxt' }, 'no events in this session'),
2691
+ h('span', { key: 'noevtxt' }, 'no events in this conversation'),
2677
2692
  Btn({ key: 'reload', onClick: () => loadSession(state.selectedSid), children: 'reload' }))
2678
2693
  // Shape-matched skeleton rows (kit EventList loading state) instead of a
2679
2694
  // lone spinner collapsing the slowest pane in the product.
@@ -2912,6 +2927,17 @@ function projectLabel(project) {
2912
2927
  return segs.length ? segs[segs.length - 1] : project;
2913
2928
  }
2914
2929
 
2930
+ // Resume banner/turn-continuation copy previously showed a raw sid slice with
2931
+ // no way to tell which conversation it refers to - prefer the same human
2932
+ // label used everywhere else in the rail, falling back to the sid slice.
2933
+ function resumeSidLabel(sid) {
2934
+ if (!sid) return '';
2935
+ const arr = Array.isArray(state.sessions) ? state.sessions : [];
2936
+ const sess = arr.find((s) => s.sid === sid);
2937
+ const label = sess && (projectLabel(sess.title) || projectLabel(sess.project));
2938
+ return label || (sid.slice(0, 8) + '…');
2939
+ }
2940
+
2915
2941
  function uniqueProjects() {
2916
2942
  const arr = Array.isArray(state.sessions) ? state.sessions : [];
2917
2943
  const seen = new Map();
@@ -3154,7 +3180,8 @@ function serverPanel() {
3154
3180
  { label: 'version', value: hh.version ? 'v' + hh.version : fallback },
3155
3181
  { label: 'uptime', value: upMs != null ? fmtDuration(upMs) : fallback },
3156
3182
  { label: 'connected clients', value: wsClients != null ? String(wsClients) : fallback },
3157
- { label: 'projects folder', value: hh.projectsDir || fallback, title: hh.projectsDir || undefined },
3183
+ { label: 'projects folder', value: hh.projectsDir || fallback, title: hh.projectsDir || undefined,
3184
+ onCopy: hh.projectsDir ? () => copyText(hh.projectsDir, 'projects folder copied') : undefined },
3158
3185
  ];
3159
3186
  return Panel({
3160
3187
  id: 'server',
@@ -3322,6 +3349,7 @@ async function refreshHistory() {
3322
3349
  try {
3323
3350
  state.sessions = await B.listSessions(state.backend);
3324
3351
  state._historyLoadedOnce = true;
3352
+ state._historyLoadedAt = Date.now();
3325
3353
  state.historySlow = false;
3326
3354
  // Index by sid so each live SSE event is an O(1) lookup, not an O(sessions)
3327
3355
  // linear scan per event during a burst load.
@@ -3466,7 +3494,7 @@ async function loadSession(sid, { focusEventI = null, focusEventTs = null, fromH
3466
3494
  ts: Date.now(),
3467
3495
  role: 'error',
3468
3496
  type: 'fetch',
3469
- text: 'Failed to load session: ' + errText(e) + ' - retry via sidebar',
3497
+ text: 'Failed to load session: ' + errText(e) + ' - retry via the rail',
3470
3498
  }];
3471
3499
  clearTimeout(slowTimer);
3472
3500
  state.eventsSlow = false;