agentgui 1.0.1051 → 1.0.1053

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/AGENTS.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # AgentGUI — Agent Notes
2
2
 
3
+ ## GUI logic+predictability sweep (2026-07-12) — thirty-eighth run
4
+
5
+ `gui-logic-predictability` workflow wf_186e2a2a-dfc (32 agents, 205 tool calls, 0 errors) — 18 confirmed findings, all implemented, re-running the same lens one cycle after the 37th run to catch residual/newly-introduced gaps. App (`site/app/js/app.js`, `site/app/js/backend.js`): `stopAllActive` now clears every attempted sid (both ok and failed) from `state.live.stopping` after `Promise.allSettled`, so a failed bulk-cancel is retryable instead of leaving that row's stop button permanently disabled; `copyText`'s clipboard-write catch now calls `announce('copy failed')` instead of swallowing the error silently (mirrors `copySid`'s existing pattern); search-result row titles now route through `projectLabel()` (`r.snippet || projectLabel(r.title) || projectLabel(r.project) || UNTITLED_CONVERSATION`) matching the rail's contract; the History detail header's title-fallback chain gained `|| state.selectedSid` before the generic label, so a metadata-sparse session shows the same identity in History as it does in the rail; the Live nav-badge error flame no longer goes stale while off the history tab (stream stays connected whenever `state.active` is non-empty); `computeLiveSessions()` gained a 5s `reconnectGraceUntil` window so an SSE reconnect mid-session no longer flaps a running session's status to stale; a brand-new session between the `chat.active` poll and history-index catch-up now shows `'indexing…'` instead of reading identically to a permanently-failed lookup; the Live dashboard's sort/filter prefs (`hydratePrefs()`) now reconcile across browser tabs on focus/visibilitychange, alongside the existing `refreshActive()` call; `streamHistory`'s EventSource (`backend.js`) gained manual close+reopen exponential backoff (500ms start, doubles, capped 30s, resets on `hello`) mirroring the WS client's `scheduleReconnect`, instead of relying on native EventSource's uncapped retry; the sessions rail empty-state and History event-list both gained next-action affordances (a start-from-chat CTA, and a "load all (N hidden)" button for very large sessions); a draft restored from localStorage now surfaces a one-time note that undo history doesn't carry over a reload. Kit (`design/src/components/{agent-chat,chat,content,sessions}.js`, `app-shell.css`): follow-up suggestion chips' container lost its `aria-hidden="true"` — the chips are real interactive `<button>`s that were completely invisible to screen-reader users despite `role="group"` being present; `MdNode`'s markdown-settle `refSink` now defers its `innerHTML` swap via a one-time `selectionchange` listener when the user's active text selection is anchored inside the element (was wiping any in-progress text selection every streaming tick); `ChatComposer`'s paste handler now surfaces a "pasted N characters" note via `flashComposerNote` for large plain-text pastes; `SearchInput` gained a visible clear (X) button (was Escape-only, undiscoverable to mouse/touch users) at a 44px touch target, sharing the same clear path as Escape; `ConversationList` now forwards a `resultCount` prop through to `SearchInput`'s existing (previously unpopulated) `aria-live` result-count region, wired from agentgui's search-results branch as `hits.length + ' result(s)'`. Kit built+tested (`bun test.js` all pass), a concurrent upstream release (`5cdd667`, v0.0.315) required a rebase with one dist-only merge conflict (resolved by rebuilding `dist/247420.js` fresh from the merged sources rather than hand-merging generated code), re-vendored, browser-witnessed live on buildesk (chat tab shows `connected` status + visible follow-up chip buttons, history tab loads 52 conversations cleanly, 0 visible errors across both screenshots). Kit pushed `bed4036`, agentgui pushed `159030f` (after a second push-rebase for a PRD-state-only commit), all 4 CI checks green both repos. **New tool defect this run (recorded, not fixable from this repo — filed as `blockedBy:external`): the `gm browser` verb's `dom=` and `capture` prefixes both throw `ReferenceError` (no `document`/page binding at all) rather than evaluating in page context — a regression beyond the 37th run's already-documented "`dom=` ignores its trailing script" caveat. Only `screenshot=` and `url=<target>\n<literal-non-DOM-expr>` dispatches actually reach the real page in this tool version; browser sessions also silently rotate ids across dispatches with no state carryover, and one dispatch hit a dead extension-bridge session requiring an unprompted CDP relay restart. Screenshots are the reliable fallback for browser witnessing until this is fixed upstream in gm-plugkit/playwriter.**
6
+
3
7
  ## Punch-list document cleanup (2026-07-12) — 37th-run confirming pass
4
8
 
5
9
  The 10 standing `PUNCHLIST-*.md` files (COHESION/COMPLETION/DENSITY/DESIGN/DESIGN-14/DESIGN-15/LOGIC/PARITY/POLISH/UX, ~440KB) plus `AUDIT-PUNCHLIST.md` (found in a follow-up confirming pass) are removed — all their findings landed and merged in earlier commits, and their content duplicated what AGENTS.md already records in denser, current form, violating the project's own Documentation Policy (history belongs in git log/CHANGELOG, never a standing doc). Four `.claude/workflows/*.js` scripts (`gui-logic-predictability.js`, `gui-completion.js`, `gui-claude-code-parity.js`, `gui-screen-realestate.js`) referenced the punch-list filenames by name in agent prompts as the "don't re-report already-fixed findings" grounding source — all four now point at `AGENTS.md` alone. **Standing rule for future sweeps: no new `PUNCHLIST-*.md`/`AUDIT-PUNCHLIST.md` (or similarly-named) file should be checked into the repo as a durable artifact — a workflow's punch-list is a transient working document for that run's synthesis step; the run's outcome belongs in AGENTS.md (or drains to rs-learn), never a standing file.**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.1051",
3
+ "version": "1.0.1053",
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, 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;
6
+ const { AppShell, WorkspaceShell, WorkspaceRail, Topbar, Crumb, Side, Status, Chat, ChatComposer, AgentChat, ConversationList, SessionDashboard, Row, Panel, PageHeader, TextField, Select, Btn, Icon, IconButton, 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
@@ -437,6 +437,21 @@ async function stopActiveChat(sid) {
437
437
  state.active = (Array.isArray(state.active) ? state.active : []).filter(a => a.sessionId !== sid);
438
438
  await refreshActive();
439
439
  render();
440
+ // The backend accepted the cancel, but the underlying process can outlive
441
+ // this immediate refresh - if it's still showing active after a bounded
442
+ // wait, stop treating it as en-route-to-stopped: clear `stopping` (or the
443
+ // row wedges permanently disabled) and surface the same retry banner used
444
+ // on an outright rejection.
445
+ setTimeout(() => {
446
+ const st2 = state.live.stopping;
447
+ const stillActive = Array.isArray(state.active) && state.active.some(a => a.sessionId === sid);
448
+ if (st2 && st2.has(sid) && stillActive) {
449
+ st2.delete(sid);
450
+ state.live.bulkStopError = 'conversation did not stop - try again';
451
+ announce('stop failed');
452
+ render();
453
+ }
454
+ }, 9000);
440
455
  }
441
456
 
442
457
  function openLiveStream() {
@@ -507,6 +522,7 @@ function openLiveStream() {
507
522
  if (ev.type === 'tool_use') { t.tools++; t.toolRunning = true; t.toolName = ev.tool || ev.name || ''; }
508
523
  if (ev.type === 'tool_result') { t.toolRunning = false; t.toolName = ''; }
509
524
  if (ev.type === 'result' && ev.usage) { t.tokens = (t.tokens || 0) + (ev.usage.input_tokens || 0) + (ev.usage.output_tokens || 0); }
525
+ if (ev.type === 'result' && typeof ev.total_cost_usd === 'number') { t.cost = (t.cost || 0) + ev.total_cost_usd; }
510
526
  if (ev.isError) { t.errors++; t.lastErrorTs = t.last; }
511
527
  }
512
528
  state.live.tally.set(data.sid, t);
@@ -708,8 +724,13 @@ function workspaceRail() {
708
724
  action: { label: 'New chat', icon: 'pencil', onClick: () => { navTo('chat'); newChat(); } },
709
725
  items,
710
726
  // W18: a persistent theme toggle pinned to the rail bottom (the Claude-Desktop
711
- // / cowork pinned-bottom affordance). ThemeToggle is already imported.
712
- footer: ThemeToggle({ compact: true }),
727
+ // / cowork pinned-bottom affordance). A shortcuts-help icon sits alongside it -
728
+ // the '?' overlay (state.showShortcuts) was previously 100% keyboard-only
729
+ // tribal knowledge with zero visible entry point outside Settings.
730
+ footer: [
731
+ h('span', { key: 'rail-help' }, IconButton({ icon: 'help', title: 'Keyboard shortcuts', onClick: () => { state.showShortcuts = true; render(); } })),
732
+ h('span', { key: 'rail-theme' }, ThemeToggle({ compact: true })),
733
+ ],
713
734
  });
714
735
  }
715
736
 
@@ -1779,7 +1800,7 @@ function computeLiveSessions() {
1779
1800
  // Surface the in-page chat's own running cost and token count on its card
1780
1801
  // (the only session we hold reliable per-session data for; others omit it).
1781
1802
  cost: (state.chat.resumeSid && (r.claudeSessionId === state.chat.resumeSid || r.sessionId === state.chat.resumeSid))
1782
- ? (state.chat.totalCost || null) : null,
1803
+ ? (state.chat.totalCost || null) : (t && t.cost ? t.cost : null),
1783
1804
  tokens: (state.chat.resumeSid && (r.claudeSessionId === state.chat.resumeSid || r.sessionId === state.chat.resumeSid) && state.chat.usage)
1784
1805
  ? ((state.chat.usage.inputTokens || 0) + (state.chat.usage.outputTokens || 0)) : (t && t.tokens ? t.tokens : undefined),
1785
1806
  };
@@ -3121,6 +3142,15 @@ function resumeSidLabel(sid) {
3121
3142
  return label || UNTITLED_CONVERSATION;
3122
3143
  }
3123
3144
 
3145
+ // Same identity contract as the sessions rail/history rail/Live SessionCard:
3146
+ // a running row must show the resolved conversation title, not just agent
3147
+ // metadata - otherwise the same session reads as "nameless" here and named
3148
+ // everywhere else.
3149
+ function runningRowTitle(r) {
3150
+ const realSid = r.claudeSessionId || r.sessionId;
3151
+ return resumeSidLabel(realSid);
3152
+ }
3153
+
3124
3154
  function uniqueProjects() {
3125
3155
  const arr = Array.isArray(state.sessions) ? state.sessions : [];
3126
3156
  const seen = new Map();
@@ -3168,7 +3198,7 @@ function runningPanel() {
3168
3198
  // unkeyed one crashes webjsx applyDiff "reading 'key'").
3169
3199
  return h('div', { key: 'run' + r.sessionId, class: 'resume-banner', role: 'group' },
3170
3200
  h('span', { key: 'rd-' + r.sessionId, class: 'status-dot-disc ' + (isStopping ? 'status-dot-connecting' : 'status-dot-live'), 'aria-hidden': 'true' }),
3171
- h('span', { key: 'rl-' + r.sessionId, class: 't-meta' }, agentName + (r.model ? ' · ' + r.model : '') + (elapsedMs ? ' · ' + fmtDuration(elapsedMs) : '') + (r.cwd ? ' · ' + pathBasename(r.cwd) : '')),
3201
+ h('span', { key: 'rl-' + r.sessionId, class: 't-meta' }, runningRowTitle(r) + ' · ' + agentName + (r.model ? ' · ' + r.model : '') + (elapsedMs ? ' · ' + fmtDuration(elapsedMs) : '') + (r.cwd ? ' · ' + pathBasename(r.cwd) : '')),
3172
3202
  Btn({ key: 'open' + r.sessionId, onClick: () => navTo('live'), children: 'open in live' }),
3173
3203
  Btn({ key: 'stop' + r.sessionId, disabled: isStopping, onClick: () => stopActiveChat(r.sessionId), children: isStopping ? 'stopping…' : 'stop' }));
3174
3204
  }),