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 +0 -0
- package/.gm/mutables.yml +8 -0
- package/.gm/prd.yml +128 -5
- package/AGENTS.md +5 -1
- package/lib/ws-handlers-util.js +8 -64
- package/package.json +2 -4
- package/site/app/js/app.js +144 -146
- package/site/app/js/backend.js +7 -19
- package/site/app/vendor/anentrypoint-design/247420.css +2910 -805
- package/site/app/vendor/anentrypoint-design/247420.js +142 -69
- package/scripts/capture-screenshots.mjs +0 -206
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:
|
|
3216
|
-
subject:
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
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
|
-
|
|
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
|
|
package/lib/ws-handlers-util.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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
|
}
|