Skip to content

chore(release): 0.13.0 - #24

Merged
THPubs merged 19 commits into
mainfrom
dev
Oct 20, 2026
Merged

THPubs merged 19 commits into
mainfrom
dev

Conversation

@THPubs

@THPubs THPubs commented Oct 20, 2026

Copy link
Copy Markdown
Member

0.13.0 — 2026-10-20

Kriyon is now open source, and it can update itself.

Added

  • Updates inside the app. Kriyon tells you when a new version is out and
    installs it when you say so. Your sessions keep running.
  • Install with Homebrew: brew install leafycode/tap/kriyon.
  • Easier to read conversations. Your messages and Claude's replies now
    look different, so you can tell them apart at a glance.

Changed

  • Clearer activity. You can see at a glance what Claude changed on your
    computer, such as installed or deleted files.
  • New font and colors for text and code.

Fixed

  • The Structured view now matches the terminal more closely.
  • Pasted text and long messages show and send correctly.
  • The message box fits small windows.
  • Closing a session cleans up its leftover files.

Security

  • Tighter limits on what the app window can load.

🤖 Generated with Claude Code

THPubs and others added 18 commits October 18, 2026 11:19
The whole tree as of the move to a public repo. Everything since 0.12.0 ships in 0.13.0; CHANGELOG.md and docs/adr record the history before this commit.
* ci: keep minor Tauri bumps out of Dependabot's weekly groups

tauri build refuses an npm Tauri package and its Rust crate on different
minor versions. Dependabot bumps npm and Cargo in separate pull requests, so
each one failed the macOS build (#2, #4). Dependabot now proposes only patch
updates for @tauri-apps/* and tauri*; a minor or major Tauri bump is done by
hand, both sides together. Security updates are not affected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci: hold back minor bumps only for Tauri packages with a twin

The tauri* pattern also matched tauri-build, tauri-plugin-positioner and
tauri-plugin-updater, which have no npm side, so their minor updates would
have stopped with nothing to remind anyone. The rule now names the seven
npm/Rust pairs on each side. The comment also says a security update can
still bring one side's minor alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(adr): name the Tauri pairs and the security-update exception

ADR 0043 said Tauri packages get only patch updates from Dependabot. Security
updates ignore update-types, so it now says which packages pair up and that
a security update can bring one side's minor alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ary (#8)

* fix(structured): keep the turns before a compaction and hide its summary

After Claude Code compacts a session, the Structured view lost every turn
from before the compaction and showed Claude Code's summary as the newest
prompt, so the view no longer matched the terminal.

Two things caused it. The compact_boundary row has no parentUuid; it links to
the row before it through logicalParentUuid, so the parent chain stopped
there. And the summary is a user row (isCompactSummary) that opened a turn of
its own. The chain now follows logicalParentUuid when parentUuid is missing,
the summary is treated like a meta row, and a compaction that lands after a
turn's reply no longer keeps that text from being read as the reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(structured): skip a compaction's summary instead of treating it as meta

The summary row counted as meta, so it joined the turn before the compaction
and moved that turn's end time to when the compaction ran. A turn that took
seconds could then read as taking hours. The summary now carries the chain
and is otherwise left out of the view, like an attachment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(structured): say the main chain crosses compactions

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ptyd): remove only the bridge socket this daemon bound

A daemon removed whatever path KRIYON_BRIDGE_SOCKET named when it ended,
whether or not it had bound that socket. A daemon started from inside a
Kriyon session, such as one from the kriyon-ptyd tests during `pnpm check`,
inherits that session's bridge path. It can't bind it while the session's
own daemon serves it, but on exit it still deleted the file. The session's
daemon kept listening on a socket nobody could reach, so its companion
stopped reaching Kriyon and the Structured view stopped updating.

The daemon now records the device and inode of the socket it binds, and
removes the path only when it still points at that file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ptyd): never pass a Kriyon session's bridge socket to a test daemon

Most kriyon-ptyd tests spawned the daemon with the environment they ran in.
Run from inside a Kriyon session, each test daemon was handed that
session's KRIYON_BRIDGE_SOCKET. Each spawn now removes it, as
stop_ends_the_whole_tree already did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ptyd): leave a bridge socket someone else bound over the daemon's

Nothing tested the device and inode check: removing whatever the daemon had
once bound passed every test. The new test unlinks the daemon's socket,
binds another at the same path, and checks it survives the daemon's stop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ptyd): record bridge ownership in the same step as the bind

The bridge thread created the socket file a moment before recording it as
its own, and the daemon's way out didn't wait for it. A daemon that stopped
in that gap left its own socket file behind. A stop before the thread had
bound at all let it bind afterwards, with the same result.

Binding and recording now happen under one lock, which the way out takes
too, and once the daemon is stopping a bind that hasn't happened yet is
refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ut of the newest turns (#10)

* fix(structured): keep live rows cut off with the transcript's start out of the newest turns

The Structured view reads only the newest 12 MiB of a transcript. Once a
session's transcript grew past that, the companion's live rows from its
start were no longer in what was read, so they counted as rows the
transcript had not written yet and were shown after the newest turn. The
view then ended on the session's first turns instead of its latest one.

The transcript only lags at its end, so a live row is now still to be
written only when it comes after the last live row the transcript holds,
and when it isn't older than the oldest row read. The second rule covers
live rows from an earlier companion run that share nothing with what was
read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(structured): pin that a row cut off just before the transcript read stays out

Removing the "after the last live row the transcript holds" rule passed
every test, because the cut-off rows in the fixtures were also older than
the oldest row read. The new test has a cut-off row received a moment after
that row was written, so only its place in the live rows keeps it out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The release check ran `stapler validate` with its output thrown away, so
when the 0.13.0-beta.3 build failed it the log said only that the DMG had no
stapled ticket, seconds after stapling had succeeded. The failure now
includes stapler's own output.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
#12)

* refactor(ui): move the reply Markdown renderer into a shared component

The Structured view drew Claude's replies with its own Markdown renderer.
It moves, unchanged, to src/ui/Markdown.tsx as `Markdown`, with the inline
text, links and copy button it uses, so the update dialog can draw release
notes the same way. `Markdown` takes a `size`: body for replies, small for a
dialog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(update): draw the release notes as Markdown

The update dialog showed the release notes as raw Markdown, with the `###`
and `-` marks in view. It now draws them with the shared Markdown
component: headings, lists, bold and code as they read on GitHub.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(update): give the update pill an icon and the accent

The "Update available" pill now has a sparkle icon with a slow glow, an
accent tint and border, and a tooltip naming the version on offer. It pops
in when an update arrives; reduced motion stills both animations.

Its tint goes in through --kr-fill, as `fillVars` (now exported) does for
the other hover-classed controls. The old pill took chip()'s inline
background, which outranked the hover fill, so it never lit up on hover.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): leave file paths as text where there is no folder to open them in

The update dialog draws release notes with the shared Markdown component
but has no session folder, so a path in the notes (`src/ui/Markdown.tsx`,
`~/.claude/settings.json`) became a link whose click always failed with
"cwd is not a directory". Without a LinkCwd folder, file paths now stay
text and only URLs are links.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(theme): give chip() a pressable variant that keeps its hover

chip() painted its background inline, which outranks .kr-hover-surface's
hover fill, so the update pill had to spread fillVars and then unset
background by hand. chip({ fill, hover }) now publishes both through
--kr-fill for a chip that is a button, and the hover-seam test covers it.
fillVars is private again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(layout): let the companion chip light up on hover

The session header's companion chip spread chip()'s inline background onto
a button with .kr-hover-surface, so the hover fill never showed. It now
uses the pressable chip, which leaves the fill to the hover class.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(theme): make the hover-seam scan see the pressable chips

The scan that checks every host of a --kr-fill recipe also carries a hover
class didn't know chip({ … }), so a pressable chip that lost
.kr-hover-surface would show no fill at rest and every test would pass.
chip({ is now one of its recipes.

The scan reads a style only when it is declared at a file's top level and
ends within its window, so the two pressable chips are shaped to be read:
the companion chip's base style moves to the top of its file, and the
update pill's icon comment becomes a doc comment. Taking either chip's
hover class away now fails the scan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…width (#13)

* fix(composer): keep the footer in one row and Send in the box at any width

In a narrow window the composer's footer broke: the usage readout was the
only part allowed to shrink, so "330k · 33% ctx" wrapped onto four lines,
and the buttons, which can't shrink, pushed Send out past the box's edge.

Nothing in the footer wraps now, and the right group (hide, Stop, Send)
never shrinks. As the footer narrows, the rest steps down in a fixed order,
least useful first: the ⌘⇧P hint, the Session chip, "Save prompt" to its ✦,
the usage readout, and "Prompts" to its icon. `footerFit` in
composerFooterFit.ts decides the steps from the footer's measured width; as
a last resort past them, the left group clips instead of pushing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(composer): fit the footer by measuring it, not by estimating widths

footerFit decided what fits from pixel widths copied by hand from the
button, chip and keycap styles, and the copies already disagreed with them
(an icon button is 27px, not 26). The next change to any of those styles
would have brought the clipped footer back with the tests still green.

The footer now measures itself: after each layout, while the tools' content
is wider than their box, it gives up one more step (fitAfter holds the
order). The steps run in layout effects, so only the settled footer is
painted, and they start again from everything when the footer's width, the
usage readout or Stop changes. The fitting lives in FooterTools, so a step
re-renders the tools and not the whole composer. No width is estimated
anywhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(composer): re-fit the footer when it widens, from one fitting module

The footer's width was read from the footer's own ref, inside the tools
component it contains. React runs a child's layout effects before it
attaches the parent element's ref, so the width observer was never created:
once the footer had given parts up, widening the window never brought them
back. The width now comes from the box the tools sit in (their own ref's
parent), which is attached in time, and a test widens a composer after it
mounts.

The fit also started again only when a hand-built key changed, so any new
input left out of it would have left the footer stuck stripped. It now
starts again whenever the tools get new props or their box changes width,
and the measuring hook lives beside the order it steps through, in
composerFooterFit.ts, with nothing for the caller to list. A 1px allowance
keeps a rounding difference between scrollWidth and clientWidth from
counting as overflow.

The layOutTools test helper now removes its prototype override when done:
jsdom keeps scrollWidth and clientWidth on Element.prototype, so the old
restore left the override in place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(structured): open a turn when a subagent hands back its report

When a background subagent finishes, Claude Code wakes the main agent
with a meta user row stamped origin.kind "peer". The Structured view
did not open a turn for it, so the work that followed joined the
previous turn, which had already recorded its end. The view then told
the backend the turn was over, and the session read Done in the
sidebar while Claude was still running a tool.

A hand-back now opens its own turn as a Work report ("Agent "…"
reported back"), named by the Agent step that launched it. A hand-back
that only echoes Claude Code's own report for that agent is dropped.
Claude writing after a turn's recorded end also reopens that turn, so
any other unrecognised wake-up cannot settle the status to Done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(structured): match an echoed hand-back by its agent id

A hand-back was told apart from Claude Code's own report by the end of
its summary, and matched to it by the text between quotes. A hand-back
was then dropped beside any other work of the same name, such as a
background command called "Build", or kept twice when its launch was
not in view and it had no name.

Each Work report now keeps the id of the work it is about (a report's
task-id, or the agent a hand-back came from) and whether it is a
hand-back. Neither is sent to the frontend. A hand-back is dropped only
when Claude Code's own report for the same id is in the turn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
#15)

While Claude works, Claude Code posts its own copy of each message it
queues for the model, such as a subagent's hand-back or a background
task's report, through the companion's "queue" door. The transcript
never writes these copies. Until the transcript caught up, the
Structured view read each one as a typed prompt and opened a turn
showing its raw <agent-message> or <task-notification> text.

Live rows from the queue door are now left out, as the transcript
leaves them out. A hand-back still opens its turn from its peer row,
and a report that wakes Claude from its own prompt row.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(companion): say hello again after a resume or /clear

After /resume or /clear, Claude Code ends the old conversation and goes on
under a new one without firing session.start, so the companion posted
nothing until the first prompt there. Kriyon had already moved the session
to the new conversation, found no companion posts for it while hooks kept
firing, and showed "Companion off".

The companion now listens on the classic SessionStart hook and, for a
resume or clear, posts a hello under the new conversation's id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(companion): say hello again after a /branch too

A /branch or --fork-session moves the running process to a new session id
the same way a resume or /clear does, and Claude Code reports it as a
classic SessionStart with source fork. Leaving fork out kept the same
quiet window open for branched conversations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(structured): draw Markdown tables and rules in replies

The Structured view's Markdown reader knew fenced code, headings, lists and
paragraphs only, so a pipe table in Claude's reply showed as raw lines of
pipes and dashes, and a `---` separator as literal text.

The reader now turns a header row followed by a delimiter row into a table
block, with each column's alignment from the delimiter, every row padded or
cut to the header's width, and a pipe inside a code span or escaped as `\|`
kept as text. A line of three or more dashes, stars or underscores is a
rule. Both are drawn as elements with theme tokens; a wide table scrolls
sideways inside the reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(structured): keep table edges from eating cells and nearby blocks

Review found four ways the new table reader went wrong on ordinary replies:

- A lone backtick in a cell opened a code span to the end of the row, so
  later pipes became text and the last cell was dropped. A backtick with
  no partner later in the row is now just a character.
- A prose line with a pipe in code, over a bare ---, became a one-column
  table. A delimiter row now needs a pipe; a bare --- is a rule.
- A list item, heading or fence right after a table, with no blank line,
  was read as one more row. Table rows now end where another block
  starts, and a heading is never a table header.
- The spaced rule (- - -, * * *) and escaped pipes had no tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(structured): show the companion starting instead of missing

While a session's companion was still starting, the Structured view said it
needs Kriyon Companion, as if it were off. It now shows the wait itself:
Claude Code's mark sends light across a link to the mascot, which ripples as
each one lands, under a shimmering "Starting Kriyon Companion…". A line
explains the one thing that can hold it up, a folder Claude Code wants
trusted, with a button to open the terminal to answer it.

The screen fades in after a short delay, so a companion that answers at once
never flashes it. Colours come from the tokens, so it follows the theme and
the mascot hue, and reduced motion stills it through the global rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(structured): share the loading shimmer with the session overlay

The companion's starting title wrote out its own copy of the shimmer the
session loading overlay already had, and the two had drifted apart in colour
and speed, so a new session showed two different shimmers back to back. The
shimmer is now one `shimmerText()` recipe both use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(structured): take the packet glow from the tokens

The light dots on the starting screen set their glow as a literal shadow.
It is now `tokens.shadow.glow`, lit with the accent, so a token change
reaches it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(structured): share the mascot tile and its size

The starting screen copied the banner's mascot tile, and both typed the
mascot's size by hand beside it. The tile and the size it is drawn for now
come from `Mascot.tsx`, so resizing one can't leave a mascot clipped or
spilling over its tile on one screen only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…18)

* feat(structured): set the user's prompts apart from Claude's replies

In the Structured view a prompt was only bold text in the same column as the
reply, so a long turn was hard to scan for who said what. A prompt now sits
on the right as a speech card in a wash of the accent, with any images under
it, and Claude's steps and reply sit under a "✻ Claude" label across the full
column. The sent stand-in, the streaming reply and the /usage card follow
the same split; work reports stay unmarked so they never read as typed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(structured): keep the prompt card's tucked corner

The prompt card set its tucked bottom-right corner and then the
all-corners radius after it, so the shorthand put that corner back to
12px and the bubble's tail never showed. One shorthand with all four
corners leaves nothing to override it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(structured): take the prompt card's spacing from the tokens

The prompt card's padding and the speaker label's gap were pixel
literals off the spacing scale; src/AGENTS.md has spacing come from
theme/tokens.ts so a change to the scale reaches them too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…otes (#20)

* fix(release): list only what is new since the previous beta in beta notes

A beta's notes came from git-cliff's unreleased draft, and git-cliff counts
only from release tags (cliff.toml's tag_pattern). So every beta listed
everything since the last stable release, not what changed since the beta a
tester already runs.

A beta's notes now count from the newest release or beta before it, and a
nightly's from the newest release or nightly, through a git range. git-cliff
drops the range under --unreleased, so the range replaces that flag; no
release tag can fall inside it, since it starts from the newest one. A
stable release's notes are still its CHANGELOG.md entry, which lists
everything since the last stable release. ADR 0046 says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(release): keep a release tagged between two betas in one section

A pre-release's notes come from git-cliff over the range from the last tag
on its channel. A release can still be tagged inside that range, such as a
patch shipped between two betas, and git-cliff then split the range into
two sections. draftNotes strips only the first heading, so the beta's notes
showed a stray "## 0.12.1" section. Ignoring every tag inside the range
keeps it in one section, under the pre-release's own tag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(release): count release tags through cliff.toml's tag_pattern

notesSince decided what a release tag is with its own pattern, while
cliff.toml's tag_pattern is the one definition that latestRelease,
nextRelease and preReleaseBase read. It now takes that pattern too, so a
change to tag_pattern moves git-cliff and the notes range together. A tag
the pattern does not match, such as a bare 0.12.5, no longer counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(adr): say what a tester who skips a beta sees in its notes

ADR 0046 said a beta's notes show what changed since the build a tester
runs. They count from the previous beta, so that holds only for a tester
who takes every beta. The ADR now says what one who skipped a beta sees,
and where the skipped beta's notes are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(structured): show pasted text without Claude Code's paste markers

Claude Code records a paste inside the prompt as
`<pasted_content id="x">…</pasted_content id="x">` so the model knows which
part the user pasted. Kriyon showed those markers as part of the prompt in
the Structured view, and a prompt that was only a paste got the opening
marker as its session title and past-session preview. The markers are now
taken out and the pasted text kept as plain text; an opening marker without
its matching close is left as written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(composer): type long messages in pieces so none of it is lost

Claude Code reads an input chunk over 800 characters as a paste. The
composer wrote a message to the terminal in one write, so a message of
800 to about 1000 characters was recorded as one pasted block, and a longer
one, which the terminal splits into ~1 KB reads, lost everything but its
last chunk. The text is now written in 256-byte pieces, 20 ms apart and
never split inside a character, which Claude Code reads as typing. Checked
against Claude Code 2.1.303: a 3000-character message arrives whole and
untagged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(structured): read pastes the way Claude Code shows them, wherever a prompt is read

Paste markers were taken out by two readers (the turn builder and
prompt_label), while the Prompt event kept them, so "Save last prompt…"
still saved the raw tags, and an all-paste prompt got an empty title.
The markers are now removed where each parser reads a user row: the
Structured view's row builder, the Prompt event, and the prompt text that
session titles and past-session previews use.

The matching now follows Claude Code's own rules: a four-hex-digit id
followed by a newline, with up to two newlines dropped on each side of a
paste. Before, any id matched, so a typed prompt quoting the tags lost
them, and an all-paste prompt showed two blank lines above its text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(composer): send a long message as one paste so it always arrives whole

Typing a long message in 256-byte pieces relied on a 20 ms gap: if Claude
Code was busy for longer than about 60 ms, the pieces arrived as one read
over its 800-character paste limit, and the text could be lost again.

A message of up to 800 bytes is typed in one write, as before this pull
request; whatever way the terminal splits or joins it, Claude Code reads it
as typing. A longer message, with its @ mentions, goes as one bracketed
paste, which Claude Code collects whole however it is read. Text that
contains the paste-end sequence cannot close the paste early. Checked
against Claude Code 2.1.303 with Kriyon's own timing: a 3160-character
message with an @ mention arrived whole and the file was attached.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(composer): remove every escape from a pasted message so it cannot end early

Only the paste-end sequence itself was defused, so text holding two escapes
before it (pasted terminal output, say) became a working end sequence again:
the paste ended inside the message and the rest was read as typed keys,
where a newline could send part of it early. The paste body now carries no
escape character at all, which no text a person means to send needs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ws it (#22)

* fix(activity): show steps and turns in plain words

The Activity tab showed what Claude Code sends the model, not what a person
can read: a subagent's hand-back headed its turn as raw `<agent-message>`
tags, a heredoc split into one "command" per script line, searches said only
"Searched the code", and tools like ScheduleWakeup read as "Used
ScheduleWakeup".

Each step now keeps a one-line summary: the description Claude already writes
for a command or subagent, or the pattern, query or page a search or fetch was
about. It costs no tokens, since Claude sends it anyway. Steps already in the
log get theirs from the input they recorded.

- A command row is titled by its description, with its commands under it.
  Heredoc bodies stay part of their command, each command shows its first
  line, long chains show the first three and how many more, and home folders
  read as `~`.
- Searches, web calls, subagents and skills say what they were about, and
  Claude Code's other tools get plain titles.
- A turn started by a subagent's hand-back or a work report is headed by its
  summary ("Agent "pr-scorer" reported back"). Paste markers are taken out of
  prompts.
- A main-agent step whose end was never recorded no longer reads "running…"
  once its turn has ended.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(activity): show what Claude did to the machine, as the design draws it

The Activity tab exists so a person can see what Claude did: files it made
or changed, what it deleted, where it went online, what it installed. It now
follows design A (side panel timeline):

- What a step did to the machine leads its title: "Installed a package",
  "Deleted files", "Touched a secret file", "Read a file outside the
  project". Otherwise a command reads as Claude's own description in the past
  tense ("Ran the tests").
- A file change shows the lines it added and removed, from Claude Code's own
  patch, and a new file reads as "Added". A command that edited files shows
  its lines too and counts under the Changes filter.
- A test run shows its result at the row's end ("43 passed", "2 failed"),
  read from the whole output by the hook, so a long run can't lose it.
- A step the user approved in a permission dialog says "you approved".
- Web steps say where they went ("Used the internet" over the address,
  "Searched the web" over the query), and a search shows what it looked for
  and how many files it found. Flag chips read "web" and "outside".
- Reads in a row fold into "Read 9 files" and file changes into "Changed 3
  files" with their lines added up. A folded row opens to show each step.
  Flagged or failed steps never fold, so they stay in sight.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(activity): use Claude Code's real paste markers in the store test

dev now takes only Claude Code's real paste format (a four-hex-digit id),
so the test's made-up id was read as prose.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(activity): show subagents' work, command output and files outside

Testing on a real session showed what the timeline still hid:

- A subagent's steps were mixed in with the main agent's. They now sit
  under the step that started the subagent, with their lines added up. A
  subagent whose start isn't in view gets a block of its own.
- Claude often reads and writes through Bash. A command that names a file
  in the home folder or the system's own (`cat ~/.claude/settings.json`,
  `/etc/hosts`) outside the project is now flagged "outside". A command
  that changed files names them ("Changed format.js, format.test.js") and is
  flagged like a file tool would be.
- The step detail showed a command's result as raw JSON. The hook now
  records the output itself, a change's patch or a page's text, keeping the
  start and end of a long one. Older JSON results and command inputs are
  shown as text too.
- Node's built-in test runner results ("ℹ pass 3") now show as "3 passed".
- A shell loop or `if` reads as one command, not one per piece.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(activity): add the changed-files column in its own migration

A store already at v6 (from an earlier build of this branch) skipped the
column, so the timeline failed to load. It now has a v7 step of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(activity): drop install noise and name subagents by their task

From the second smoke run:

- An install's node_modules and git's own files no longer count as files a
  command changed, so `npm install` doesn't list a package's LICENSE or add
  its lines to the project's.
- A subagent's hand-back heads its turn by the task Claude gave it
  ("Agent "Review format.js and add test" reported back"), not its type.
- A subagent handing back reads "Reported back", not "Used
  SubagentHandback".
- Claude loading its own tools does nothing on the machine, so it is left
  out of the timeline and its counts.
- More of Claude's verbs read in the past tense ("Appended", "Reviewed").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(activity): flag package removals and keep descriptions in one tense

From the third smoke run:

- Removing a package (`npm uninstall`, `pnpm remove`, `pip uninstall`,
  `brew uninstall`, `cargo remove`, …) takes something off the machine, so
  it is flagged like a delete and titled "Removed a package".
- Every verb Claude chains in a description now reads in the past tense
  ("Installed dayjs and checked its output"), not just the first. Words
  that read as nouns after "and" ("…and test files") stay as written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(activity): hide a background helper's check-ins

Claude Code forks helpers of its own: they carry an agent id but no
subagent type, since nobody launched them. One stopping a check-in loop
showed up as "Subagent · unknown" with a step that never finished, though it
did nothing on the machine. A helper's check-in is now left out like other
housekeeping, and anything else a helper does shows under "Claude Code in
the background".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(activity): show every flag a step earned

A command that removed one package and installed another showed only its
most serious flag. Each flag now gets its own chip, most serious first, and
"reinstall" and "retry" read in the past tense.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(activity): read test results from runner summaries, and a subagent's launch

The hook took any line with "N passed" as a test result, so a run where
every test failed showed nothing and `grep passed` output got a green
badge. It now reads only the summary lines vitest, jest, pytest, cargo and
Node's runner print, and adds up every runner in one command (`pnpm check`
counts vitest, node and cargo together).

On a subagent's own event, the hook now reads the sidecar Claude Code writes
beside its transcript (`subagents/agent-<id>.meta.json`) and records which
launch started it, so a subagent can be tied to its launch exactly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(activity): link subagents by one rule in core, and flag package removals

Matching a subagent to the call that launched it was written three times
(the store's SQL, the timeline and the live Agents panel), and the copies
already disagreed. kriyon-core now owns it: `exact_launch` (the launch the
sidecar names, or a background launch's agent id) before `guessed_launch`
(the earliest free launch of its type). The Activity log and the live panel
both use it, an exact link never takes a launch from another subagent, and a
launch that names no type runs as `DEFAULT_SUBAGENT`.

Removing a package is now its own step flag in core, so the frontend no
longer keeps a second, already different copy of the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(activity): keep a subagent's launch type and link from the store

The store read a launch's subagent type from the kept input, which the hook
cuts at 2000 characters, so a subagent with a long brief lost its type. It
now keeps the type the hook sends on its own (migration v8, filled in from
the kept input for older launches), with the launch facts core links by.

Each read of a session's turns links its subagents once through core and
sends every subagent step its launch and its name; hand-back headings use the
same name. The step detail no longer sends `agentId` twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(activity): show commands as written, and name agents one way

- A shell loop or `if` stays one command even when nested, keeping its
  real separators, and a here-string or `$(( … ))` is no longer read as a
  heredoc.
- Subagent steps nest under the step the store says launched them, and a
  subagent block, its launch row, its hand-back heading and the step detail
  all name it the same way.
- ", then commit" reads in the past tense; after "then" a word is always a
  verb.
- A background helper's unfinished step keeps "running…" after the turn.
- Only the real home folder is shortened to `~`, through one helper the
  Details rail shares.
- The step detail shows only a command step's command; other tools keep all
  of their input.
- Spacing comes from the theme tokens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* chore(release): 0.13.0

* docs(readme): list Homebrew as a way to install, not coming soon

The 0.13.0 entry lists `brew install leafycode/tap/kriyon`, and publishing
0.13.0 writes the tap's first cask, so the README no longer says the tap is
coming soon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Comment thread scripts/release-lib.mjs Fixed
Comment thread scripts/release-lib.mjs Fixed
Comment thread crates/kriyon-core/src/main_session.rs Dismissed
Comment thread src-tauri/src/commands.rs Dismissed
Comment thread src-tauri/src/daemon_rt.rs Dismissed
Comment thread src-tauri/src/daemon_rt.rs Dismissed
Comment thread src-tauri/src/sleep_rt.rs Dismissed
Comment thread src-tauri/src/status_rt.rs Dismissed
Comment thread src-tauri/src/status_rt.rs Dismissed
Comment thread src-tauri/src/status_rt.rs Dismissed
Comment thread src-tauri/src/watch_route.rs Dismissed
Comment thread src-tauri/src/watch_route.rs Dismissed
cleanEntry and draftNotes removed guidance comments in one pass, so a
comment split around another (`<!<!-- x -->-- y -->`) left a whole
`<!-- y -->` behind in the release notes. They now repeat until no comment
is left. CodeQL flagged both as incomplete multi-character sanitization on
the release pull request.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@THPubs
THPubs merged commit c4de373 into main Oct 20, 2026
26 checks passed
@THPubs THPubs mentioned this pull request Oct 20, 2026
4 tasks done
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