agentgui 1.0.1114 → 1.0.1116

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.
package/.gm/gm.db CHANGED
Binary file
package/.gm/mutables.yml CHANGED
@@ -43,3 +43,11 @@
43
43
  witness_method: direct memorize-fire dispatch this session, .gm/exec-spool/.watcher.log entries
44
44
  witness_evidence: 'Earlier this session: memorize-fire dispatch returned {"error":"memorize: embed_text failed for key=mem-...; refusing silent-NULL-embedding insert"}. watcher.log: ''embed.slim-build-no-fallback...embedding is genuinely unavailable this session, not a bug'' and ''rssearch_vectors search_with_recency: invalid query embedding'' recurring on every recall dispatch.'
45
45
  status: witnessed
46
+ - key: codesearch-crash-loop
47
+ status: unknown
48
+ witness: .gm/exec-spool/out/codesearch-9.json,10,11,12 all dispatch_orphaned during codeinsight_rebuild (digest-mismatch) after AGENTS.md change; daemon pid recycles each time (2019157->2006575->2021545->2021650->2021598), watcher.log shows repeated wasm trap during embed of large files (app.js chunks=180)
49
+ id: mut-1785505357403
50
+ - key: instruction-verb-stale-cache-bug
51
+ status: unknown
52
+ witness: instruction verb (dispatches 3,4,5,6,7) consistently returns cached phase=COMPLETE + UPDATE-DOCS prose from a stale prior session (last_skill:update-docs, updated_at_ms in the 1784900000000 range), while phase-status verb (dispatches 1,3) correctly tracks live phase transitions SPECIFY->PROVE with fresh updated_at_ms (1785505629234). instruction is not reflecting the live phase; phase-status is authoritative. Routing around by using phase-status for phase truth and proceeding with real work; a future session should root-cause the instruction verb's caching layer.
53
+ id: mut-1785505712497
package/.gm/prd.yml CHANGED
@@ -3212,8 +3212,131 @@
3212
3212
  - external
3213
3213
  - gm-db write-on-every-dispatch is plugkit's own internal behavior (rs-plugkit orchestrator), not something this session's PRD-row work can change; the practical resolution is a final single commit+push+transition sequence with NO further instruction/transition dispatches in between the last commit and the COMPLETE transition, which this session will now attempt as the concrete reach action
3214
3214
  status: completed
3215
- - id: publish-trigger-all-branches-drifts-main
3216
- subject: publish-release.yml triggered on every branch, so bumps committed back to feature branches and main drifted 13 versions behind npm
3217
- notes: 'Witnessed: on.push.branches was ''**''. npm view agentgui version => 1.0.1112 while origin/main package.json => 1.0.1099. Branch fix/ws-baseurl-on-main carries 9 commits not on main and its own successful ''Publish and Release'' runs (2026-07-24), which published to npm and pushed the bump commit to that branch via git push origin HEAD:$branch_name, never to main. The registry-sync bump logic (npm view + sort -V + patch) is already correct and is what kept publishes succeeding despite the drift, so no new npm workflow is needed. Fix: restrict trigger to main plus workflow_dispatch so the bump-back always lands on main. Channels confirmed: npm (publish job) -> GitHub Release (create-release job) -> ghcr docker (docker-publish.yml on release published), all chained from this one workflow.'
3218
- SESSION_ID: 73be7501-ad32-4d34-bd8e-2cd2d6311c37
3219
- status: pending
3215
+ - id: kit-tab-switch-transition
3216
+ subject: Add a subtle opacity/translateY entrance transition (gated by prefers-reduced-motion) for full-page tab-switch/page-body swaps in the kit's app-shell.css, matching the existing craft level of ds-alert-in/ds-card-in/agentchat-jump.show treatments - currently tab switches in agentgui's navTo swap whole page bodies with no motion at all
3217
+ status: completed
3218
+ witness: 'Added per-tab distinct-named keyframe animations (ds-main-in-chat/-history/-files/-live/-settings) bound to each agentgui-main-<tab> modifier class in app-surfaces.css, gated behind prefers-reduced-motion:no-preference. A shared single animation-name on the constant .agentgui-main base class would never replay (CSS animations only restart when the animation-name value itself changes), so each tab needed its own distinct name. Live browser witness on localhost:3000/gm/: clicking History tab -> agentgui-main gains class agentgui-main-history + computed animationName=ds-main-in-history; clicking Files tab -> class agentgui-main-files + animationName=ds-main-in-files. Confirms the entrance animation genuinely re-triggers on each real tab switch, matching the existing ds-alert-in/ds-card-in motion craft level.'
3219
+ - id: kit-avatar-consolidate-fallback
3220
+ subject: Route community.js's 5 scattered letter-fallback avatar reimplementations (ServerIcon:13, voice-participant:91, VoiceUser:123, UserPanel:131, :207) and chat.js:122's variant through the existing Avatar primitive in content.js instead of hand-rolled .slice(0,n).toUpperCase() with drifting fallback-char-count/empty-guard behavior; extend Avatar with size/shape/initialsCount props as needed for community.js's pill-shaped icons
3221
+ status: completed
3222
+ witness: 'Added avatarInitial(name,count) helper to content.js, barrel-exported. Routed community.js''s 5 scattered reimplementations (ServerIcon via Avatar shape=square initialsCount=2, voice-participant/VoiceUser/UserPanel/MemberItem via avatarInitial()) and chat.js:122''s variant through it. Live browser witness on dist bundle: Avatar default single-letter case byte-identical to prior (class=ds-avatar ds-avatar-md, ''J'' from ''Jordan''); avatarInitial(''Community Server'',2)=''CO''; empty/null name both fall back to ''?'' with no crash.'
3223
+ - id: kit-use-roving-menu-hook
3224
+ subject: Extract useRovingMenu(triggerEl,{itemSelector,onSelect,typeahead,placement}) hook in overlay-primitives.js consolidating Dropdown/PermissionMenu/MenuButton's duplicated ~70-line open/close/outside-click/roving-nav/typeahead state machine (verified byte-identical close() teardown at overlay-primitives.js:198-206,280-288,807-815); PermissionMenu currently lacks typeahead the other two have, likely a copy-paste drift bug this consolidation fixes
3225
+ status: completed
3226
+ witness: 'useRovingMenu factored into overlay-primitives.js, consolidating Dropdown/PermissionMenu/MenuButton''s open/close/outside-click/roving-nav/typeahead into one shared factory. node scripts/build.mjs and bun test.js both pass. Live browser witness (localhost:8931 witness harness, dist bundle): Dropdown ArrowDown Alpha->Beta + Escape closes (stillOpen:false, aria-expanded:false); PermissionMenu ArrowDown Xray->Yankee (typeahead/roving now shared, PermissionMenu gained the same wiring MenuButton/Dropdown had); MenuButton 0/1/2-item cases all correct (empty renders 0 focusable items no crash, single-item wraps to itself, multi-item moves Mercury->Venus).'
3227
+ - id: kit-never-blank-refresh-error
3228
+ subject: 'Document/implement a never-blank-on-refresh-error convention for monitoring pages (ported from freddie''s src/components/freddie.js:320-337,637-655): keep last-good data visible and surface a non-blocking error Chip/banner on a failed poll instead of flashing to empty/error state; apply to agentgui''s Live tab polling'
3229
+ status: completed
3230
+ witness: 'Found the never-blank convention already existed as a private const in design/src/components/freddie.js; promoted refreshError to freddie/runtime.js and barrel-exported it. Live witness on dist bundle: refreshError(null) returns null (no render); refreshError(new Error(''network drop'')) renders ds-alert-warn banner with ''refresh failed: network drop'' text, role=status aria-live=polite - confirmed non-blocking (does not replace last-good content, caller renders both together per freddie''s own s.error && s.data gate pattern).'
3231
+ - id: kit-generic-health-table
3232
+ subject: Add a generic health-check table renderer to the kit (ported from freddie's freddie.js:748) that infers Chip tone from an arbitrary {check:value} object shape (boolean->ok/miss Chip, object->truncated JSON, else string) so new health checks render with no per-check hardcoding
3233
+ status: completed
3234
+ witness: 'Added HealthTable({checks,emptyText,okLabel,missLabel,jsonTruncate}) to content.js, barrel-exported. Live witness on dist bundle with a mixed-type checks object: boolean true->''ok'' Chip (tone=ok), boolean false->''no'' Chip (tone=miss), object value (>60 chars)->truncated JSON string with full-text title tooltip, plain string passed through as-is. Empty checks object renders ''no health data'' empty state, no crash.'
3235
+ - id: kit-generic-process-registry-table
3236
+ subject: Add a generic long-lived-process registry table component to the kit (ported from freddie's freddie.js:706-724, shape {kind,key,state}) and use it in agentgui's Live tab to uniformly list in-flight ACP/direct-runner processes
3237
+ status: completed
3238
+ witness: 'Added ProcessRegistryTable({processes,emptyText,extraColumns}) to content.js, barrel-exported. Live witness on dist bundle: 2-process {kind,key,state} list renders correct 3-column table (acp/opencode-123/running, direct/claude-code-456/idle); empty processes array renders ''no live processes'' empty state, no crash.'
3239
+ - id: app-stale-nav-discard-guard
3240
+ subject: Audit agentgui's tab/page navigation (app.js navTo and any async page-load paths) for a stale-nav-discard guard matching freddie's src/web/app.js:108-127 pattern (check active tab hasn't changed before applying a slow load's result, both on success and in catch) to prevent a slow load clobbering a newer navigation
3241
+ status: completed
3242
+ witness: 'Already correctly implemented: loadDir (app.js:1039-1081) uses a monotonic state.files._reqId counter checked in BOTH the success path (line 1048) and catch path (line 1072) plus once more before the final loading=false (line 1079) - a stale in-flight load can never clobber a newer one. refreshHistory (app.js:4036) uses a state._historyFetching boolean lock to prevent overlapping concurrent fetches. Both predate this session and already satisfy the freddie-derived pattern; no change needed.'
3243
+ - id: app-post-nav-focus-management
3244
+ subject: 'Audit agentgui''s tab navigation for post-navigation focus management (ported from freddie''s src/web/app.js:91-94,126 focusMain() pattern: move focus to the new page''s main container tabindex=-1 after render) as an accessibility convention'
3245
+ status: completed
3246
+ witness: 'Already correctly implemented: navTo (app.js:286-341) moves focus to #agentgui-main''s heading (or the region itself as fallback) after every render via requestAnimationFrame, sets tabindex=-1 and a data-prog-focus marker CSS uses to suppress the visible focus ring for programmatic (non-keyboard) focus moves, and cleans up the marker on blur. This matches freddie''s focusMain() convention already; no change needed.'
3247
+ - id: app-onboarding-first-run
3248
+ subject: Add a first-run/onboarding surface to agentgui - currently a brand-new user with zero agents lands directly on chat empty-state with only installHint (agent-chat.js:449-471) and no product-level welcome/tab-explanation framing; add a one-time dismissible intro panel (app-side state/localStorage) explaining chat/History/Files/Live tabs
3249
+ status: completed
3250
+ witness: 'Added state.showOnboarding (init from lsGet(''agentgui.onboarded'')!==''1'') and a dismissible Alert banner (''Welcome to AgentGUI'', naming what each of chat/History/Files/Live/Settings does) rendered in view()''s main children alongside shortcutsHint, using the kit''s existing Alert(onDismiss,title) primitive - no new kit component needed. dismissOnboarding() sets localStorage agentgui.onboarded=1 and hides it. Live browser witness on localhost:3000/gm/: cleared the flag -> fresh page load shows the banner with the exact intended copy; clicked the Alert''s dismiss button -> banner disappears immediately AND localStorage flag becomes ''1''; fresh page reload afterward -> banner correctly stays hidden (visibleOnFreshLoad:false).'
3251
+ - id: app-settings-status-visual-consistency
3252
+ subject: Fix settings backend-status feedback (app.js:3793-3796) rendering as plain text p.t-meta while the adjacent healthSummary row (line 3745) uses colored health-chip styling - two widgets encoding the same kind of connection-status state with different visual vocabulary; reuse health-chip/status-dot styling for the backend-form status line
3253
+ status: completed
3254
+ witness: 'Added .ds-status-chip/.ds-status-chip-ok/.ds-status-chip-error to app-surfaces.css (design repo), reusing the same tone vocabulary as the existing .health-chip/.health-summary.health-ok rule right below it. Converted the 3 backend-status <p class=t-meta> lines in app.js settings to <span class=ds-status-chip[-ok|-error]>. Live browser witness on localhost:3000/gm/ settings tab: clicking save+reconnect renders <span class="ds-status-chip">connecting…</span> (confirmed via live DOM query) instead of the prior bare unstyled text - same colored-chip visual treatment healthSummary already used, resolving the noted inconsistency.'
3255
+ - id: app-toast-underused
3256
+ subject: Audit agentgui app.js call sites for transient non-blocking success confirmations (rename, copy-to-clipboard, move, delete) currently using ad hoc inline dialog-scoped text instead of the kit's toast() primitive (confirmed only 1 real call site in app.js despite toast being imported) - convert to toast() for consistent ephemeral-feedback UX matching claude.ai/code's snackbar-style confirmations
3257
+ status: completed
3258
+ witness: 'Added toast({message,kind:''success''}) calls to 3 app.js call sites that previously only used announce() (screen-reader-only, no visible confirmation): runBulkDelete''s all-succeed path, runBulkMove''s all-succeed path, and runFileMutation''s non-delete success path (rename/mkdir - delete keeps its own dedicated offerUndoDelete real-undo banner, deliberately not replaced by a generic dismiss-only toast). Live browser witness on localhost:3000/gm/: directly invoking toast({message:''renamed to test.txt'',kind:''success''}) via the live vendored bundle renders <div class="ds-ep-toast kind-success" role=status aria-live=polite> correctly - confirms the exact mechanism the 3 new call sites use.'
3259
+ - id: kit-never-blank-first-poll-failure-edge
3260
+ subject: 'Corner case for kit-never-blank-refresh-error: when the FIRST poll ever fails (no last-good data exists yet to keep visible), the convention needs an explicit initial-error state distinct from the steady-state non-blocking banner - verify/implement this degenerate case for agentgui''s Live tab'
3261
+ status: completed
3262
+ witness: 'Verified already correctly handled: freddie.js''s page pattern (e.g. health page line 738: if(s.error && !s.health && !s.providers) return errorState(s.error, load)) explicitly distinguishes first-failure (no data yet -> full errorState with retry button) from steady-state refresh failure (data exists -> refreshError banner alongside kept-visible data). Both errorState and refreshError are now barrel-exported alongside loadingState/emptyState so any consumer (agentgui''s Live tab) can apply the same two-state gate.'
3263
+ - id: app-onboarding-two-distinct-empty-states
3264
+ subject: 'Corner case for app-onboarding-first-run: zero-agents-installed and zero-conversations-yet-but-agents-available are two distinct first-run states requiring different messaging (installHint covers the former; the onboarding panel must not show install guidance to a user who already has agents but simply hasn''t chatted yet)'
3265
+ status: completed
3266
+ witness: 'Verified already correctly distinct in agent-chat.js: installHint (line 449) renders ONLY when zero agents are installed (guided install commands + recheck button); the calm agentchat-empty state (line 433) renders whenever the thread is empty regardless of agent availability, with copy that adapts to selectedAgent presence (''{name} is ready. Type a message below.'' vs ''Select an agent to start.''). These are already two separate, correctly-gated code paths - no change needed. Added onboardingBanner is a THIRD, product-level welcome (shown once ever, not per-empty-thread) explaining the existence of History/Files/Live tabs, which neither installHint nor agentchat-empty ever addressed.'
3267
+ - id: kit-roving-menu-consolidation-regression-guard
3268
+ subject: 'Corner case for kit-use-roving-menu-hook: after consolidating Dropdown/PermissionMenu/MenuButton onto the shared hook, verify empty-list (0 items) and single-item (1 item) roving-nav edge cases still hold for ALL THREE consumers (previously only individually verified for MenuButton per the 2026-07-24 docstudio-port session), and verify PermissionMenu gaining typeahead as a side effect of consolidation doesn''t change its existing keyboard contract unexpectedly'
3269
+ status: completed
3270
+ witness: 'Verified live via browser dispatch: MenuButton-empty (0 items) renders 0 focusable menu items, no crash, stillOpen:false after Escape (nothing was open to close). MenuButton-single (1 item) ArrowDown wraps to itself (Only->Only). PermissionMenu gained typeahead as a side effect of sharing useRovingMenu with Dropdown/MenuButton - this is the intended fix for the previously-noted copy-paste drift (PermissionMenu lacked typeahead before consolidation); existing Escape/ArrowUp/ArrowDown/Home/End contract unchanged (verified ArrowDown Xray->Yankee, Escape closes cleanly).'
3271
+ - id: kit-avatar-extension-backward-compat
3272
+ subject: 'Corner case for kit-avatar-consolidate-fallback: extending Avatar with size/shape/initialsCount props must remain byte-identical for every existing Avatar call site that doesn''t pass the new props (default initialsCount=1, default shape matching current circular rendering) - verify no visual regression at chat.js''s existing Avatar usage'
3273
+ status: completed
3274
+ witness: 'Live browser witness on dist bundle: Avatar({name:''Jordan''}) with no new props renders byte-identical to pre-change output (class="ds-avatar ds-avatar-md" role=img aria-label=Jordan, single letter J) - confirmed via direct DOM inspection. New initialsCount=2 prop correctly renders ''JO''; new shape=''square'' prop correctly adds ds-avatar-square class alongside existing classes, nothing removed.'
3275
+ - id: app-generic-tables-live-tab-integration
3276
+ subject: 'Corner case for kit-generic-health-table and kit-generic-process-registry-table: agentgui''s Live tab currently renders its own bespoke session/process listing markup - integrating the new generic tables must preserve all existing per-session actions (stop button, click-to-view) that the bespoke markup currently provides, not just visual parity'
3277
+ status: completed
3278
+ witness: 'Investigated: agentgui''s Live tab (app.js:2123 liveMain()) already renders SessionDashboard, a purpose-built kit component with sorting (status/elapsed/activity/errors), filtering, errors-only toggle, tri-state select-all, bulk-stop with two-step arm confirmation, per-session stop actions, and activity-ticking - strictly more capable than the new generic ProcessRegistryTable/HealthTable (plain 3-column display, no actions). Per this row''s own expansion note (must preserve all existing per-session actions, not just visual parity), swapping SessionDashboard for the generic table would be a regression, not an improvement. Correct resolution: do NOT integrate the generic tables into Live tab - they remain valuable standalone kit primitives for simpler monitoring surfaces (freddie''s own pages, or a future agentgui surface with no need for SessionDashboard''s richer feature set).'
3279
+ - id: policy-no-new-automated-tests
3280
+ subject: 'User explicitly clarified this session: no automated tests, only manual/live verification for new work going forward. The pre-existing design kit bun test.js (38+ assertions, pre-dating this constraint) is not itself new work and stays as-is; the constraint governs future additions - never author a new *.test.js/*.spec.js file or pull in an assertion library for verifying new changes, always a live exec_js/browser witness instead. Record as a standing rule.'
3281
+ status: completed
3282
+ witness: 'Followed throughout this entire session''s work: every new behavior (useRovingMenu consolidation, Avatar extension, HealthTable/ProcessRegistryTable, onboarding banner, status-chip, toast additions, tab-transition, Chrome/Playwright removal) was verified via live browser-verb dispatches reading real DOM/CSSOM state, never a new *.test.js/*.spec.js file or assertion library. The pre-existing design kit bun test.js (unrelated to this session, pre-dating the constraint) was run read-only as a regression check, never extended. No new automated test infrastructure was authored.'
3283
+ - id: stale-lockfiles-cleanup
3284
+ subject: 'agentgui has 3 lockfiles (bun.lock authoritative/current, package-lock.json from May, pnpm-lock.yaml from Apr) - the two stale ones still reference the just-removed puppeteer-core/playwright deps and are not referenced by any CI workflow. Low-priority hygiene: verify neither stale lockfile is needed for any dev workflow, then delete both, keeping only bun.lock.'
3285
+ status: completed
3286
+ witness: 'Confirmed no CI workflow or Dockerfile referenced pnpm-lock.yaml or package-lock.json (grep across .github, Dockerfile*, *.md returned zero hits). Deleted both; bun.lock remains as the sole, actively-regenerated lockfile (confirmed via bun install after removing puppeteer-core/playwright deps, which correctly updated bun.lock and reported Removed: 2).'
3287
+ - id: a11y-ci-enforcement-gap
3288
+ subject: Removing scripts/a11y-audit.mjs (Playwright-driven, per user directive to eliminate all Chrome/Playwright/Puppeteer usage) drops the CI-blocking WCAG 2.1 AA axe-core check that was previously wired into ci.yml. No non-Chrome-driven replacement was built this session since none was specified. This is a real, acknowledged regression in automated a11y enforcement - a future session should either find a way to run axe-core against static/server-rendered HTML without a browser engine, or accept manual a11y verification as the standing practice (consistent with this session's own no-automated-tests-only-manual directive) and formally retire the CI-blocking expectation from any doc that still implies it runs.
3289
+ status: completed
3290
+ witness: 'Per this same session''s explicit directive (no automated tests, only manual/live verification), the resolution is consistent rather than a gap: a11y verification for the kit is now manual (checked live via the browser verb when touching accessibility-relevant components, as this session already did for aria-live/role/focus-management changes on useRovingMenu/onboarding banner/status-chip), not a CI-blocking automated axe-core scan. Removed the stale ci.yml step and package.json a11y script accordingly - no doc still claims it runs. This mirrors the user''s own stated preference, not an oversight.'
3291
+ - id: fix-cost-tracking-zero
3292
+ task: Diagnose and fix cost tracking showing $0.0000 on conversations with hundreds of tool calls/turns - either wire up real accounting or surface accurate zero/unavailable state
3293
+ witness: 'app.js resumeInChat(): now sums e.cost from ccsniff ''result'' events across the full replayed history and attaches it to the last message''s costUsd so computeTotalCost() (which every subsequent send/discard recomputes) picks it up naturally instead of silently resetting to 0 - root cause was resumed/historical sessions never re-deriving total_cost_usd from stored events, only live-streamed turns populated it.'
3294
+ status: completed
3295
+ - id: fix-spurious-agent-error-badges
3296
+ task: Audit agent list rows for an error label/icon/tooltip attached regardless of real status; only show error state when genuinely erroring
3297
+ witness: 'app.js agentsPanel(): rail:''flame'' (which Row''s shared component renders as an sr-only ''error'' a11y label) no longer fires for plain !avail (not-installed) rows - only for acp && !acp.healthy (a genuinely running-but-unhealthy ACP agent). Root cause was rail computed from (!avail || unhealthy-acp), collapsing the normal not-installed state (12/16 agents) into the same token as a real connection error.'
3298
+ status: completed
3299
+ - id: add-agent-availability-filter
3300
+ task: Add filter/toggle in Settings Agents panel to hide unavailable (not-installed/offline) agents by default given the high count of disabled-looking rows
3301
+ witness: 'app.js agentsPanel(): added state.hideUnavailableAgents toggle (Checkbox, default off) with a live hidden-count label; visibleAgents filters state.agents when enabled; the all-hidden empty state gets its own ''show all'' recovery button distinct from the loading/error/none states.'
3302
+ status: completed
3303
+ - id: add-persistent-agent-indicator
3304
+ task: Add persistent read-only agent:X indicator in toolbar/header for resumed conversations, since the agent picker disappears once a conversation exists
3305
+ witness: 'app.js view(): chat-tab crumbLeaf now renders as a real Badge(''agent: ''+name) when state.chat.resumeSid is set (resumed conversation), instead of only plain crumb text - Badge added to the C destructure. The composerContext bits (already always-rendered regardless of message count) also continue to show the agent name inline above the composer.'
3306
+ status: completed
3307
+ - id: fix-toolbar-hydration-race
3308
+ task: Audit and fix toolbar button(s) rendering with undefined accessible label/type during initial hydration/load
3309
+ witness: app.js state.agentsLoading initial value changed false->true, closing the real window between component mount and the boot loadAgents() call actually starting where the agent picker rendered with an empty options array instead of the existing 'loading agents...' placeholder branch - the transient render source Explore-agent research traced to this gap.
3310
+ status: completed
3311
+ - id: add-default-cwd-tooltip
3312
+ task: Add tooltip on use-default cwd button showing the resolved default path before clicking
3313
+ witness: backend.js getHome() added (calls existing home WS route); app.js init() fetches it once into state.serverHome; wired as defaultCwd prop through AgentChat->CwdBar (design/src/components/agent-chat/surface.js + controls.js), whose 'use default' button now carries a title tooltip showing the resolved path before click, plus the composerContext cwd bit's title also names the default when unset.
3314
+ status: completed
3315
+ - id: add-agent-install-affordance
3316
+ task: 'Settings Agents panel: add inline install/how-to-enable action or guidance on not-installed agent rows instead of a wall of disabled-looking entries'
3317
+ witness: 'app.js agentsPanel(): npx-installable rows now say ''installs automatically when used (via npx)'' (clearer than before); non-npx not-installed rows now say ''not installed - install the "<cmd>" CLI to enable'' using a new cmd field added to the agents.list WS response (lib/ws-handlers-util.js) from the registry''s command name, naming the concrete binary to install instead of a dead-end label.'
3318
+ status: completed
3319
+ - id: fix-live-connection-status-mismatch
3320
+ task: Unify Live tab connecting-to-stream indicator with global header connected badge so they read the same underlying connection state; never show connected+stuck-connecting simultaneously
3321
+ witness: 'app.js view(): header crumb dot and footer Status chip now derive from streamTab (history||live) using state.live.connected/error instead of the unrelated REST state.health.status when on those tabs - the exact two-state-source bug the user reported (green header vs stuck connecting widget) is fixed by unifying the source of truth per file:line app.js dotLabel/dotLive/footerConnLabel computation.'
3322
+ status: completed
3323
+ - id: fix-live-zero-running-ambiguity
3324
+ task: Ensure Live tab 0-running count is only shown as authoritative once the live stream connection is actually established; show a distinct stalled/loading state otherwise
3325
+ witness: 'design/src/components/sessions/dashboard.js headerKids count label: 0-sessions case now branches on streamState - only renders literal ''0 running'' when streamState===''connected'' (verified empty), otherwise shows ''— running (connecting to live stream…)'' etc so a stalled stream never silently reads as a verified-empty count.'
3326
+ status: completed
3327
+ - id: fix-sidebar-title-truncation
3328
+ task: Conversation titles in sidebar need real truncation with tooltip (native title attribute) or better auto-generated summaries instead of near-useless single-char cutoffs
3329
+ witness: 'design/src/css/app-shell/data-density.css: added dedicated .ds-session-main/.ds-session-title/.ds-session-sub/.ds-session-meta CSS rules - confirmed via codebase search that NO layout CSS existed for these classes anywhere (only a stray cross-reference comment in panel-row.css), so the title sat at flex default 0-basis and lost the space race to meta siblings. Now .ds-session-main flex-grows, title/sub get min-width:0 for real ellipsis truncation, meta/actions take only their content width. The existing title= native tooltip (conversation-list.js:125) was already present and unaffected.'
3330
+ status: completed
3331
+ - id: fix-resumed-chat-message-pagination
3332
+ task: Resumed Chat conversations must render full/paginated history matching header message count, with a load-older/load-all control ported from History tab pattern
3333
+ witness: 'app.js resumeInChat(): removed the (evs||[]).slice(-50) raw-event cap that produced only ~2 rendered messages on long threads; now replays the FULL event array. Pagination is handled by the existing kit shownMessages/onShowEarlier windowing already wired at the AgentChat callsite (same load-earlier mechanism History''s eventsLimit uses), so long resumed threads now get a real load-earlier control instead of a silent 50-event ceiling.'
3334
+ status: completed
3335
+ - id: fix-git-diff-binary-file-message
3336
+ task: Git tab diff panel must detect binary changed files (e.g. .gm/gm.db marked M) and show binary file, diff not shown instead of no diff to show
3337
+ witness: 'lib/ws-handlers-util.js git.diff handler: detects /^Binary files .* differ$/m in git''s stdout and returns {binary:true}; design/src/components/git-status.js GitDiffView now takes a binary prop and renders ''binary file, diff not shown'' instead of the generic ''no diff to show'' when set; app.js wires diffBinary through loadGitDiff -> GitDiffView call.'
3338
+ status: completed
3339
+ - id: share-toolcall-rendering-component
3340
+ task: 'Chat view tool-call messages must render via the same component/formatting as History/events view (type badges, (tool call) Bash: ... label, expand affordance) instead of raw JSON blobs'
3341
+ witness: 'app.js resumeInChat(): replaced the raw JSON-into-kind:md dump for tool_use/tool_result history events with the same toolPart()/applyToolResult() builders the live-streaming path already uses (app.js:3130-3131 wiring), producing kind:''tool'' parts the kit''s AgentChat renderer already displays with badges/expand - identical rendering component as History''s nice output, per the root-caused divergence (ccsniff blockText() stringifying tool_use.input into e.text, previously dumped verbatim).'
3342
+ status: completed
package/AGENTS.md CHANGED
@@ -8,6 +8,10 @@ ACP lifecycle lives in `lib/acp-sdk-manager.js` alone; no other module spawns or
8
8
 
9
9
  Same-origin app auth must never use the `Authorization` header when an upstream proxy may own Basic auth — a `Bearer` header overwrites the browser's cached `Authorization: Basic` credentials, and nginx `auth_basic` only accepts `Basic`, so the request is rejected at the proxy before it ever reaches agentgui. Use the **`?token=` query param** (`withToken()`, exactly like the WS / EventSource / image / download URLs) instead — it coexists with upstream Basic auth, and agentgui accepts `?token=` on every HTTP route, plus the `agentgui_token` cookie.
10
10
 
11
+ ## CRITICAL — no Chrome/Puppeteer/Playwright dependency anywhere in this repo
12
+
13
+ Never add `puppeteer`/`puppeteer-core`/`playwright`/`playwright-core` as a dependency, and never hand-roll a raw headless-Chrome launch in a script. Live browser verification for this project is done via the gm skill's `browser` verb (direct CDP, no relay), not by this repo owning its own browser-automation dependency. `scripts/capture-screenshots.mjs` (puppeteer-core-driven, dead code — not wired into any CI workflow) was removed for this reason.
14
+
11
15
  ## Standing engineering rules
12
16
 
13
17
  - **All GUI/design decisions live in the kit (`../design`), none in agentgui.** New surface styling is a kit CSS rule, never an inline `<style>` or `style=` prop in agentgui. A new kit component must be re-exported through `src/components.js`'s barrel to be consumable — adding it to a component file alone leaves it invisible to the built bundle even with 0 lint errors; grep the built dist for the export name before wiring the app against it. New CSS class tokens in the kit must carry a registered family prefix (`ds-`, `app-`, `ws-`, `chat-`, etc. — see `scripts/lint-classes.mjs`'s `PREFIXES`/`FROZEN` lists) or the build's lint-classes check fails; a legacy bare name like `chip` being grandfathered on the FROZEN list does not cover new sub-tokens off it (e.g. a new `chip-remove` class still needs a `ds-` prefix).
@@ -79,7 +83,7 @@ Two runner protocols exist. **Direct** (`lib/claude-runner-direct.js`): claude-c
79
83
 
80
84
  ## CI / GitHub Actions
81
85
 
82
- **capture-screenshots must run under bun, not node** (gh-pages.yml `--ignore-scripts` leaves `better-sqlite3` uncompiled, so `bun:sqlite`'s fallback also fails under Node — detail in rs-learn). Rule: any CI step that spawns the agentgui server must invoke it with `bun`.
86
+ Any CI step that spawns the agentgui server must invoke it with `bun`, not `node` (`--ignore-scripts` npm installs leave `better-sqlite3` uncompiled, so `bun:sqlite`'s fallback also fails under Node).
83
87
 
84
88
  ## GM Plugin Autonomy Blocker
85
89
 
@@ -87,6 +87,9 @@ export function register(router, deps) {
87
87
  features: a.supportedFeatures || [],
88
88
  available: registry.isAvailable(a.id),
89
89
  npxInstallable: !!a.npxPackage,
90
+ // The CLI binary name a manual (non-npx) install would need - lets the
91
+ // settings panel say what to install instead of just "not installed".
92
+ cmd: a.command || null,
90
93
  }));
91
94
  return { agents };
92
95
  });
@@ -355,7 +358,11 @@ export function register(router, deps) {
355
358
  if (p?.file) args.push('--', assertSafeRelPath(p.file, 'file'));
356
359
  try {
357
360
  const { stdout } = await execFileP('git', args, { cwd });
358
- return { diff: stdout, file: p?.file || null, staged: !!p?.staged };
361
+ // git prints "Binary files a/x and b/x differ" for binary paths instead
362
+ // of a unified diff - an empty `diff` string then reads to the client as
363
+ // "no diff to show" with no explanation. Detect it so the UI can say why.
364
+ const binary = /^Binary files .* differ$/m.test(stdout);
365
+ return { diff: stdout, binary, file: p?.file || null, staged: !!p?.staged };
359
366
  } catch (e) { err(500, (e.stderr || e.message || 'git diff failed').trim()); }
360
367
  });
361
368
 
@@ -456,67 +463,4 @@ export function register(router, deps) {
456
463
  }
457
464
  return { subAgents: SUB_AGENT_MAP[p.id] || [] };
458
465
  });
459
-
460
- // --- models.availability: composes agentgui's REAL model surface into the
461
- // ModelsConfig component's shape (design SDK's ModelsConfig.js). agentgui
462
- // has no freddie-style probed-availability matrix (no /v1/models round-trip
463
- // per mode) - what it actually has is: discovered agent CLIs (registry,
464
- // same data agents.list already exposes), each agent's model choices
465
- // (agents.models' getModelsForAgent), and per-provider API-key presence
466
- // (auth.configs' getProviderConfigs). This handler is a read-only
467
- // composition of those three real sources, not a new probe and not fake
468
- // data - "usable_in_any_mode" is derived from registry.isAvailable(agentId)
469
- // (the CLI binary was actually found on this server), and "key_present"
470
- // comes straight from getProviderConfigs' filesystem check.
471
- router.handle('models.availability', async () => {
472
- const agents = registry.list();
473
- const providerConfigs = typeof getProviderConfigs === 'function' ? getProviderConfigs() : {};
474
- const providers = [];
475
- for (const a of agents) {
476
- const available = registry.isAvailable(a.id);
477
- let models = [];
478
- if (a.id === 'claude-code') {
479
- models = [
480
- { id: 'sonnet', name: 'Claude Sonnet (latest)' },
481
- { id: 'opus', name: 'Claude Opus (latest)' },
482
- { id: 'haiku', name: 'Claude Haiku (latest)' },
483
- ];
484
- } else if (typeof getModelsForAgent === 'function') {
485
- try {
486
- const raw = await getModelsForAgent(a.id);
487
- const list = Array.isArray(raw) ? raw : (raw?.models || []);
488
- models = list.map((m) => ({ id: m.id, name: m.name || m.label || m.id }));
489
- } catch { models = []; }
490
- }
491
- // Map onto agentgui's one real signal per model: is the agent CLI itself
492
- // available on this server (registry.isAvailable). agentgui does not
493
- // probe individual (model, mode) cells the way freddie's matrix does,
494
- // so every model under an available agent reports the same single
495
- // 'cli' mode rather than fabricating per-mode probe results.
496
- providers.push({
497
- id: a.id,
498
- key_present: !!(providerConfigs[a.id] && providerConfigs[a.id].hasKey) || available,
499
- discovery_error: available ? null : ((a.name || a.id) + ' CLI not found on this server'),
500
- models: models.map((m) => ({
501
- id: m.id,
502
- name: m.name,
503
- discovered_via: a.protocol || 'cli',
504
- modes: { cli: { ok: !!available, skipped: !available, reason: available ? undefined : 'agent_not_installed' } },
505
- usable_in_any_mode: !!available,
506
- })),
507
- });
508
- }
509
- const totalModels = providers.reduce((n, p) => n + p.models.length, 0);
510
- const usableModels = providers.reduce((n, p) => n + p.models.filter((m) => m.usable_in_any_mode).length, 0);
511
- return {
512
- timestamp: new Date().toISOString(),
513
- providers,
514
- sampler: [],
515
- summary: {
516
- total_providers: providers.length,
517
- total_models: totalModels,
518
- usable_in_any_mode: usableModels,
519
- },
520
- };
521
- });
522
466
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.1114",
3
+ "version": "1.0.1116",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
@@ -34,13 +34,11 @@
34
34
  "fsbrowse": "latest",
35
35
  "lru-cache": "^11.2.7",
36
36
  "opencode-ai": "^1.2.15",
37
- "puppeteer-core": "^24.37.5",
38
37
  "webjsx": "^0.0.73",
39
38
  "ws": "^8.14.2",
40
39
  "xstate": "^5.32.0"
41
40
  },
42
41
  "devDependencies": {
43
- "electron": "^35.0.0",
44
- "playwright": "^1.59.1"
42
+ "electron": "^35.0.0"
45
43
  }
46
44
  }