Skip to content

Add multiple features and fix many things#7

Open
the-dem wants to merge 115 commits into
undefinedopcode:mainfrom
the-dem:main
Open

Add multiple features and fix many things#7
the-dem wants to merge 115 commits into
undefinedopcode:mainfrom
the-dem:main

Conversation

@the-dem

@the-dem the-dem commented Jul 3, 2026

Copy link
Copy Markdown

I have been using this for a while, and adding features. I have kept these features local though.

This update makes kate-code more generic and works better with more providers. It uses more ACP features, and has more configuration options. It also fixes many bugs that caused it to crash or become unusable.

Please cherry pick as needed. The commit IDs will be munged, and a bunch of upstream will be in there, since my tooling found a log in there (it after running it, I figured it was from upstream, but it was still a log) and, because this is a company laptop, I sanitised out the log before pushing.

This was mostly vibed, and it was done by Claude Opus/Sonnet/Fable, Codex 5.x, Composer 2.5, and maybe some Kimi 2.x. I feel that, because of the subject of the application, this is probably OK, but feel free to reject code if you do not think it is.

undefinedopcode and others added 30 commits January 10, 2026 08:32
Enhanced permission request display for ExitPlanMode:
- JavaScript now checks if input has a "plan" field
- If present, renders plan content as markdown using marked.js
- Otherwise, shows JSON as before

This makes ExitPlanMode permission dialogs much more readable:
- Formatted headings, lists, code blocks
- Syntax highlighted code samples in plan
- Proper typography instead of JSON string

Added comprehensive CSS for .permission-plan:
- Markdown element styling (h1-h3, p, ul, ol, code, pre)
- Consistent with chat message markdown styling
- Proper spacing and colors for dark theme
- Tables support for plan documentation

The plan is now shown as a nicely formatted document rather than
raw JSON, making it easy to review before approving.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Bundle highlight.js and marked.js locally to avoid QWebEngineView CDN
blocking. Implement automatic light/dark theme detection based on KDE
color scheme luminance, dynamically applying appropriate highlight.js
themes (atom-one-light/dark) and code block backgrounds.

Add copy-to-clipboard button on code blocks with visual feedback and
base64 encoding for reliable copying of complete code regardless of
special characters. Include JavaScript bridge logging for debugging.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement ACP session mode switching following the Agent Client Protocol
specification. Users can select modes (Default, Plan, Accept Edits, Don't
Ask) from a dropdown above the input field, and the agent can also change
modes dynamically during conversation.

Add mode dropdown to ChatInputWidget with label and QComboBox showing
available modes. Parse availableModes and currentModeId from session/new
response. Implement session/set_mode method to change modes on user
selection. Handle current_mode_update notifications from agent when it
changes modes. Connect signals for bidirectional mode synchronization
between UI and agent.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement QCompleter-based autocompletion for slash commands (/) and
file references (@) in the chat input. Commands are populated from ACP's
available_commands_update notifications, and files from project scanning.
Also adds dynamic mode dropdown population from ACP, enhanced Edit/Write
tool handling with diff display, and project file listing with common
directory filtering.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ContextChunk struct for passing file selections as context
- Implement 'Add to Context' action in Kate editor context menu
- Add context chips UI to display attached file selections
- Update sendMessage to include context chunks in prompt payload
- Replace naive line diff with Myers' LCS-based diff algorithm

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds automatic Kate editor theme integration for syntax highlighting,
supporting both user themes and built-in KSyntaxHighlighting themes
(Breeze Dark, Dracula, Monokai, etc.). Code highlighting now matches
Kate's color scheme with proper token colors and backgrounds.

Implements proper LCS-based diff algorithm (similar to git diff) for
Edit tools, replacing naive line-by-line comparison. Shows minimal
change sets with context lines.

Adds support for multiple edits per Edit tool call. When ACP returns
multiple diffs in a single tool call, all are displayed with headers
showing "Edit 1 of N: filename" for each.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add SessionStore for saving/loading session IDs per project and
SessionSelectionDialog for resume vs new session choice. The ACP
session layer now separates initialize from session creation,
emitting initializeComplete to let ChatWidget decide the action.

Session resumption is currently disabled in ChatWidget since ACP
doesn't support it well - always creates new sessions for now.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add context menu quick actions: Explain Code, Find Bugs, Suggest
Improvements, and Add Tests. Each sends the selected code with a
prompt to Claude.

Include DiffHighlightManager infrastructure for showing pending
edits in the editor (currently disabled - diffs shown inline in
tool call UI instead).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Read Kate's configured editor font from katerc and inject it into
the chat WebView via CSS variables. Add shades-of-purple syntax
highlighting theme as an option.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Disconnect signals before cleanup in ACPService destructor to prevent
emission during destruction. Add Q_UNUSED for unused exitStatus param.

Update chat CSS and JS with various UI improvements.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update diff rendering to preserve hljs syntax highlighting within
diff lines by using inline spans and tinted backgrounds instead of
overriding text color. Also update theme converter to target pre.diff
selectors for consistent highlighting.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix race condition in ACPService::stop() by disconnecting signals
  before killing process to prevent concurrent cleanup
- Add dedicated styling for Task and TaskOutput tools in chat UI
  with subagent type badges, background/resume indicators
- Render Task tools with prompt details and model info
- Add tool-specific icons (🤖 Task, 📥 TaskOutput, etc.)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Introduce TranscriptWriter utility that saves session conversations as
markdown files to ~/.kate-code/transcripts/, including messages, tool
calls with diffs, and command results. Supports session resume with
continuation markers.

Update Task tool styling to use CSS variables (--task-purple,
--task-purple-bg) that adapt to light/dark themes. Refactor ChatWidget
header bar to use icon-only tool buttons with status indicator dot.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add ability to cancel running prompts via stop button or Escape key,
using the ACP $/cancel_request notification. Replace emoji icons with
Material Symbols throughout the chat UI for a cleaner look. Organize
transcripts into project-specific subfolders.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add debian/ directory and kate-code.spec for building .deb and .rpm
packages. Include Dockerfiles and build-packages.sh script for
cross-distro builds. Update README with installation instructions
for Arch, Fedora, and Debian/Ubuntu.

Also fix missing QJsonDocument include in TranscriptWriter.cpp for
stricter Qt header handling on Fedora.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Detect ExitPlanMode tool completion and automatically switch the mode
dropdown to the appropriate state. If launchSwarm is requested, switch
to "acceptEdits" mode; otherwise switch to "default" mode.

Track tool inputs by toolCallId to access parameters when tool completes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add Dockerfile.arch and update build-packages.sh to support building
.pkg.tar.zst packages using makepkg in a Docker container.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add SummaryGenerator and SummaryStore utilities for generating and
persisting session summaries. Integrate summarization into ChatWidget
and SessionSelectionDialog for improved session management.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Re-enable session resumption dialog that offers to resume with prior
session summary. Add markdown rendering for user messages in chat.
Clear todos when starting new session. Add debug logging for summary
generation flow.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Ensure session summaries are generated when Kate closes by:
- Connecting to QApplication::aboutToQuit in KateCodePlugin
- Adding prepareForShutdown() chain through View to ChatWidget
- Implementing waitForPendingRequests() in SummaryGenerator with
  timeout and event loop processing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
undefinedopcode and others added 30 commits March 7, 2026 10:43
Allow users to configure an MCP server JSON config file path per ACP provider.
This enables external MCP servers (like the oia ClickHouse/S3 server) to be
loaded when using Kate Code with different agents.

Changes:
- Added mcpConfigPath field to ACPProvider struct
- Added loadExternalMcpServers() helper to read ~/.cursor/mcp.json format
- Added setMcpConfigPath() to ACPSession
- Updated config UI to show/edit MCP config path in provider table
- Updated ChatWidget to pass MCP config path when configuring backend

The MCP config path is optional. When set, external MCP servers are loaded
from the JSON file and passed to the ACP session/new request alongside the
built-in Kate MCP server.

Co-authored-by: Cursor <cursoragent@cursor.com>
The working tree carried a coherent but uncommitted body of 1.3.1
changes (plain-text chat input, friendly permission-mode labels,
Unicode icon glyphs, a clear-output control, ordered WebView update
replay, and Codex KVP guidance). Leaving it uncommitted made further
work hard to review and risked losing it.

Commit the intentional source changes as a labelled baseline so
subsequent fixes produce clean, reviewable diffs. The untracked dist/
directory of old RPM artifacts is deliberately left out.

Verified: cmake --build succeeds on this tree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The agent output area showed a blinking block caret while a response
streamed (animation: blink 1s infinite), which the user found
distracting.

Replace the blink animation with a steady, fixed-opacity caret and drop
the now-unused @Keyframes blink rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was no global setting to pre-approve routine agent commands, so
every command permission request had to be answered by hand.

Add a global "allowed commands" glob list. SettingsStore persists it
under Permissions/allowedCommands and exposes isCommandAllowed(), which
matches a command against each pattern via
QRegularExpression::wildcardToRegularExpression (anchored, case
sensitive); an empty list approves nothing. A "Command Auto-Approval"
group on the Advanced config tab edits the patterns, one per line.

Wiring this matcher into the permission flow follows in a later commit.
The logic was prototyped by a Sonnet worker agent; it was reintegrated
here by hand because its worktree branched from origin/main and predated
the reworked config page.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closing Kate (or a window) while an agent session and its embedded
QWebEngineView were alive could crash during teardown. The main causes:
the QWebEngineView's web channel and pending runJavaScript callbacks
were torn down in an uncontrolled order, and because m_chatWebView is
constructed after m_session it is destroyed first, so when ~ACPSession
later emits disconnected(0) the still-connected onStatusChanged handler
called the already-freed web view.

Give ChatWebView a real destructor and sever session/web-view signals
before Qt destroys ChatWidget's children, so nothing re-enters a
half-destroyed object.

Implementation:
- ChatWebView::~ChatWebView() now marks the page unloaded, clears the
  pending-script queue, disconnects the view and bridge, stops any
  in-flight load, and detaches the web channel (page()->setWebChannel
  (nullptr)) symmetrically to setupBridge(); runJavaScript()'s callback
  is guarded with a QPointer so late Chromium results are a no-op.
- ChatWidget::~ChatWidget() disconnects m_session and m_chatWebView
  signals (idempotent via m_shutdownDone) but does not run summary
  generation, which would block on the network and pump the event loop
  inside a destructor. prepareForShutdown() keeps that heavier work for
  the application-quit path and is now null-safe.
- KateCodeView removes itself from the plugin's view list on destruction
  (new KateCodePlugin::removeView) so onAboutToQuit() cannot dereference
  a freed view; it no longer calls prepareForShutdown() from its
  destructor, where the tool view may already be gone.
- onStatusChanged() null-checks the settings store.

Prototyped by a Sonnet worker agent; the coordinator reworked the
shutdown-trigger placement to avoid a network wait and a use-after-free
the agent's first version would have introduced.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two separate Kate processes both registered the fixed DBus service name
org.kde.katecode.editor, so the second process's Kate MCP server could
not reach its own editor: agents in a second Kate instance lost the
katecode_read/write/edit/documents tools. A fixed WebEngine remote
debugging port (9222) collided the same way and was on by default.

Give each Kate process a unique primary service name and tell its MCP
server which name to use, keeping the legacy name as a best-effort
fallback so single-process behaviour is unchanged.

Implementation:
- EditorDBusService registers org.kde.katecode.editor.<pid> as the
  primary name and best-effort also claims the bare legacy name (first
  process wins; later ones skip it harmlessly). The object path is
  unchanged, so the service is reachable under either name.
- ACPSession::buildMcpServers() passes KATECODE_DBUS_SERVICE=<pid name>
  in the Kate MCP server's env.
- MCPServer reads that env var (falling back to the legacy name) for all
  five editor DBus calls.
- ChatWebView only enables QTWEBENGINE_REMOTE_DEBUGGING when the user
  sets KATECODE_REMOTE_DEBUGGING, so no fixed port is opened by default.

This lets two separate Kate processes each run an agent. Concurrent
agents within one process are handled by a separate gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Within a single Kate process all windows share one editor DBus service,
so letting two windows run agents at once would cross their MCP editor
calls. Rather than the large per-window routing rework, block the second
window cleanly (the fallback the user asked for).

Add a process-wide single-active-agent gate owned by the plugin and
consulted by each ChatWidget before it starts a session.

Implementation:
- KateCodePlugin tracks the owning widget in a QPointer (auto-clears if
  that window is destroyed) and exposes acquireAgentSlot/releaseAgentSlot
  /agentSlotAvailableFor plus an agentActivityChanged() signal; removeView
  also emits it so a closed window lets others refresh.
- ChatWidget gains std::function gate hooks (wired by KateCodeView to the
  plugin). ensureAgentSlot() guards all three connect entry points and
  shows a clear message when blocked; the slot is released on both the
  Disconnected and Error status transitions; refreshAgentAvailability()
  enables/disables Connect and Resume (only while disconnected) and is
  driven by the plugin signal.

Combined with the per-process DBus name, two separate Kate processes can
each run an agent, while extra windows in one process are safely blocked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The permission-mode dropdown always sent legacy session/set_mode, even
though modern agents (Claude, Codex) advertise a "mode" config option and
expect session/set_config_option; failed changes also left the dropdown
showing a mode the agent had rejected.

Make setMode() choose the transport and confirm the result, following
implementation.md.

Implementation:
- setMode() prefers session/set_config_option (configId "mode") when the
  agent advertised a "mode" config option, validating the value against
  the advertised choices; otherwise it falls back to session/set_mode.
- Interactive changes use a dedicated request id and handler, kept
  entirely separate from the startup per-provider config flow so session
  setup is never re-run.
- handleInteractiveModeResponse() drives m_currentMode from the returned
  configOptions on success (emitting modesAvailable/modeChanged), accepts
  a bodyless legacy success, and on failure re-emits the last confirmed
  mode so ChatInputWidget rolls the dropdown back; the session is not
  dropped.
- Rapid selections coalesce: one request in flight, the latest desired
  value queued and sent once the response arrives, so the agent converges
  on the final choice. State is reset on stop().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two robustness improvements for working across all ACP servers.

Kate context: the first-message system-reminder only listed the kate MCP
tool preferences, so the agent was not explicitly told it runs inside
Kate. Prepend a concise orientation: it is running in the KDE Kate editor
via Kate Code, the user's open files are its working context, file
operations should go through the kate MCP tools for in-editor review, and
the workspace root is the project directory.

Capabilities: image and embedded-context (resource) prompt blocks were
sent unconditionally, which can break agents that don't advertise them
(e.g. Codex). Parse agentCapabilities from the initialize response into
m_supportsImage / m_supportsEmbeddedContext / m_supportsPromptQueueing
(reset on stop, exposed via getters) and only append image and
resource/context blocks when advertised; the plain text block and the
reminder are always sent. supportsPromptQueueing and supportsEmbeddedContext
also feed later input-side features.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Submitting a message while the agent was still streaming eagerly created
a second assistant placeholder (a second streaming caret) and sent a
concurrent session/prompt, which agents without prompt queueing mishandle
and which broke the single m_promptRequestId matching.

Show the follow-up user message immediately but queue the send until the
current turn finishes, so only one streaming placeholder exists at a time
and prompts are sent one at a time (uniform across all ACP servers).

Implementation:
- Extract the assistant-placeholder + prompt-block + session/prompt send
  into dispatchPrompt(); sendMessage() now always emits the user message,
  then either queues a QueuedPrompt (when isPromptRunning()) or dispatches.
- On prompt completion, after clearing m_promptRequestId, flush one queued
  prompt via dispatchPrompt(); the queue is cleared on stop() and on
  cancelPrompt().

The normal, not-busy single-message path is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Input-area UX improvements the user asked for.

- Resizable input (Task 9): the chat view and the input area now sit in a
  vertical QSplitter, so the input box can be dragged larger. resizeEvent
  caps the input area at half the widget height so it can never crowd out
  the chat view. The text edit drops its fixed 100px cap and expands to
  fill the pane. resetWebView() swaps the view via QSplitter::replaceWidget
  and defers deletion with deleteLater.
- Waiting indicator (Task 10): a label in the mode row shows "Waiting for
  your input" when the agent is idle and connected, or "Agent is working…"
  while a prompt runs; blank when disconnected. Driven by the existing
  setEnabled()/setPromptRunning() calls.
- File-include button (Task 11): a button opens a file picker; text files
  (by MIME, with a UTF-8/NUL heuristic fallback, capped at 1 MiB) are
  inlined into the message as a fenced block, while binary or oversized
  files insert their path so the agent can read them via the Kate MCP/fs
  tools.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two related conveniences.

Save output (Task 13): a header button exports the current session's
Markdown transcript to a user-chosen file. ACPSession::transcriptFilePath()
exposes the live TranscriptWriter path; the button copies it via a save
dialog (overwrite-safe) and reports success/failure as a system message,
or notes when there is nothing to save yet.

Command auto-approval (Task 14): wires the global allow-list into the
permission flow. onPermissionRequested() extracts the command from the
request input, and when it matches SettingsStore::isCommandAllowed() and
a clear allow option exists (kind allow_once/allow_always, else an
allow/approve/yes option), it sends that outcome immediately and notes
the auto-approval instead of prompting. It is deliberately conservative:
it never auto-rejects, falls through to the dialog whenever anything is
ambiguous, and does nothing when the allow-list is empty (the default).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reflect the feature set added on top of 1.3.1: crash-on-close fix,
multi-window support (per-process DBus name plus a single-agent gate),
preferred set_config_option mode switching, capability detection and
Kate orientation, prompt queueing, resizable input with a waiting
indicator and file-include, save-output, and command auto-approval.

Update CMakeLists.txt, src/katecode.json and kate-code.spec (1.4.0-1)
with a changelog entry. No RPM was built or published.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace comments that referenced internal task numbers (e.g. "Task 9")
with self-describing wording, since those numbers mean nothing in the
repository.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A Codex upstream failure arrived as a session_info_update with
threadStatus systemError, which Kate ignored entirely, plus a normal
message chunk carrying "stream disconnected … request ID" — so the
failure was invisible or looked like ordinary assistant output, and the
session felt dead. Genuine ACP error responses were only logged too.

Detect and show these as distinct error messages.

Implementation:
- ACPSession emits a new sessionError() on session_info_update when the
  Codex threadStatus is systemError/error, without disturbing the normal
  turn finalisation (the trailing chunk and prompt response still run). A
  defensive final else logs any unhandled session/update type.
- ChatWebView::showError() renders a red-tinted "error" message
  (base64-passed, textContent so no injection); chat.js/chat.css gain the
  addErrorMessage() renderer and .message.error style.
- ChatWidget surfaces both sessionError and onError() in the chat.
- ACPService no longer routes bridge stderr through errorOccurred, so the
  now-visible error channel carries only genuine errors, not noisy
  informational stderr.

This does not stop the upstream disconnect (that is OpenAI/bridge side),
but makes Kate report it clearly and stay usable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The agent's streamed reply could fail to appear at the end of the visible
log, and a saved transcript captured only the user message. Several
causes: scrollToBottom() scrolled the window, but the real scroll
container is #messages, so streamed output stayed below the fold; a fresh
Connect reused the previous session's page, leaving stale content; the
assistant transcript was only recorded on agent_message_end, which the
Claude agent never sends (it ends the turn via the prompt response); and
JS exceptions in injected calls were silently swallowed.

Fixes:
- scrollToBottom() now scrolls the #messages element (window scroll kept
  as a harmless fallback).
- onConnectClicked() clears the web view before starting a fresh session
  so the log begins clean.
- The session/prompt completion path records the assistant message to the
  transcript when agent_message_end did not (idempotent: that handler
  clears the content, so there is no double-record).
- runJavaScript() wraps injected scripts in try/catch and qWarns any
  exception (with script context), so a render failure is no longer
  invisible.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ships the Codex/ACP error surfacing and the agent-output-to-end-of-log
fixes on top of 1.4.0. Updates CMakeLists.txt, src/katecode.json and
kate-code.spec (1.4.1-1) with a changelog entry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The session summariser was a separate mechanism: it POSTed to the
Anthropic HTTP API with a KWallet-stored key and a model chosen from
a hard-coded two-entry combo box. If the hard-coded Claude models are
renamed or withdrawn, or the user wants a different/cheaper model or
vendor, there is no way to select one, and summaries silently depend
on an API key that nothing else uses.

Summaries are now produced by ACP agents instead. The Session
Summaries settings group gains a "Summary agent" dropdown listing the
configured providers plus a "Current agent" entry, which is the new
default: when the session ends, the live agent is asked to summarise
itself from its own context, with a cancellable progress dialogue
while the reply streams in.

Implementation:
- SettingsStore: new summaryProviderId()/setSummaryProviderId()
  (key Summaries/providerId, defaulting to the __current__ sentinel)
  and providerById(). summaryModel() remains but is no longer used.
- SummaryGenerator: rewritten from QNetworkAccessManager to a headless
  ACP run (initialize -> session/new with no MCP servers ->
  session/prompt -> collect agent_message_chunk text) per job, keyed
  by ACPService instance. Tool permission requests are declined so a
  misbehaving agent cannot hang a summary. The public interface is
  unchanged; waitForPendingRequests() default timeout rises to 60 s
  because a subprocess is slower to start than an HTTP call.
- ACPSession: new requestSummary()/cancelSummary()/summaryResult().
  While a summary prompt is in flight its chunks accumulate silently
  (no chat messages, no transcript entries); stop() and disconnects
  fail the summary so waiters return.
- ChatWidget: disconnect now summarises before stopping the session so
  the current agent is still available; the Disconnected handler keeps
  triggering for unexpected agent deaths, where the current-agent
  option falls back to a headless run of the active provider over the
  saved transcript (now read via SummaryStore instead of a hand-built
  path). The API-key gating of summaries is gone.
- Config page: summaries no longer disabled when KWallet or the API
  key is missing; the summariseOnResume path drops its key check.

The Anthropic API-key storage and settings UI are now unused and will
be removed separately.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resuming a session by context injection sent the prior summary or raw
transcript as "Summary from last session:" with no further guidance.
A raw transcript ends with the last user prompt and agent turn, so
agents routinely read it as a fresh instruction and re-perform the
final task of the old session. There was also no feedback that the
restored context had actually been read and understood.

Wrap the injected record in a system-reminder that states this is a
session restore, that the material is background context only (no
acting on instructions in it, no resuming work, no file changes), and
that the agent should reply with a single sentence describing what
the previous session covered before waiting for the user. That one
line doubles as visible confirmation to the user that the restore was
processed.

Applies to both summary and raw-transcript injection; true resume
(session/load) is unaffected as the agent restores its own context.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With summaries generated through ACP agents, the Anthropic API key
and the KWallet machinery that stored it no longer have any consumer,
yet the Advanced tab still claimed the key was "used for generating
session summaries" and the summaries checkbox was greyed out whenever
KWallet or the key was missing.

Delete the dead path outright:

- SettingsStore: loadApiKey()/saveApiKey(), the KWallet open/close
  state machine, the apiKeyLoaded/apiKeySaved/walletError signals and
  summaryModel()/setSummaryModel() are gone. The stale
  Summaries/model key is removed from storage on startup.
- KateCodeConfigPage: the whole "Anthropic API Key" group and its
  slots are removed; summary controls no longer depend on the wallet.
- Build: KF6::Wallet and Qt6::Network are no longer linked (the
  network module was only used by the old HTTP summariser).

The key itself is left untouched in the user's wallet; kate-code
simply no longer reads it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A five-way review of the new summariser code and the wider codebase,
with every finding adversarially verified, confirmed a set of real
bugs in the summary flow, the ACP layer, terminal handling and the
single-agent gate.

Summary flow:
- requestSummary() raced the prompt it had just cancelled: cancel is
  only a notification and chunks carry no request id, so fragments of
  the aborted turn were collected into the saved summary, and agents
  that reject concurrent prompts failed the summary outright. The
  summary prompt is now parked until the cancelled prompt's response
  arrives.
- Tool permission requests during a summary turn are auto-declined
  (mirroring the headless generator) instead of rendering an
  unanswerable prompt behind the modal wait dialogue.
- generateSummaryWithCurrentAgent() hardened: heap-allocated dialogue
  and QPointer guards survive widget destruction mid-wait; the
  summaryResult connection uses a stack context object so
  prepareForShutdown()'s blanket disconnect cannot sever it; a
  re-entrancy flag stops shutdown or the Disconnected handler from
  stacking a second summary; WaitForMoreEvents stops the wait loop
  busy-spinning a core.
- resolveResumeContext()'s summarise-on-resume wait now shows a
  window-modal dialogue (no more re-entrant clicks corrupting session
  state) and both it and its caller guard against destruction.
- Headless summary jobs get a hard 120 s timeout so a silent agent no
  longer leaks a subprocess per session end; unknown provider ids fall
  back to the active provider; a crashed session is no longer
  summarised twice (m_userSentMessage now resets in the Disconnected
  handler); m_lastProjectRoot uses the directory the session actually
  started in rather than re-querying the provider mid-handshake.

ACP layer:
- A JSON-RPC error response to session/prompt now finalises the turn
  (previously isPromptRunning() stayed true and every later prompt
  queued forever); initialize/session-new errors move the status to
  Error instead of wedging on "Connecting".
- Unexpected agent death now runs the same cleanup as stop():
  transcript finalised, terminals released, prompt queue and request
  ids cleared, so reconnect after a crash works.
- ACPService buffers raw bytes and splits lines before UTF-8 decoding
  (multi-byte characters straddling a 64 KiB read no longer corrupt),
  and clears the buffer on start() so a crashed process's partial
  line cannot poison the next session's first message.

Terminals:
- waitForExit() re-looks the terminal up after its nested event loop;
  the old reference dangled if the terminal was released during the
  wait (use-after-free on disconnect during a long command).
- terminal/create with an args array now execs the program directly
  instead of joining args with spaces into `bash -c`, which destroyed
  argument boundaries and re-interpreted metacharacters.

UI/config:
- The single-agent slot is released again when Resume Session is
  aborted, and re-acquired after the stop()/start() cycle in New
  Session and Resume so two agents can no longer run concurrently.
- Provider update/remove/reorder now start from the effective provider
  list, so edits are no longer silently discarded when the list came
  from the defaults fallback; removing the provider selected as the
  summary agent resets the setting to "Current agent".
- TranscriptWriter and SummaryStore now derive identical project
  folder names for trailing-slash and empty roots.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Users intermittently see "MCP error -32000: Connection closed" from
the kate MCP tools: the client-side error a dead MCP server produces
on every subsequent tool call. Three defects in kate-mcp-server could
end the process (or the connection) prematurely:

- The stdin loop treated any read() failure as end-of-input and quit.
  A signal-interrupted read (EINTR) or a spurious socket-notifier
  wakeup on a non-blocking pipe (EAGAIN; node-based parents such as
  claude-code can leave the shared pipe description non-blocking)
  silently killed the server mid-session. These errnos now simply
  return and wait for the next activation.
- MCP ping requests were answered with -32601 "Method not found".
  The spec requires a prompt empty result, and clients may treat a
  failed ping as a stale connection and close it. ping is now
  handled.
- Responses were written with a single unchecked write(): a large
  katecode_read reply could be partially written (corrupting the
  JSON stream), and a client tearing down its pipe could kill the
  server via the default SIGPIPE action. Writes now loop over
  partial writes and EINTR, and SIGPIPE is ignored so the process
  ends via the clean stdin-EOF path instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Feature release: ACP-agent session summaries with a current-agent
option, session-restore framing, removal of the Anthropic API-key
path, a sweep of review fixes, and kate-mcp-server robustness.

kf6-kwallet-devel is dropped from the spec BuildRequires to match the
removed KWallet dependency.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kate-mcp-server crashed with SIGABRT ("has encountered a fatal error
and was closed") whenever a kate MCP tool was invoked. The coredump
backtrace ended in libdbus aborting from dbus_message_new_method_call
via the QDBusInterface constructor:

  assertion "_dbus_check_is_valid_bus_name (destination)" failed

The per-process editor service name (added in 1.4.0 so multiple Kate
windows can each own an agent) was built as
"org.kde.katecode.editor.<pid>". A D-Bus well-known name element must
not begin with a digit, so the all-digit pid element is an invalid bus
name; libdbus aborts the whole process when handed it as a call
destination. This also meant the per-process name never actually
registered, so the feature never worked as intended.

Prefix the pid element with "p" ("...editor.p<pid>") in both places
that build the name — EditorDBusService::registerOnBus() (the
registrar) and ACPSession::buildMcpServers() (which passes it to the
child via KATECODE_DBUS_SERVICE) — so the name is valid and the two
sides still agree.

Also harden the MCP server: validate the target name against the D-Bus
bus-name grammar in handleToolsCall() before constructing any
QDBusInterface, returning a normal MCP error if it is malformed. A bad
name can no longer abort the server and kill the agent's tool access;
it degrades to a visible error instead.

Reproduced and verified: the old "...editor.12345" name aborted the
1.5.0 binary (exit -6) on the first tool call and now returns a clean
error; the fixed "...editor.p12345" name is accepted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Patch release for the kate-mcp-server per-process D-Bus name crash fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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