Skip to content

feat: add charts, preserve chat history and improve file tools - #146

Merged
Lancetnik merged 24 commits into
feat/a2ui-calendar-heatmapfrom
fix/a2ui-number-format
Oct 12, 2026
Merged

Lancetnik merged 24 commits into
feat/a2ui-calendar-heatmapfrom
fix/a2ui-number-format

Conversation

@Lancetnik

@Lancetnik Lancetnik commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

Why are these changes needed?

Long chats need to open on their recent messages and retain their visible history after compaction, show ongoing agent work consistently, and draw factual charts directly from data. This PR also bounds local file reads, validates block edits before writing, warns before file overwrites, simplifies A2UI card controls and preserves the current view when switching Drawer tabs. Transient provider connection failures and interrupted response streams now retry the failed model call before failing the Turn.

  • Add a shared native Chart primitive for line, grouped bar and stacked bar charts. Labels, series and event annotations accept data bindings. SVG rendering includes axes, legends, exact-value tooltips, an accessible data table, localized controls, theme colors and contained horizontal scrolling. Missing measurements remain null gaps. Authoring validation and charts.md document the supported contract.

  • Archive typed chat events for complete UI and card-draft replay while the model receives the current compacted context. Chat deletion removes archived segments. Compaction uses the current Turn's resolved model, including chat overrides, and retains a recent tail within half the token threshold, capped at 60 conversational events. Advanced Settings exposes a per-profile threshold, defaulting to 50,000 estimated tokens, applied to the next message without a restart.

  • Open the SPA chat on its latest ten complete turns, then request earlier event pages when scrolling near the top. Stable cursors survive archived compaction segments and reconnects restore the already loaded range. Prepending preserves mounted item identities and the visible scroll anchor; chronological replay applies later feedback, inquiry answers and card updates to earlier items. Loading status, retry and an explicit Load earlier messages button support slow or failed requests. The model switcher requests only the last transcript message. WebSocket replay buffers concurrent live events before the ready marker, and clients without the pagination option retain full replay. Pages limit transport and rendering; selection reads the existing complete event archive on the server.

  • Retain and render compaction started/completed/failed events across reconnects and restarts. Successful status remains in the timeline after subsequent messages; cancellation retires a pending status. Compaction has its own progress indicator.

  • Retry OpenAI/Anthropic connection and timeout errors, HTTPX/HTTPX2 network failures and interrupted response streams through the existing LLM middleware, including explicitly wrapped transport causes. Resolve the actual HTTP status before a provider-specific string code; fatal HTTP errors still stop immediately. The default two-retry budget and exponential backoff remain unchanged. Retrying preserves completed local tool results and does not restart the Turn; cancellation interrupts backoff. Document the retry/timeout settings, environment overrides and separate SDK retries.

  • Keep Thinking active through tool rounds and pauses before the next reply. A response containing text and tool calls is a continuing response; replay restores the same activity state. Streaming text, final responses and cancelled/failed turns have the appropriate indicator behavior. Aggregate one chip per tool across interleaved calls, empty continuing replies and observer alerts within a work block. Visible progress and user messages start a new group. Hover, keyboard focus or a tap opens the ordered list of distinct run details with skill, script and file names; repeated details share a count and time range; long lists scroll within the viewport. Call times occupy the left column, replacing numbering and the timestamps below each entry. Clickable filenames open files directly for read and write tools, including home-relative and absolute paths in granted folders; preview and download retain the current Thread grant scope. Only the Download icon sits beside the filename in that list, replacing standalone artifact and provenance badges. Clicking a list folder path opens Files, expands and selects that directory while preserving the current Thread. read chat details show the conversation title and identifier, falling back to its identifier when it is unavailable. Clicking the title opens that conversation and dismisses the run popover; the same action is accessible by keyboard. The popover closes on hover leave, Escape, an outside click or a second tap, without a redundant close button. Tools with no meaningful details keep a plain counted chip instead of a popover that repeats their name. Save confirmations distinguish card definitions from instances.

  • Show only the first source line of each shell command or code run, with an ellipsis and a down/up chevron to expand or collapse its complete, copyable source. Local and sandboxed variants preserve the language and repeated-run counts. Leaving a hover-opened popover closes it after clicking a chevron; keyboard focus retains it. Execution rows mount only while the popover is open, so reopening starts collapsed without a separate state reset.

  • Align the host file tools with Pi-style line reads and block replacements. read_file returns complete UTF-8 lines under a 500-line / 10 KiB cap, accepts 1-based offset and line limit, and reports file size, returned and remaining counts plus the next call to continue. Text with unfamiliar extensions is readable, relative paths resolve in the workspace, and PDFs/images retain visual reading. A line over the byte cap gives a bounded-shell fallback diagnostic. update_file accepts edits with oldText/newText, checks every unique, non-overlapping match against the original before writing, tolerates Unicode typography/trailing whitespace, preserves untouched lines and BOM/LF/CRLF style, and returns a numbered diff with patch metadata. It checks folder write grants for both workspace and external paths and replaces the native toolkit version. write_file creates UTF-8 files and parent folders, warns without writing when a file exists, and requires an explicit retry with overwrite=true to replace it. Permission checks apply to every read, edit and overwrite.

  • Reuse unchanged skill instructions and resources whose full text is already in the current context. Load missing or changed references, and reload needed text removed by compaction. Apply this guidance to the shared skill prompt, card authoring, rich views, Screens and table instructions.

  • Render authored card layouts without extra Overview/title/catalog decoration. Use consistent anchored save popovers with keyboard focus, viewport fitting, replacement/copy choices, retries and read-only behavior. Use the shared Select for directory selection. Numeric Metric values and changes omit trailing decimal zeros while preserving authored strings.

  • Switching to Files changes only the Drawer, preserving the current Chat, Task, run, Screen and file preview. Per-profile session selections survive refresh and history navigation; unsaved editor buffers remain available. Drawer switches alone do not reload Screen data.

  • Add Copy as image for cards, excluding service controls from the clipboard PNG and reporting success or errors. Resolved inquiries show a compact answer badge and retain their details toggle.

  • Move weather and market quote fetching beside the bundled WeatherPanel and MarketBoard definitions, and remove their AG2 wrappers and agent tool schemas. Backend refresh uses the same data providers and stable source IDs, so existing Chat and saved file instances continue to refresh. Tests cover HTTP parsing, saved-instance updates through typed events, and the tools actually offered to the model.

  • Use GitHub Issues as the source of truth for tasks and specs. Local .scratch/ files only buffer work currently in progress and are deleted after merge and issue closure. Contributor and triage instructions now use GitHub publishing, fetching and labels.

  • Offer only tools backed by the current profile's configured capabilities. Voice controls require credentials for the effective Active Live model or supported legacy provider fallback; image generation requires an image-capable Text model resolved for the current Turn and its key or ChatGPT sign-in. CLI execution requires a local ACP adapter or configured host bridge; disabled MCP servers contribute no tools. Diagnostic tools remain available. Existing chats refresh their schemas on their next Turn without network availability probes.

  • Resolve list_voices, set_voice, voice sessions and voice diagnostics through the same per-profile Active override / install-wide Active Live model. set_voice persists that model's voice for the next session; profiles sharing the model share its voice. Legacy profile voice settings remain supported when no named Live model is active.

Includes the regenerated SPA bundle, chart and activity browser fixtures, file-tool regression tests, and file-tool usage documentation. The profile scheduler unit test injects fake title and summary collaborators.

Related issue number

Follow-up to #145; stacked on feat/a2ui-calendar-heatmap.

Validation

  • ruff check ., ruff format --check . and mypy over 160 source files passed.

  • Full Python unit run: 2,616 passed, 2 skipped, 11 integration cases deselected on Python 3.13. Integration tests were not run.

  • Provider retries: 38 focused cases passed, including 30 new cases for real SDK/transport errors, HTTP status precedence, explicit causes, retry limits and cancellation. Real AG2 turns with the OpenAI Chat Completions and Responses SDKs use a mock HTTP transport to interrupt a stream after a completed tool call: both recover with the same request, produce the final answer and append the tool’s file marker exactly once. No external provider calls were made.

  • All 616 SPA tests passed, including the OpenAPI/zod contract gate. Strict Svelte typecheck reported zero errors and warnings; the production bundle was rebuilt.

  • Live Chrome: 88 activity and tool-run popover scenarios at each of 1280px and 360px widths, 52 chart fixtures and 150 existing layout fixtures passed with no console or network errors. Activity scenarios cover progress text with tools, tool-only responses, gaps between rounds, reopening during tools, interleaved aggregation across observer warnings, hover/focus/tap dismissal, complete long lists, repeated-detail counts and time ranges, clickable filenames, inline Download icons and call times in the left column, non-interactive chips without meaningful details, final replies, cancellation/failure and compaction. The actual chat was also checked for hover, keyboard dismissal and opening a file from its run list. Latest checks opened ~/Desktop/english/plan.md through the current chat grant and verified directory navigation to english, reports and sources, including expansion, selection and preservation of the chat. Browser fixtures also cover conversation titles, unavailable-chat fallback and navigation from the run list. Live Chrome additionally verified mouse and Enter navigation from read chat to the named conversation, with popover dismissal and no console or local network errors.

  • Execution details: Chrome fixtures verify single-line previews, complete multiline code, safe source rendering, grouped counts, independent expansion, contained scrolling, hover dismissal after pointer focus and collapsed reopening. The actual chat on port 8878 also passed mouse expansion, Enter collapse, down/up chevrons, hover leave after clicking the chevron, collapsed reopening, Tab focus retention and Escape dismissal, with no console or local network errors.

  • Progressive loading: 26 Chrome scenarios across 1280px and 360px widths passed, covering automatic loading, scroll anchoring, live arrivals during a pending page, duplicate request suppression, reconnects, retry, reaching the oldest page and discarding a response after chat navigation. In the actual long chat, initial replay decreased from 1,642 events / 10.6 MB to 713 events / 6.6 MB; three further HTTP requests restored all history with less than one pixel of anchor movement and no console or network errors.

  • Live chart preview additionally verified factual bound data, missing weeks, a lesson-start marker, interactive legends, the localized table and narrow-screen scrolling.

  • Earlier live browser validation covered persisted compaction settings and archived chat replay; Drawer/history navigation with an unsaved editor buffer; clipboard PNG output without service controls; and resolved inquiry details.

  • Built a wheel and verified that both Card data-provider modules and YAML definitions are included, and the former weather/finance agent tool modules are absent. Ran graphify update ..

Checks

  • I ran ruff check ., ruff format --check ., and pytest -m "not integration" locally and they pass.
  • If I changed anything under web/, I rebuilt and committed the SPA bundle (npm --prefix web run build).
  • I've included any doc changes needed for this change.
  • I've added or updated tests corresponding to the changes (if relevant).

AI assistance

Implemented with Codex assistance; reviewed the source diff, regression tests, regenerated bundle and live browser results.

  • I understand the changes in this PR and can explain them in my own words.
  • I have verified that this description accurately reflects the actual diff.
  • If AI assistance was used, I reviewed, tested, and validated the generated code/text before submitting.

@Lancetnik
Lancetnik added this pull request to stack #131 October 11, 2026 06:07
@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Branch coverage

Name Stmts Miss Branch BrPart Cover
src/assistant/a2ui.py 281 22 84 14 89%
src/assistant/a2ui_skill.py 125 9 36 7 90%
src/assistant/acp/approvals.py 58 1 8 2 95%
src/assistant/acp/chats.py 251 22 56 12 89%
src/assistant/acp/listeners.py 28 1 10 1 95%
src/assistant/acp/serve.py 66 16 4 2 74%
src/assistant/acp/serve_ws.py 103 0 24 3 98%
src/assistant/agent.py 278 31 78 8 88%
src/assistant/attachments.py 57 2 20 0 97%
src/assistant/builtin_tools.py 98 2 36 1 98%
src/assistant/card_author.py 63 13 22 8 73%
src/assistant/card_drafts.py 157 21 60 16 83%
src/assistant/card_instances.py 221 21 78 18 86%
src/assistant/card_sources/runtime.py 77 17 14 2 77%
src/assistant/card_sources/schema.py 91 13 44 13 81%
src/assistant/card_sources/service.py 235 20 102 23 87%
src/assistant/cards/__init__.py 336 30 152 21 90%
src/assistant/cards/bundled/marketboard.py 116 4 48 4 95%
src/assistant/cards/bundled/weatherpanel.py 119 6 34 1 95%
src/assistant/cards/layout.py 80 3 48 4 95%
src/assistant/channels/__init__.py 15 7 6 0 38%
src/assistant/channels/base.py 40 3 4 1 91%
src/assistant/channels/discord.py 136 73 44 1 42%
src/assistant/channels/formatting.py 121 2 32 3 97%
src/assistant/channels/router.py 544 15 186 14 96%
src/assistant/channels/slack.py 165 95 52 1 39%
src/assistant/channels/telegram.py 284 51 96 18 79%
src/assistant/cli.py 498 252 92 8 46%
src/assistant/codex_auth.py 241 67 54 5 71%
src/assistant/coding/bridge_client.py 44 7 4 1 83%
src/assistant/coding/bridge_server.py 115 30 22 4 74%
src/assistant/coding/diff.py 98 13 36 6 84%
src/assistant/coding/model_catalog.py 101 6 30 4 92%
src/assistant/coding/session.py 92 7 18 2 92%
src/assistant/config.py 204 22 58 7 89%
src/assistant/connections.py 264 18 78 14 91%
src/assistant/feedback.py 39 5 4 1 86%
src/assistant/filesearch.py 96 12 48 6 88%
src/assistant/folders.py 264 13 82 4 95%
src/assistant/gateway/app.py 478 184 126 20 58%
src/assistant/gateway/core.py 872 102 232 39 86%
src/assistant/gateway/openapi_schema.py 26 0 6 1 97%
src/assistant/gateway/profile_manager.py 410 43 118 23 87%
src/assistant/gateway/repair.py 66 3 26 1 96%
src/assistant/gateway/routes/card.py 122 14 28 6 87%
src/assistant/gateway/routes/card_instance.py 43 3 4 0 94%
src/assistant/gateway/routes/common.py 39 1 16 1 96%
src/assistant/gateway/routes/connection.py 167 2 42 2 98%
src/assistant/gateway/routes/file.py 194 4 72 6 96%
src/assistant/gateway/routes/folder.py 87 7 8 1 92%
src/assistant/gateway/routes/llm.py 252 17 54 6 92%
src/assistant/gateway/routes/permission.py 55 2 12 2 94%
src/assistant/gateway/routes/profile.py 73 4 8 0 95%
src/assistant/gateway/routes/secret.py 66 5 6 1 92%
src/assistant/gateway/routes/settings.py 208 18 26 8 89%
src/assistant/gateway/routes/skill.py 223 9 30 1 96%
src/assistant/gateway/routes/system.py 239 26 42 4 89%
src/assistant/gateway/routes/task.py 104 7 18 5 90%
src/assistant/gateway/stream_bridge.py 63 2 16 1 96%
src/assistant/gateway/tasks_service.py 407 50 130 17 86%
src/assistant/gateway/wire.py 27 4 6 1 85%
src/assistant/hitl/channel.py 21 2 4 2 84%
src/assistant/hitl/desktop.py 133 12 26 5 89%
src/assistant/hitl/gateway.py 22 4 2 1 79%
src/assistant/hitl/inquiry.py 168 6 36 5 95%
src/assistant/integrations/google_auth.py 136 67 24 0 49%
src/assistant/live_configs.py 131 8 48 11 89%
src/assistant/llm_configs.py 221 5 86 1 98%
src/assistant/memory.py 162 9 48 7 91%
src/assistant/middleware.py 91 2 20 3 95%
src/assistant/observability.py 69 5 10 0 94%
src/assistant/observers.py 97 0 24 1 99%
src/assistant/onboarding.py 64 2 26 0 98%
src/assistant/pairing.py 145 2 46 3 97%
src/assistant/peers.py 150 1 40 3 98%
src/assistant/permissions.py 300 21 106 8 93%
src/assistant/profiles.py 186 2 46 4 97%
src/assistant/provider_catalog.py 108 6 46 6 92%
src/assistant/resources.py 210 6 46 7 95%
src/assistant/scheduler_lock.py 33 0 4 1 97%
src/assistant/screen_skill.py 121 22 46 13 75%
src/assistant/screens.py 106 11 34 7 87%
src/assistant/secrets.py 262 4 78 2 98%
src/assistant/self_tools.py 82 21 18 4 71%
src/assistant/settings.py 196 10 56 8 93%
src/assistant/skills.py 55 3 14 0 96%
src/assistant/skills_install.py 167 19 56 12 86%
src/assistant/state_store.py 163 12 50 6 92%
src/assistant/storage.py 36 4 0 0 89%
src/assistant/system_tools.py 164 42 52 12 70%
src/assistant/tasks/scheduling.py 99 6 30 4 92%
src/assistant/tasks/store.py 168 19 56 7 88%
src/assistant/tasks/summary.py 38 2 2 1 92%
src/assistant/tools/__init__.py 94 6 44 3 93%
src/assistant/tools/_mcp_compat.py 25 3 2 1 85%
src/assistant/tools/approval.py 12 6 4 0 38%
src/assistant/tools/ask.py 14 6 2 0 50%
src/assistant/tools/coding.py 30 7 2 1 75%
src/assistant/tools/docker_sandbox.py 68 20 10 1 68%
src/assistant/tools/file_edits.py 85 1 34 1 98%
src/assistant/tools/files.py 156 18 46 4 89%
src/assistant/tools/google.py 186 132 36 2 28%
src/assistant/tools/image_gen.py 112 38 38 2 64%
src/assistant/tools/mcp.py 195 14 42 9 89%
src/assistant/tools/web_fetch.py 30 4 10 3 82%
src/assistant/usage.py 74 7 14 0 85%
src/assistant/visual_preview.py 154 113 40 1 22%
src/assistant/voice.py 53 15 8 1 67%
src/assistant/voice_providers.py 72 24 4 0 66%
src/assistant/workspace.py 271 25 88 3 92%
TOTAL 17304 2233 4676 582 85%

51 files skipped due to complete coverage.

@Lancetnik Lancetnik changed the title fix(a2ui): omit trailing decimal zeros in metrics fix(a2ui): simplify chat cards and unify save menus Oct 11, 2026
@Lancetnik Lancetnik changed the title fix(a2ui): simplify chat cards and unify save menus fix(chat): preserve compacted history and simplify card controls Oct 11, 2026
Render line and bar charts from bound data, retain compaction status for replay, and reuse skill references already present in context. Keep Thinking active across tool rounds and rebuild the SPA bundle.
@Lancetnik Lancetnik changed the title fix(chat): preserve compacted history and simplify card controls feat(a2ui): add native charts and preserve compacted chat history Oct 11, 2026
Group consecutive tool batches across empty continuing model replies while retaining Thinking activity. Cover counted labels, skill cards, replay, and visible message boundaries.
Aggregate interleaved calls across continuing replies and observer alerts. Group repeated details with counts and time ranges, move call times to the left column, and expose file actions as inline icons. Hide redundant provenance badges and empty popovers.
Replace the standalone preview icon with a clickable filename while retaining the Download icon, grouped call counts, and call times. Verify filename navigation and narrow-screen layout in Chrome.
@Lancetnik Lancetnik changed the title feat(a2ui): add native charts and preserve compacted chat history feat: add charts, preserve chat history and improve file tools Oct 11, 2026
@Lancetnik
Lancetnik merged commit f072294 into main Oct 12, 2026
18 checks passed
@Lancetnik
Lancetnik deleted the fix/a2ui-number-format branch October 12, 2026 08:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants