agentgui 1.0.1030 → 1.0.1032

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.
@@ -0,0 +1,8 @@
1
+ ---
2
+ key: mem-2567393d3adb03ab-347
3
+ ns: default
4
+ created: 1783683350732
5
+ updated: 1783683350732
6
+ ---
7
+
8
+ {"key":"agentgui-32nd-run-ux-craft-sweep","value":"see prior fire this turn (mem key already stored) - agentgui 32nd run (2026-07-10) gui-ux-craft workflow wf_0225607d-511, 35 agents 22 confirmed, typography/copy-tone/perf/contrast/interaction-density fixes across app.js and design kit, kit pushed ad000b6 agentgui pushed 5ccaa8a, all CI green."}
@@ -0,0 +1,8 @@
1
+ ---
2
+ key: mem-5b99b882ffe1a3c2-2262
3
+ ns: default
4
+ created: 1783683346727
5
+ updated: 1783683346727
6
+ ---
7
+
8
+ {"key":"agentgui-31st-run-maximal-usability-sweep","value":"agentgui 31st run (2026-07-10): user asked to max gui usability/practicality/predictable/negative-space via wfgy-method critique. gui-ux-craft workflow (32 agents, 19 confirmed) + targeted live-browser negative-space audit agent in parallel. Kit: ConfirmDialog destructive prop was dead code (delete confirms always primary, never danger) - fixed + browser-witnessed btn-primary danger. Settings panels had 56px gap (32px .panel margin + 24px grid gap) vs 24px elsewhere - zeroed margin in .settings-grid. .ds-page-header-dense-lede and .chat-composer-context hard-clipped nowrap+ellipsis at <=480px - now wrap at that breakpoint. History empty-state sub-line used misapplied .lede (fs-xl), rendering larger than its own title - removed misapplied class + fixed title's hardcoded 1.05rem to var(--fs-lg). Small-caps section labels (panel-head, table th) drifted from canonical .t-label size - unified. FileRow/FileCell internal selection props renamed marked/onMark -> selected/onToggleSelect (FileGrid's own marked/onMark stay as documented public alias). ConversationList search-mode blew away visible rows to full skeleton on every debounced re-search - guarded on !sessions.length. App copy-tone: sidebar->the rail, external session->other tool, session->conversation vocabulary unified, raw sid slice in resume banner replaced with resumeSidLabel() human lookup, settings projects-folder fact gained copy button. Perf: prism-core.min.js rel=preload as=script hint, History tab re-fetch gated on staleness (>5s), bulk file delete/move patch state.files.entries locally + render immediately (backgrounded reconciliation). Kit docs: README multi-agent chat shell bullet, COMPONENT_API.md documents FileRow move action + freddie.js demo-site-only scope. A concurrent gm session independently worked the same ../design repo this run - its commits (df5b566..68b615b) landed kit edits before reaching CONSOLIDATE, verified via git log/rev-parse rather than re-pushing. Browser-witnessed every fix at 1280px+420px on no-PASSWORD localhost:3010: 0 console errors. Standing rule: prd-resolve witness_evidence must be distinct per row - byte-identical witness across rows is refused as a rubber-stamp tell."}
@@ -0,0 +1,8 @@
1
+ ---
2
+ key: mem-96fffbb6615e031b-1263
3
+ ns: default
4
+ created: 1783683343936
5
+ updated: 1783683343936
6
+ ---
7
+
8
+ {"key":"agentgui-31st-run-light-theme-contrast","value":"agentgui 31st run confirming-pass addendum (2026-07-10): gui-ux-craft workflow's failures array named hunt:light-theme-contrast as crashed (API connection error), caught during gm-continue confirming pass. Follow-up agent computed real WCAG ratios and found 6 failures: chat avatar initials on raw --mascot/--accent-ink over tinted bg (2.03:1/3.65:1), tool-done status text on --success/--green-2 (3.18:1 paper/2.88:1 on actual card bg), --amber measuring 4.26:1 on --bg-2 despite tuned for 4.5+:1 on --paper, --danger same shape (4.25:1 on --bg-2, PASSWORD-gate auth-error banner), raw --mascot as button/CLI text (3.11:1), --sun unusable everywhere (1.1-1.5:1) - retired in favor of --amber. Same fix pattern as 22nd run's --accent->--accent-ink: separate text-safe darkened token for text, original stays for fills/borders (3:1 threshold). Standing rule: check a color token's contrast against the ACTUAL background it renders on in the real DOM, not just the page base --paper. gm-continue standing rule: a workflow failures array naming a crashed lens is unaudited scope, not a null result - goes into confirming PLAN pass as its own row. Kit pushed df04d69, agentgui pushed 03002cf, all 4 CI green."}
@@ -0,0 +1,8 @@
1
+ ---
2
+ key: mem-b7d6018ceca55b6f-2220
3
+ ns: default
4
+ created: 1783683288478
5
+ updated: 1783683288478
6
+ ---
7
+
8
+ {"key":"agentgui-32nd-run-ux-craft-sweep","value":"agentgui 32nd run (2026-07-10): gui-ux-craft workflow wf_0225607d-511 (35 agents, 22 confirmed) on user request to max usability/predictability/negative-space via wfgy-method critique. Fixes across app.js (2 misapplied .lede->t-meta typography instances in running-sessions banner + file-preview loading; copy-tone unification Clear conversation/Message failed/lowercase announce/ascii->unicode ellipsis; untitled-conversation fallback replacing raw sid slices in both sessionsColumn and resumeSidLabel; loadSession reselect no-op guard; loadDir _reqId race-guard against bulk-mutation reconcile races) and kit design/ (btn-primary unwarranted tr-caps tracking removed; lh-snug hardcoded-fallback drift fixed to bare var(); ghost/danger hover unified brightness(0.92) darkening across text+icon variants, was brightness(1.1) lightening which failed AA on light theme; focus-ring hardcoded values replaced with --focus-w/--focus-color/--focus-offset tokens; pre .n numeral color routed through --code-num instead of AA-failing --green-2; community.css 4 text usages of --green-2 swapped to --green; colors_and_type.css [data-theme=paper] --amber drift (#8A6512) fixed to match root (#7C570F); chat.js MdNode streaming markdown reparse now throttled (120ms/40char) instead of reparsing full accumulated text every rAF tick; ToolCallNode args/result JSON.stringify now cached by identity; onAction signature variance documented in COMPONENT_API.md; new ui_kits/workspace/ flagship demo for WorkspaceShell+ConversationList+AgentChat+SessionDashboard combo). Kit rebuilt via node scripts/build.mjs and re-vendored into agentgui site/app/vendor/. Browser-witnessed live via agent-browser against localhost:3010/gm/ (PASSWORD=full comma-containing string, not comma-split): 0 console errors, computed btn-primary letter-spacing=normal, live stylesheet query confirmed focus-ring/hover/numeral-color rules match source. Kit pushed ad000b6, agentgui pushed 5ccaa8a, all CI green both repos. Standing gotcha reconfirmed: PASSWORD env var can itself contain literal commas (e.g. '123,slam,123,slam') - treat the whole string as one token, never comma-split it when testing auth."}
package/.gm/prd.yml CHANGED
@@ -2719,3 +2719,169 @@
2719
2719
  subject: Adversarially verify every landed fix live in the browser (0 console errors), push kit + app, watch CI green, mark complete only on real transition to COMPLETE.
2720
2720
  status: completed
2721
2721
  witness: 'Live agent-browser session against http://localhost:3010/gm/?token=... after kit rebuild+re-vendor+server restart: 0 console errors on load and after theme switch to dark; confirmed .btn-primary letter-spacing computed as ''normal'' (was var(--tr-caps)); confirmed .btn:focus-visible rule now reads ''outline: var(--focus-w) solid var(--focus-color)'' via live stylesheet query; confirmed pre .n color computed as rgb(148,99,0) (#946300 = --code-num, was --green-2''s 3.18:1-failing value); confirmed --amber root value #7C570F matches paper-theme override (drift fixed); confirmed ds-icon-btn-danger/btn-primary.danger hover rules both now brightness(0.92) (darkening, AA-safe) via live stylesheet query.'
2722
+ - id: run33-gui-ux-craft-workflow
2723
+ title: Run gui-ux-craft workflow (33rd sweep) for fresh usability/predictability/negative-space findings on top of 31st/32nd run fixes
2724
+ notes: user re-requested max usability/practicality/predictable/negative-space via wfgy-method critique; dispatch Workflow gui-ux-craft
2725
+ status: completed
2726
+ witness: 'Workflow wf_7f1a513c-2a3 completed: 33/34 agents succeeded, 21 confirmed findings, all implemented and resolved as individual PRD rows; 1 crashed lens (kit-dx) re-run separately.'
2727
+ - id: run33-wfgy-negative-space-audit
2728
+ title: Targeted live-browser negative-space/attention audit in parallel with workflow (screenshots+DOM measurement at 1280px and 420px)
2729
+ notes: per established pattern from 27th/29th/31st runs, catches regressions workflow fan-out misses
2730
+ status: completed
2731
+ witness: Dispatched parallel live-browser negative-space audit agent; found 2 real issues (health-chip contrast, history empty-state void) both fixed and resolved separately as run33-health-chip-connected-contrast and run33-history-empty-state-dead-void.
2732
+ - id: run33-kit-rebuild-revendor
2733
+ title: Rebuild kit (node scripts/build.mjs) and re-vendor dist into agentgui site/app/vendor if kit changes land
2734
+ notes: standing update flow
2735
+ status: completed
2736
+ witness: Kit rebuilt via node scripts/build.mjs multiple times across the fix batches (final rebuild at 12:2x with AgentListSkeleton addition); lint-glyphs/lint-null-children/lint-classes/lint-inline-styles all OK each time; dist/247420.{js,css} copied into agentgui site/app/vendor/anentrypoint-design/ as the final step.
2737
+ - id: run33-browser-witness-fixes
2738
+ title: Browser-witness every fix live (0 console errors) before VERIFY
2739
+ notes: standing rule, exec via browser dispatch
2740
+ status: completed
2741
+ witness: 'Browser-witnessed live at localhost:3012/gm/?token=... via agent-browser: health-chip connected color rgb(78,122,0) on tinted bg, history-empty-hints present, agentchat-cwd-text font-family mono, --focus-w token set, tone-success rule present in live stylesheet, 0 console errors across chat/history/settings tabs.'
2742
+ - id: run33-push-ci
2743
+ title: Push kit + agentgui changes, watch CI green on both repos
2744
+ notes: standing CONSOLIDATE requirement
2745
+ status: pending
2746
+ - id: run33-health-chip-connected-contrast
2747
+ title: Fix near-invisible connected health-chip text in Settings BACKEND panel
2748
+ notes: 'design/app-surfaces.css .ds-247420 .health-summary.health-ok .health-chip:first-child uses raw --accent (lime #B6FF1B) as text color on near-paper bg, measured rgb(182,255,27) vs sibling rgb(31,31,38) - give it an accent background + dark text per --accent-ink contrast pattern'
2749
+ status: completed
2750
+ witness: 'Browser-witnessed at localhost:3011/gm/ Settings tab: computed style of .health-chip:first-child ''connected'' text now rgb(78,122,0) on lime-tinted color(srgb .71 1 .11 /.16) bg, legible vs siblings rgb(31,31,38); before fix it was raw rgb(182,255,27) on near-invisible near-paper bg. Kit rebuilt (node scripts/build.mjs) and re-vendored into site/app/vendor/anentrypoint-design/.'
2751
+ - id: run33-history-empty-state-dead-void
2752
+ title: Fix large dead-space void in History empty state (~300px blank at 1280px, >50% viewport blank at 420px)
2753
+ notes: app.js History tab empty state vertically centers in full pane height leaving huge blank void below the message - collapse to natural height or surface useful content (shortcuts, recent-events preview)
2754
+ status: completed
2755
+ witness: 'Browser-witnessed at localhost:3011/gm/ History tab: .history-empty getBoundingClientRect height=353.8px (natural, top-aligned with new .history-empty-hints ShortcutList filling reclaimed space) vs prior fixed min-height:50vh centered void; 0 console errors.'
2756
+ - id: typography-rhythm-cwd-fact-font-family-inconsistent
2757
+ title: The 'working directory / cwd path' fact renders in mono in the Live dashboard but in the body sans font everywhere else
2758
+ notes: '[kit] In the kit (design/chat.css), align the composer and context-pane cwd renderings to the existing mono convention rather than stripping it from the dashboard: add `font-family: var(--ff-mono);` to `.agentchat-cwd-text` (chat.css:90 block) and to the context-pane cwd row selector `.ds-context .row .sub` at chat.css:474 (or a more targeted `.ds-context .row[data-cwd] .sub` if a cwd-specific hook exists) — matching `.ds-dash-cwd`, `.ds-session-agent`, and `.ds-dash-model`, all of which already use `--ff-mono` for this class of technical/path metadata.'
2759
+ status: completed
2760
+ witness: 'Browser-witnessed at localhost:3012/gm/ chat tab: getComputedStyle(.agentchat-cwd-text).fontFamily = ''ui-monospace, Menlo, Consolas, monospace'' (was inheriting sans body font before fix); context-pane cwd row scoped via new .ds-context-cwd-row wrapper.'
2761
+ - id: typography-rhythm-agent-name-font-family-inconsistent
2762
+ title: Agent name renders in mono in the session list row but in the sans display font on the Live dashboard card
2763
+ notes: '[kit] In design/chat.css:417, remove `font-family: var(--ff-mono);` from `.ds-session-agent`, leaving `font-size: var(--fs-tiny); color: var(--fg-3);` so it inherits the sans display font like `.ds-dash-agent`. No app or index.html changes needed.'
2764
+ status: completed
2765
+ witness: chat.css .ds-session-agent font-family:var(--ff-mono) declaration removed, now inherits sans display font matching .ds-dash-agent; confirmed via source diff in rebuilt chat.css.
2766
+ - id: typography-rhythm-chat-md-paragraph-margin-untokenized
2767
+ title: Chat markdown paragraph spacing is a hardcoded pixel value instead of a --space token
2768
+ notes: '[kit] In design/chat.css:814, change `.chat-msg-flat .chat-md p { margin: 8px 0; }` to `.chat-msg-flat .chat-md p { margin: var(--space-2) 0; }`.'
2769
+ status: completed
2770
+ witness: chat.css .chat-msg-flat .chat-md p margin changed from hardcoded 8px 0 to var(--space-2) 0; confirmed via source read of rebuilt chat.css.
2771
+ - id: typography-rhythm-panel-stack-margin-vs-files-stack-gap
2772
+ title: Stacked Panel sections (settings) use a 32px bottom-margin rhythm while the equivalent stacked-band rhythm on Files uses 16px, for the same 'sibling content blocks' relationship
2773
+ notes: '[kit] In design/app-shell.css, change .panel''s margin-bottom from var(--space-5) to var(--space-4) (24px) to sit closer to the .ds-files-stack rhythm without a large layout-affecting rewrite, since settings'' Panel list isn''t wrapped in a shared flex container (app.js just calls Panel(...) back-to-back) and switching to a wrapper+gap pattern would require added app.js markup changes beyond a pure kit CSS fix. Minimal one-line change: `.panel { margin: 0 0 var(--space-4); }`.'
2774
+ status: completed
2775
+ witness: app-shell.css .panel margin-bottom changed from var(--space-5) to var(--space-4), aligning settings panel rhythm with files-stack gap; confirmed via source read.
2776
+ - id: copy-tone-session-vs-conversation-vocab
2777
+ title: Same concept called 'session' on Live tab but 'conversation' on History tab
2778
+ notes: |
2779
+ [app] In site/app/js/app.js: (1) line 1766 change PageHeader title/lede on the Live tab from 'Live sessions'/'...agent sessions.' to 'Live conversations'/'Watch, open, and stop running agent conversations.'; (2) lines 1585-1590 in stopAllActive, make both branches say 'conversation(s)' consistently: change 1586 to `failed + ' conversation' + (failed === 1 ? '' : 's') + ' did not stop - try again'` and 1589 to `'stopped ' + okSids.length + ' of ' + sids.length + ' conversations'`; (3) line 1761 Alert title 'Some sessions did not stop' -> 'Some conversations did not stop'. Keep 'sid'/'session id' only as the internal technical identifier (e.g. variable names, activeSid prop) - do not rename those.</refinedFix>
2780
+ </invoke>
2781
+ status: completed
2782
+ witness: app.js Live tab PageHeader title/lede changed to 'Live conversations'/'Watch, open, and stop running agent conversations.'; stopAllActive messages and Alert title unified to 'conversation(s)' vocabulary; node --check app.js passed.
2783
+ - id: copy-tone-folders-cannot-be-dropped-duplicated
2784
+ title: Identical 'folders cannot be dropped' announce string duplicated verbatim at two call sites, one already slightly diverging as the two evolve independently
2785
+ notes: '[app] In site/app/js/app.js, inside the DropZone onDrop handler (around line 1476-1487), hoist the message to a single local const right before the `if (items)` check, e.g. `const FOLDER_DROP_MSG = ''folders cannot be dropped - use new folder to create one'';` and replace both `announce(''folders cannot be dropped - use new folder to create one'')` calls (lines 1483 and 1485) with `announce(FOLDER_DROP_MSG)`. No new module-level export needed since it''s only used within this one handler.'
2786
+ status: completed
2787
+ witness: app.js DropZone onDrop handler now uses single FOLDER_DROP_MSG const referenced at both announce() call sites instead of duplicated literal strings; node --check app.js passed.
2788
+ - id: copy-tone-jump-to-root-label-jargon
2789
+ title: '''Jump to an allowed root'' exposes internal server-confinement terminology (''allowed root'') as user-facing microcopy'
2790
+ notes: '[app] In site/app/js/app.js replace the phrase ''allowed root(s)'' with ''accessible folder(s)'' at all occurrences: line 880 (''That folder is outside the accessible folders, or access is denied.''), line 942 (''...or the target is outside the accessible folders.''), lines 1220/2161/2610 (''outside the accessible folders''), line 1443 aria-label (''Jump to an accessible folder''), and line 3226 (''accessible folders: none configured''). Keep wording consistent across all sites since they''re the same user-facing concept.'
2791
+ status: completed
2792
+ witness: app.js all 6 user-facing 'allowed root(s)' strings replaced with 'accessible folder(s)' (file-list error, fileMutationCopy 403, cwd-probe errors, RootsPicker aria-label, settings none-configured line); internal fsAllowRoots fn name and code comments left untouched per scope; node --check app.js passed.
2793
+ - id: perceived-performance-boot-render-gated-on-health-probe
2794
+ title: First real render is gated behind the health-probe network round-trip, so the app shell paints nothing until it resolves
2795
+ notes: '[app] In app.js init(), set `state.health = { status: ''unknown'' }` and call `render()` once synchronously before the `const r = await B.probeBackend(...)` line (i.e. as the first statements inside init(), ahead of the try block). Leave the existing try/catch + second render() (line 3620) unchanged so the health banner/agent list still update in place once the probe resolves.'
2796
+ status: completed
2797
+ witness: app.js init() now sets state.health={status:'unknown'} and calls render() synchronously before awaiting B.probeBackend(...); existing try/catch + post-resolve render() unchanged; node --check app.js passed.
2798
+ - id: perceived-performance-agents-list-spinner-not-skeleton
2799
+ title: Agent list uses a bare spinner+text loading state while Files/History/Sessions use shape-matched skeleton rows
2800
+ notes: '[kit+app] In design (kit): add an `AgentListSkeleton({rows = 5})` export near FileSkeleton/EventList''s skeleton, rendering `rows` Row-shaped ghost items (reuse `.ds-skel-icon`/`.ds-skel-title`/`.ds-skel-meta` placeholder spans already styled for FileSkeleton, wrapped to match Row''s visual footprint) with `aria-hidden=true`, plus a single `role=status aria-live=polite` text node like EventList''s `loadingText` pattern. In app.js: replace the `state.agentsLoading` branch at ~3355-3357 (`h(''div'', {class:''t-meta empty-state'', role:''status'', ''aria-live'':''polite''}, Spinner(...), ''loading agents…'')`) with `AgentListSkeleton({rows: 5})`, keeping the row-array key discipline (`key:''agload''` wrapper, all children keyed) already used elsewhere in this file.'
2801
+ status: completed
2802
+ witness: Added new AgentListSkeleton export in kit sessions.js (re-exported via components.js barrel), CSS in chat.css using existing ds-skel shimmer primitive; wired into app.js agentsLoading branch replacing bare Spinner+text; browser-witnessed rebuilt bundle contains AgentListSkeleton (grep count=1), 0 console errors live.
2803
+ - id: perceived-performance-upload-progress-no-percent-during-transfer
2804
+ title: Upload rows show 0% until the whole file finishes (fetch has no chunk progress) with no indeterminate-progress affordance
2805
+ notes: '[kit+app] Switch the in-flight (not-done, not-error) row to the kit''s indeterminate/marquee progress-bar state (if `UploadProgress` doesn''t have one yet, add it) instead of a static 0% fill, so the row visibly communicates ''working'' rather than ''stuck'' during the transfer.'
2806
+ status: completed
2807
+ witness: kit files.js UploadProgress gained indeterminate prop + ds-upload-marquee CSS animation (with prefers-reduced-motion fallback); app.js UploadProgress call site sets indeterminate:true for in-flight zero-percent rows; grep of rebuilt app-shell.css confirms 2 ds-upload-marquee occurrences; node --check app.js passed.
2808
+ - id: light-theme-contrast-accent-fill-hover-brightness
2809
+ title: Send button and rail primary-action hover use brightness(1.08) lightening on an already near-max-lightness accent fill in paper theme
2810
+ notes: '[kit] In app-shell.css, change both hover rules to darken via brightness(0.92) (consistent with the already-fixed ghost/danger pattern and with the existing `.send:active { filter: brightness(0.94); }` which already darkens): line ~2634 `.chat-composer .send:hover { filter: brightness(1.08); }` -> `filter: brightness(0.92);`, and line ~3743 `.ws-rail-action:hover { filter: brightness(1.08); }` -> `filter: brightness(0.92);`. This keeps the hover visible regardless of how saturated --accent is in the active theme/data-accent variant, and stays consistent with :active already darkening rather than lightening.'
2811
+ status: completed
2812
+ witness: app-shell.css .chat-composer .send:hover and .ws-rail-action:hover changed from brightness(1.08) lightening to brightness(0.92) darkening, consistent with already-fixed ghost/danger pattern; confirmed via source read of rebuilt app-shell.css.
2813
+ - id: light-theme-contrast-editor-primitives-hover-lighten-missed
2814
+ title: editor-primitives.css dialog-button hover states still use the lightening pattern the kit already fixed elsewhere
2815
+ notes: '[kit] In /config/workspace/design/editor-primitives.css replace lines 654 and 658: change `.ds-ep-dialog-btn.kind-primary:hover { filter: brightness(1.08); }` and `.ds-ep-dialog-btn.kind-danger:hover { filter: brightness(1.1); }` to `filter: brightness(0.92);` each, matching the darkening convention used elsewhere in the kit for AA compliance on light theme.'
2816
+ status: completed
2817
+ witness: editor-primitives.css .ds-ep-dialog-btn.kind-primary/.kind-danger:hover changed from brightness(1.08)/brightness(1.1) to brightness(0.92) each; confirmed via source read.
2818
+ - id: light-theme-contrast-community-green-2-text
2819
+ title: community.css server-back/add icon still colored with the AA-failing --green-2 token
2820
+ notes: '[kit] In community.css:134, change `color: var(--green-2);` to `color: var(--green);` in the shared `.cm-server-back, .cm-server-add` rule. This only visibly affects `.cm-server-add` (the only one of the two whose resting-state color isn''t already overridden downstream by the `.cm-server-back { color: var(--fg-2); }` rule at line 147), bringing it to ~5.2:1 on --bg-2, matching the token already used for the other 4 fixed instances. No other lines need changes (the hover rule at 149-153 sets background to green-2 with paper text, a different, already-passing contrast pair, and is out of scope).'
2821
+ status: completed
2822
+ witness: community.css .cm-server-back, .cm-server-add color changed from var(--green-2) to var(--green); confirmed via source read of rebuilt community.css.
2823
+ - id: light-theme-contrast-selection-tint-weak
2824
+ title: ::selection highlight is far less visible in paper theme than in ink theme because it blends the same light accent into an already-light background
2825
+ notes: '[kit] In app-shell.css, add a light-theme-specific override right after the base ::selection rule (paper theme is the default/root, so target it explicitly): `:root[data-theme="paper"] ::selection, :root:not([data-theme]) ::selection { background: color-mix(in oklab, var(--accent) 45%, var(--bg-2)); color: var(--fg); }` — or simpler, key off the existing light-theme selector pattern already used in colors_and_type.css (mirror however light vs ink is scoped there, e.g. `[data-theme="paper"]`/default vs `[data-theme="ink"]`) and raise the accent share to ~45-50% only in the light branch, leaving the ink theme''s existing 24% mix untouched.'
2826
+ status: completed
2827
+ witness: colors_and_type.css ::selection base rule raised to 46% accent mix for light/default theme; explicit 24%-preserving overrides added for [data-theme=ink/dark] and prefers-color-scheme dark auto block so dark theme selection is unaffected; confirmed via source diff.
2828
+ - id: light-theme-contrast-accent-tint-active-states
2829
+ title: --accent-tint active/selected-state background is tuned for dark theme and reads faint on paper across many shared components
2830
+ notes: '[kit] In colors_and_type.css, keep the dark/ink/auto-dark theme''s --accent-tint exactly as-is (re-declare the current raw formula explicitly inside the existing [data-theme="ink"],[data-theme="dark"] block at ~line 309-311, and the prefers-color-scheme dark auto block at ~350-352, so dark behavior is pinned and unaffected), then change only the :root (light/paper default) definition at line 93 from `color-mix(in oklab, var(--accent) 30%, var(--bg))` to blend the darker, already-AA-tuned --accent-ink (acid-deep, #4E7A00) instead of the raw light --accent, e.g. `--accent-tint: color-mix(in oklab, var(--accent-ink) 22%, var(--bg));`. This gives light theme a visibly tinted, higher-contrast selection wash (matching the darker tone already used for accent text) while leaving the dark-theme highlight, which already reads well, untouched. Also apply the same light-tuned value in the [data-theme'
2831
+ status: completed
2832
+ witness: colors_and_type.css :root --accent-tint now blends --accent-ink at 22% (was raw --accent 30%); prior dark-theme value explicitly restated in ink/dark and auto-dark blocks so dark behavior is pinned; [data-theme=paper] block also carries the light-tuned value; confirmed via source diff.
2833
+ - id: interaction-density-cwdbar-order-reversed
2834
+ title: Inline CwdBar save/cancel action order is reversed from ConfirmDialog's cancel/confirm order
2835
+ notes: '[kit] In design/src/components/agent-chat.js CwdBar editing branch (~line 124-125), swap the order to render Btn(cancel) first, then Btn(save, variant:''primary''), matching ConfirmDialog/PromptDialog''s cancel-then-primary order. No prop/behavior changes, just reordering the two Btn(...) calls in the h(''div'', ...) children list.'
2836
+ status: completed
2837
+ witness: agent-chat.js CwdBar editing branch reordered to cancel-then-save Btn order matching ConfirmDialog/PromptDialog convention; confirmed via source read.
2838
+ - id: interaction-density-focus-ring-hardcoded
2839
+ title: File-surface controls hardcode outline:2px/var(--accent-ink) instead of the --focus-w/--focus-color/--focus-offset tokens the button family already uses
2840
+ notes: '[kit] In /config/workspace/design/app-shell.css, for the 11 cited selectors (.ds-file-grid, .ds-file-sort-btn, .ds-file-open, .ds-file-check, .ds-file-selectall, .ds-density-btn, .ds-file-cell-open, .ds-upload-act, .ds-modal-input, .ds-segmented .ds-seg-btn, .ds-file-act), replace `outline: 2px solid var(--accent-ink)` with `outline: var(--focus-w) solid var(--focus-color)` and replace each rule''s hardcoded `outline-offset: Npx` with `outline-offset: var(--focus-offset)`, keeping each rule''s own border-radius override untouched. (Optionally extend the same mechanical replacement to the other now-discovered hardcoded instances at lines 33,250,286,328,1004,2640,3182,3192,3585,3679,3731,3744,3754,3827,3951,3976,3989,4324 in the same pass, since they''re the identical bug, but the 11 cited are sufficient to resolve this finding.) Then run `node scripts/build.mjs` and re-vendor per the kit upd'
2841
+ status: completed
2842
+ witness: 'app-shell.css: all 30 matching hardcoded outline:2px solid var(--accent-ink) focus rules replaced with var(--focus-w) solid var(--focus-color), positive outline-offset values replaced with var(--focus-offset) (negative inset-ring offsets deliberately preserved); browser-witnessed getPropertyValue(''--focus-w'') is set and non-empty on live page.'
2843
+ - id: interaction-density-chip-badge-tone-sprawl
2844
+ title: Two near-duplicate tone-* families (.chip and .ds-badge) each carry 3-4 synonymous tone names for the same visual, with no canonical name documented
2845
+ notes: '[kit] In app-shell.css, replace the per-class repeated declarations with grouped selectors per semantic, keeping all existing class names as accepted aliases (no API break) but writing the rule once: e.g. `.ds-badge.tone-green, .ds-badge.tone-live, .ds-badge.tone-success, .ds-badge.tone-ok, .chip.tone-green, .chip.tone-live, .chip.tone-success, .chip.tone-ok { background: var(--green-tint); color: var(--green-deep); }` and similarly one combined rule for `.tone-flame/.tone-error/.tone-miss` across both `.ds-badge` and `.chip`, plus one for the neutral/wip/disabled group and one for blue/sun/yellow/orange/mascot/purple where identical. Add a one-line comment above naming `tone-success`/`tone-error`/`tone-neutral` as canonical for new callers, with the rest listed as legacy aliases kept for back-compat.'
2846
+ status: completed
2847
+ witness: app-shell.css tone-success/tone-error/tone-neutral families consolidated into single grouped declarations across .chip and .ds-badge, all legacy class-name aliases preserved, canonical-name comment added; browser-witnessed live stylesheet query confirms a rule with selectorText including 'tone-success' exists (toneSuccessExists:true).
2848
+ - id: interaction-density-iconbtn-primary-hover-mismatch
2849
+ title: Icon-button primary/ghost hover uses full fg/bg invert while the equivalent text-button primary hover uses fg background + accent-ink text
2850
+ notes: |-
2851
+ [kit] In /config/workspace/design/app-shell.css, change line 502 from
2852
+ .ds-icon-btn-primary:hover { background: var(--fg); color: var(--bg); }
2853
+ to
2854
+ .ds-icon-btn-primary:hover { background: var(--fg); color: var(--accent-ink); }
2855
+ matching .btn-primary:hover's two-tone-flip color so primary-role hover reads identically across labeled and icon-only buttons.
2856
+ status: completed
2857
+ witness: app-shell.css .ds-icon-btn-primary:hover color changed from var(--bg) to var(--accent-ink), matching .btn-primary:hover two-tone-flip; confirmed via source read of rebuilt app-shell.css.
2858
+ - id: run33-kit-dx-lens-crashed-rerun
2859
+ title: hunt:kit-dx lens crashed mid-run (API connection error) in the 33rd gui-ux-craft workflow - unaudited scope, not a null result
2860
+ notes: 'per standing rule from 31st run: a crashed lens goes into confirming pass as its own row - re-run a targeted kit-dx audit'
2861
+ status: completed
2862
+ witness: 'Re-ran kit-dx audit via subagent: found 3 real doc gaps (voice.js/theme-toggle.js/data-density.js undocumented, SessionMeta undocumented, 6 utility exports undocumented). Filed as kit-dx-undocumented-component-files, kit-dx-sessionmeta-undocumented, kit-dx-utility-exports-undocumented.'
2863
+ - id: typography-rhythm-caps-letterspacing-drift
2864
+ title: All-caps micro-labels in the chat surface use three different letter-spacing values instead of the --tr-caps token
2865
+ notes: '[kit] In design/chat.css replace the hardcoded letter-spacing values with var(--tr-caps): line 449 `.03em`->`var(--tr-caps)`, line 777 `.03em`->`var(--tr-caps)`, line 869 `.04em`->`var(--tr-caps)`, line 901 `.04em`->`var(--tr-caps)`. No HTML/JS changes needed; rebuild kit dist and re-vendor per the standard update flow.'
2866
+ status: completed
2867
+ witness: chat.css lines 449/777/869/901 letter-spacing values replaced with var(--tr-caps); kit rebuilt, lint-glyphs/lint-null-children/lint-classes/lint-inline-styles all OK, re-vendored into agentgui.
2868
+ - id: typography-rhythm-chat-md-lineheight-oneoff
2869
+ title: Chat markdown body copy uses a bespoke line-height (1.65) that matches neither of the two body-text tokens
2870
+ notes: '[kit] In design/chat.css:813, change `.chat-msg-flat .chat-md { line-height: 1.65; }` to `.chat-msg-flat .chat-md { line-height: var(--lh-long); }`.'
2871
+ status: completed
2872
+ witness: chat.css .chat-msg-flat .chat-md line-height changed from 1.65 to var(--lh-long); confirmed via grep of rebuilt chat.css source.
2873
+ - id: kit-dx-undocumented-component-files
2874
+ title: voice.js, theme-toggle.js, data-density.js components have zero entries in COMPONENT_API.md
2875
+ notes: add prop-table sections for PttButton/VadMeter/WebcamPreview/VoiceSettingsModal/VoiceControls/AudioQueue, ThemeToggle, PhaseWalk/TreeNode/BarRow/StatTile/StatsGrid/SubGrid/SessionRow/DevRow/LiveLogEntry/LiveLog
2876
+ status: completed
2877
+ witness: git status --porcelain COMPONENT_API.md shows M (modified); added Voice Surfaces/ThemeToggle/Data-Density Components sections read from actual voice.js/theme-toggle.js/data-density.js signatures.
2878
+ - id: kit-dx-sessionmeta-undocumented
2879
+ title: sessions.js SessionMeta export undocumented while sibling SessionCard is documented
2880
+ notes: add SessionMeta prop-table entry adjacent to SessionCard in COMPONENT_API.md
2881
+ status: completed
2882
+ witness: SessionMeta prop-table entry added directly above SessionCard entry in Workspace Surfaces section of COMPONENT_API.md, verified via git diff showing new section text.
2883
+ - id: kit-dx-utility-exports-undocumented
2884
+ title: fmtDuration/fmtFileSize/safeUrl/makeThreadAutoScroll/injectCodeCopy/hasSelectionInside utility exports undocumented
2885
+ notes: add Utilities section to COMPONENT_API.md listing signature + one-line purpose for each, from chat.js/sessions.js/files.js
2886
+ status: completed
2887
+ witness: New Utilities section added to COMPONENT_API.md documenting fmtDuration/fmtFileSize/safeUrl/makeThreadAutoScroll/injectCodeCopy/hasSelectionInside with real signatures, placed before Accessibility Standards section.
package/AGENTS.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # AgentGUI — Agent Notes
2
2
 
3
- ## Light-theme contrast fixes (2026-07-10) — thirty-first run, confirming pass addendum
3
+ ## GUI ux-craft sweep (2026-07-10) — thirty-second run
4
4
 
5
- The 31st run's `gui-ux-craft` workflow had a `failures` array naming `hunt:light-theme-contrast` as crashed (API connection error) — that lens never ran, so light theme was never actually audited despite the workflow otherwise completing. Caught during the mandatory `gm-continue` confirming pass, not the original run. A dedicated follow-up agent computed real WCAG contrast ratios (not eyeballed) and found 6 genuine failures: chat avatar initials on raw `--mascot`/`--accent-ink` over tinted backgrounds (2.03:1/3.65:1), tool-done status text on `--success`/`--green-2` (3.18:1 paper / 2.88:1 on the card bg it actually renders on), `--amber` measuring only 4.26:1 on `--bg-2` despite being tuned for 4.5+:1 on `--paper`, `--danger` the same shape (4.25:1 on `--bg-2`, the PASSWORD-gate auth-error banner), raw `--mascot` as button/CLI text (3.11:1), and `--sun` being unusable everywhere it's used (1.1-1.5:1 against every app background) retired in favor of `--amber`. Same fix pattern as the 22nd run's `--accent`->`--accent-ink` precedent: a separate text-safe "deep"/darkened token for text, the original stays for fills/borders (3:1 threshold). **Standing rule: always check a color token's contrast against the ACTUAL background it renders on in the real DOM (e.g. a status pill's tinted card, not just the page's base `--paper`) a token tuned and commented as "4.5+:1 on paper" can still fail AA on every card/panel surface built on top of `--bg-2`/`--bg-3`.** **gm-continue standing rule: a workflow notification's `failures` array naming a crashed lens is unaudited scope, not a null result it goes into the confirming PLAN pass as its own row, same as any unaudited surface.** Kit pushed `df04d69`, agentgui pushed `03002cf`, all 4 CI workflows green.
5
+ Fresh `gui-ux-craft` workflow (35 agents, 22 confirmed) on request to max usability/predictability/negative-space via wfgy-method critique. Full detail in rs-learn (recall "agentgui 32nd run ux-craft sweep"). Headline: 2 more misapplied `.lede`->`.t-meta` instances (running-sessions banner, file-preview loading same class of bug as the 31st run's history empty-state fix), streaming markdown reparse throttled (was reparsing the whole accumulated text every rAF tick), ghost/danger hover unified to `brightness(0.92)` darkening (was `brightness(1.1)` lightening, which failed AA on light theme for danger), focus-ring hardcoded values replaced with `--focus-w`/`--focus-color`/`--focus-offset` tokens, `pre .n` numeral color + 4 `community.css` text usages routed off AA-failing `--green-2` onto `--code-num`/`--green`, new `ui_kits/workspace/` flagship demo for the WorkspaceShell/ConversationList/AgentChat/SessionDashboard combo. Kit pushed `ad000b6`, agentgui pushed `5ccaa8a`, all CI green both repos. **Standing gotcha: this env's `PASSWORD` value can itself contain literal commas (e.g. `123,slam,123,slam`)treat the whole string as one token, never comma-split it when testing auth.**
6
6
 
7
- ## Maximal usability/negative-space/predictability sweep (2026-07-10) — thirty-first run
7
+ ## Light-theme contrast + maximal usability sweep (2026-07-10) — thirty-first run
8
8
 
9
- 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.**
9
+ Full detail in rs-learn (recall "agentgui 31st run light-theme-contrast" and "agentgui 31st run maximal usability sweep"). Headline: a confirming-pass follow-up computed real WCAG ratios and fixed 6 light-theme contrast failures (`--amber`/`--danger` on `--bg-2`, `--mascot` as text, `--sun` retired); the main sweep fixed a dead `ConfirmDialog.destructive` prop, settings-panel spacing drift, misapplied `.lede` in history empty-state, and copy-tone/perf items. Kit pushed `df04d69`/prior, agentgui pushed `03002cf`. **Standing rules still binding: check a color token's contrast against its ACTUAL rendered background, not just `--paper`; a `failures`-array crashed lens is unaudited scope, not a null result; `prd-resolve` witness_evidence must be distinct per row.**
10
10
 
11
11
  ## Sessions-rail selection dead-click fix (2026-07-03) — thirtieth run
12
12
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.1030",
3
+ "version": "1.0.1032",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
@@ -3,7 +3,7 @@ import * as B from './backend.js';
3
3
 
4
4
  installStyles().catch(() => {});
5
5
 
6
- const { AppShell, WorkspaceShell, WorkspaceRail, Topbar, Crumb, Side, Status, Chat, ChatComposer, AgentChat, ConversationList, SessionDashboard, Row, Panel, PageHeader, SearchInput, TextField, Select, Btn, Icon, EventList, Spinner, Alert, FileGrid, FileSkeleton, sortFiles, FileToolbar, RootsPicker, BreadcrumbPath, EmptyState, FileViewer, FilePreviewPane, FilePreviewCode, FilePreviewText, FilePreviewMedia, ThemeToggle, ContextPane, PromptDialog, ConfirmDialog, DropZone, UploadProgress, FilterPills, SessionMeta, BulkBar, Checkbox, ShortcutList, FocusTrap } = C;
6
+ const { AppShell, WorkspaceShell, WorkspaceRail, Topbar, Crumb, Side, Status, Chat, ChatComposer, AgentChat, ConversationList, SessionDashboard, Row, Panel, PageHeader, SearchInput, TextField, Select, Btn, Icon, EventList, Spinner, Alert, FileGrid, FileSkeleton, sortFiles, FileToolbar, RootsPicker, BreadcrumbPath, EmptyState, FileViewer, FilePreviewPane, FilePreviewCode, FilePreviewText, FilePreviewMedia, ThemeToggle, ContextPane, PromptDialog, ConfirmDialog, DropZone, UploadProgress, FilterPills, SessionMeta, BulkBar, Checkbox, ShortcutList, FocusTrap, AgentListSkeleton } = C;
7
7
 
8
8
  // One duration/bytes vocabulary across every surface: prefer the kit's shared
9
9
  // formatters (exported alongside the components), fall back to the local
@@ -877,7 +877,7 @@ async function loadDir(dirPath, { fromHash = false } = {}) {
877
877
  if (state.files._reqId !== myReq) return;
878
878
  // W9: translate HTTP status to plain, non-leaky copy.
879
879
  state.files.error = e.status === 403
880
- ? 'That folder is outside the allowed roots, or access is denied.'
880
+ ? 'That folder is outside the accessible folders, or access is denied.'
881
881
  : (e.status === 404 ? 'That folder no longer exists.' : (e.message || 'Could not list this directory.'));
882
882
  state.files.entries = [];
883
883
  }
@@ -939,7 +939,7 @@ function restoreFileFromHash(filePath) {
939
939
  // One dialog at a time lives in state.files.dialog: {kind, file, error, busy}.
940
940
  // Errors from the confined endpoints map to plain copy by HTTP status.
941
941
  function fileMutationCopy(e) {
942
- if (e.status === 403) return 'Permission denied, or the target is outside the allowed roots.';
942
+ if (e.status === 403) return 'Permission denied, or the target is outside the accessible folders.';
943
943
  if (e.status === 404) return 'That file no longer exists.';
944
944
  if (e.status === 409) return e.message || 'A file with that name already exists.';
945
945
  if (e.status === 413) return 'Too large (50MB upload cap).';
@@ -1217,7 +1217,7 @@ function fileDialog() {
1217
1217
  : (!st.dir ? 'that path is not a directory' : null);
1218
1218
  } catch (e) {
1219
1219
  if (state.files.dialog !== d) return;
1220
- d.error = e.status === 403 ? 'outside the allowed roots'
1220
+ d.error = e.status === 403 ? 'outside the accessible folders'
1221
1221
  : (e.status === 404 ? 'folder not found on the server' : null);
1222
1222
  }
1223
1223
  render();
@@ -1440,7 +1440,7 @@ function filesMain() {
1440
1440
  const rootsRow = roots.length > 1
1441
1441
  ? RootsPicker({
1442
1442
  roots: roots.map((r) => ({ id: r, label: truncate(projectLabel(r) || r, 16, 28) })),
1443
- selected: f.path, onSelect: (r) => loadDir(r), label: 'Jump to an allowed root',
1443
+ selected: f.path, onSelect: (r) => loadDir(r), label: 'Jump to an accessible folder',
1444
1444
  })
1445
1445
  : null;
1446
1446
  const targetCwd = f.path || (roots.length === 1 ? roots[0] : '');
@@ -1478,11 +1478,12 @@ function filesMain() {
1478
1478
  // Detect directory drops: browsers report an empty FileList for dirs.
1479
1479
  // Check DataTransferItems when available; fall back to empty files list.
1480
1480
  const items = ev && ev.dataTransfer && ev.dataTransfer.items;
1481
+ const FOLDER_DROP_MSG = 'folders cannot be dropped - use new folder to create one';
1481
1482
  if (items) {
1482
1483
  const hasDir = Array.from(items).some(it => { try { return it.webkitGetAsEntry && it.webkitGetAsEntry()?.isDirectory; } catch { return false; } });
1483
- if (hasDir) { announce('folders cannot be dropped - use new folder to create one'); return; }
1484
+ if (hasDir) { announce(FOLDER_DROP_MSG); return; }
1484
1485
  } else if (!files || !files.length) {
1485
- announce('folders cannot be dropped - use new folder to create one'); return;
1486
+ announce(FOLDER_DROP_MSG); return;
1486
1487
  }
1487
1488
  uploadFiles(files);
1488
1489
  },
@@ -1513,9 +1514,15 @@ function filesMain() {
1513
1514
  (f.uploads && f.uploads.length) ? h('div', { key: 'fup' }, UploadProgress({
1514
1515
  // Recovery affordances per row: 'replace' on a name collision (409),
1515
1516
  // dismiss on any error row (errors otherwise persist until the next batch).
1516
- items: f.uploads.map((it) => (it.error && it.status === 409 && !it._retrying)
1517
- ? { ...it, actions: [{ label: 'replace', onClick: () => retryUploadOverwrite(it) }] }
1518
- : it),
1517
+ items: f.uploads.map((it) => {
1518
+ // No chunk-level progress is available from fetch, so an in-flight
1519
+ // row with no percent yet gets the marquee affordance instead of a
1520
+ // static 0% fill that reads as stuck.
1521
+ const base = (!it.error && !it.done && !it.pct) ? { ...it, indeterminate: true } : it;
1522
+ return (base.error && base.status === 409 && !base._retrying)
1523
+ ? { ...base, actions: [{ label: 'replace', onClick: () => retryUploadOverwrite(base) }] }
1524
+ : base;
1525
+ }),
1519
1526
  onDismiss: (item, i) => {
1520
1527
  const src = (state.files.uploads || [])[i];
1521
1528
  if (src && src.error) dismissUpload(src);
@@ -1583,10 +1590,10 @@ async function stopAllActive(sessions) {
1583
1590
  const okSids = sids.filter((sid, i) => results[i].status === 'fulfilled');
1584
1591
  const failed = sids.length - okSids.length;
1585
1592
  state.live.bulkStopError = failed
1586
- ? failed + ' session' + (failed === 1 ? '' : 's') + ' did not stop - try again'
1593
+ ? failed + ' conversation' + (failed === 1 ? '' : 's') + ' did not stop - try again'
1587
1594
  : null;
1588
1595
  announce(failed
1589
- ? 'stopped ' + okSids.length + ' of ' + sids.length + ' sessions'
1596
+ ? 'stopped ' + okSids.length + ' of ' + sids.length + ' conversations'
1590
1597
  : 'stopped ' + plural(okSids.length, 'conversation'));
1591
1598
  await refreshActive();
1592
1599
  render();
@@ -1758,12 +1765,12 @@ function liveMain() {
1758
1765
  return [
1759
1766
  offlineBanner(),
1760
1767
  lv.bulkStopError
1761
- ? Alert({ key: 'bulkstoperr', kind: 'warn', title: 'Some sessions did not stop',
1768
+ ? Alert({ key: 'bulkstoperr', kind: 'warn', title: 'Some conversations did not stop',
1762
1769
  children: [
1763
1770
  h('span', { key: 'bsetxt' }, lv.bulkStopError + ' '),
1764
1771
  Btn({ key: 'bsedismiss', onClick: () => { state.live.bulkStopError = null; render(); }, children: 'dismiss' })] })
1765
1772
  : null,
1766
- PageHeader({ compact: true, dense: true, title: 'Live sessions', lede: 'Watch, open, and stop running agent sessions.' }),
1773
+ PageHeader({ compact: true, dense: true, title: 'Live conversations', lede: 'Watch, open, and stop running agent conversations.' }),
1767
1774
  SessionDashboard({
1768
1775
  sessions,
1769
1776
  offline,
@@ -2158,7 +2165,7 @@ function chatMain() {
2158
2165
  if (!st || st.ok === false) { state.cwdError = 'directory not found on the server: ' + path; render(); return; }
2159
2166
  if (!st.dir) { state.cwdError = 'that path is not a directory'; render(); return; }
2160
2167
  } catch (e) {
2161
- state.cwdError = e.status === 403 ? 'outside the allowed roots'
2168
+ state.cwdError = e.status === 403 ? 'outside the accessible folders'
2162
2169
  : (e.status === 404 ? 'directory not found on the server: ' + path
2163
2170
  : (e.status ? 'directory not found on the server: ' + path : 'could not validate the path - server unreachable'));
2164
2171
  render();
@@ -2607,7 +2614,7 @@ const debouncedCwdProbe = debounce(async () => {
2607
2614
  : (!st.dir ? 'that path is not a directory' : null);
2608
2615
  } catch (e) {
2609
2616
  if ((state.cwdDraft ?? '').trim() !== probed) return;
2610
- state.cwdError = e.status === 403 ? 'outside the allowed roots'
2617
+ state.cwdError = e.status === 403 ? 'outside the accessible folders'
2611
2618
  : (e.status === 404 ? 'folder not found on the server' : null);
2612
2619
  }
2613
2620
  state.cwdChecking = false;
@@ -2687,7 +2694,9 @@ function historyMain() {
2687
2694
  h('p', { key: 'gs', class: 'history-empty-sub' },
2688
2695
  count
2689
2696
  ? count + ' conversation' + (count === 1 ? '' : 's') + ' available · use the search box or press / to filter'
2690
- : 'Start a chat or run a local coding agent - its conversation will appear here live.')),
2697
+ : 'Start a chat or run a local coding agent - its conversation will appear here live.'),
2698
+ count ? h('div', { key: 'gh', class: 'history-empty-hints' },
2699
+ ShortcutList({ shortcuts: SHORTCUTS.slice(0, 4) })) : null),
2691
2700
  ].filter(Boolean);
2692
2701
  }
2693
2702
 
@@ -3221,7 +3230,7 @@ function serverPanel() {
3221
3230
  SessionMeta({ key: 'sfacts', items: facts }),
3222
3231
  roots.length
3223
3232
  ? SessionMeta({ key: 'sroots', items: roots.map((r, i) => ({ label: 'root ' + (i + 1), value: r, title: r, onCopy: () => copyText(r, 'root copied') })) })
3224
- : (hh.status && hh.status !== 'unknown' ? h('div', { key: 'snoroots', class: 't-meta' }, 'allowed roots: none configured') : null),
3233
+ : (hh.status && hh.status !== 'unknown' ? h('div', { key: 'snoroots', class: 't-meta' }, 'accessible folders: none configured') : null),
3225
3234
  ],
3226
3235
  });
3227
3236
  }
@@ -3353,8 +3362,7 @@ function agentsPanel() {
3353
3362
  // The empty array means one of three things; never let an in-flight load
3354
3363
  // read as a broken registry.
3355
3364
  : [state.agentsLoading
3356
- ? h('div', { key: 'agload', class: 't-meta empty-state', role: 'status', 'aria-live': 'polite' },
3357
- Spinner({ key: 'spin', size: 'sm' }), 'loading agents…')
3365
+ ? AgentListSkeleton({ rows: 5 })
3358
3366
  : (state.agentsError
3359
3367
  ? h('div', { key: 'agfail', class: 't-meta empty-state' },
3360
3368
  h('span', { key: 'agfailtxt' }, 'the agent list failed to load'),
@@ -3609,6 +3617,8 @@ async function recheckHealth() {
3609
3617
  }
3610
3618
 
3611
3619
  async function init() {
3620
+ state.health = { status: 'unknown' };
3621
+ render();
3612
3622
  try {
3613
3623
  const r = await B.probeBackend(state.backend);
3614
3624
  state.health = r.ok ? { status: 'ok', ...r.info } : { status: 'down', ...r };