Skip to content

[release] v0.114.8 - #6526

Merged
mmabrouk merged 62 commits into
mainfrom
release/v0.114.8
Sep 5, 2026
Merged

[release] v0.114.8#6526
mmabrouk merged 62 commits into
mainfrom
release/v0.114.8

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

New version v0.114.8 in

  • web
    • web/oss
    • web/ee
    • web/mobile
  • services
  • api
  • sdks
    • sdks/python
  • clients
    • clients/python
    • clients/typescript
  • kubernetes
    • kubernetes/helm

tomatotomata and others added 30 commits August 2, 2026 21:51
…ceptor

Prevents destructive mutation of the caller's kwargs dict. If the intercepted function is retried, the request key remains available.
Adds @lru_cache(maxsize=1) to avoid recreating the Fernet instance on every encrypt/decrypt call. Reduces SHA-256 + base64 overhead for vault operations under load.
'mretrics' → 'metrics'. No behavior change.
…ser_profile

assert statements are stripped when Python runs with -O (optimize).
This caused silent failures where None leaked into downstream code.

- db_manager.py: assert workspace_id → if + raise ValueError
- user_profile.py: assert user_id → if + raise HTTPException

Verified with custom test script under both python and python -O.
No behavior change under normal execution — only makes the code
safe under python -O.
The deprecation warning system was a no-op because it used
getattr(env, old_var, None), which always returns None.
env is a module/object, not os.environ.

Changed to os.getenv(old_var) so deprecation warnings actually
fire when users set old environment variable names.

Verified with local test script showing warnings now emit correctly.
On a keyless account, a message typed in the /m composer was stashed in
pendingTasksAtom and held correctly, but nothing rendered it. The screen
showed only the AgentIntroCard below the connect-model strip, so the person
could not see what they had typed.

This matches the desktop behavior: the desktop renders the held seed as a
visible user turn above the connect-model banner. Mobile now matches that.

Changes:
- Read pendingTasksAtom non-destructively (without consuming the entry) in
  LiveConversation to get the held task text.
- Render it as a right-aligned user bubble in the transcript whenever
  heldTaskText is set, placed above the AgentIntroCard. The bubble
  disappears automatically once the gate drops and the send effect fires
  (takePendingTaskAtom removes the entry from the atom).
- Pass 'Connect a model to start chatting...' as the composer placeholder
  when modelBlocked is true, matching the desktop disabled-state copy.
- Thread a placeholder prop through Composer to ChatComposer so the host
  can override the default idle placeholder.

Fixes #6209

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Christian-Sidak <61099993+Christian-Sidak@users.noreply.github.com>
The rail orders agents by chat-session count and sessions by recency, with
no way to override either. This adds the persistence port and the pure
merge helpers behind a manual order, and folds it into the agent ranks.

Ranks are the one choke point every agent surface reads — the Agents group
and the agent headings under Sessions — so applying the arrangement there
makes it impossible for the two lists to disagree.

No UI yet: nothing writes a zone.
…gement

Applies the manual order at the ref and group atoms, not at render: the
row cap slices before grouping, so a render-layer sort could never promote
an agent past the fifth row.

Each heading arranges its own rows, so a cross-heading drop is impossible
by construction. Buckets with no zone — Pinned, Recent, any date heading —
pass through untouched, which is how pins keep leading.

Rows and headings now carry a dragItem describing their zone; nothing acts
on it yet. The rail also holds its children still while a drag is in
flight, so a poll cannot move a row under the pointer.
Press a row and move, and it drags; a 2px accent line marks where it will
land while the source fades in place. No row reflows under the pointer,
because nothing in the list is animated: the rects are measured once at
dragstart and only a chip and a line move per frame.

The engine runs outside React and loads lazily on the first press that
lands on a draggable row — NavMenu is a static import on every page, and
most sessions never drag.

Rows are anchors, so the native link drag is suppressed or its ghost would
ride along with ours. Alt+Arrow moves a row without a pointer.
Touch cannot both long-press a row's menu and long-press to drag it, so
Move up / Move down join the row menu as reserved keys the shared verbs
never see. They are equally the keyboard path.

The filter menu's Reset now clears the arrangement alongside the filters,
so trying a drag is reversible. It reads 'Reset' rather than 'Reset to
defaults' because it covers two things now.

Its enabled state comes from a new atom, not the existing filters-dirty
one: that also picks the empty-state copy, and widening it would make a
hand-arranged rail claim no sessions matched its filters.
Moves the drag geometry into @agenta/navigation beside the other order
helpers: it is pure math over cached numbers, and that package has the
vitest harness while navigation-ui has none.

Covers the case the merge exists for — a drop in the 5-row Agents group
must leave a twelve-agent arrangement intact — and composes the rank
injection through the real sorter, where a sign error would otherwise
only show up on screen.
…wser

An agent heading saved itself under its group key (agent:<id>) while the
Agents group saved bare workflow ids. Both write the same zone, so the two
surfaces disagreed and a heading drag reordered nothing. Headings now map
their key to the bare id through the reorder descriptor.

The drop also read the index only the animation frame sets, so a drag
released before any frame ran — a fast flick, a backgrounded tab —
committed the index it started with and dropped nothing. The index is now
resolved at drop time.
Date and flat grouping must offer none, agent headings must save under the
bare agent id so they share the Agents group's zone, and pins must never
be arrangeable. All of it is a table the browser cannot easily be driven
through, and getting it wrong is silent.
Rows no longer swap to a grab cursor on hover, and the drag no longer
forces a grabbing cursor on the body. The reference interaction keeps a
plain arrow throughout; the swap was an affordance it does not have.

The chip and the insertion line remain the feedback that a drag is on.
A group's drop slot was measured from its heading element alone, so the
line snapped just under the heading text and a group could never be
dropped past another group's sessions. Each group now measures its whole
block — heading plus the rows under it — which also puts the midpoint that
picks the gap where the eye expects it.

The drag chip was rendering in the browser's default font: preflight is
off and the chip is a sibling of #__next, so it inherits none of the app's
typography. It also shrank to its own text; it is the row lifted out, so
it now keeps the row's width and height and rides centred on the pointer.
font-sans did nothing: Next declares its font variable on the app's root
element, and the chip is a sibling of #__next, so the class resolved to
no family and the chip fell back to the browser's default serif.

The chip now copies the dragged row's computed type instead of declaring
its own — which also makes a heading chip read like a heading and a
session chip like a session, rather than both taking one hardcoded size.
…e the rail

Pinned is a group heading like any other, so it was stamped draggable in
the agents zone — and slicing "agent:" off "pinned" left an EMPTY id, so
dragging it would have written "" into the agent order. "No agent yet" hit
the same path and resolved to "none". A heading now opts out by resolving
to no id, and Pinned opts out of the status heading order too.

The chip also tracked the pointer horizontally. Now that it carries the
row's full width, that hung it out of the rail and over the page whenever
a drag did not start at the row's far left. It is pinned to the row's own
left edge and moves vertically only, as the reference does.
The chip copied the row's line-height, which on a leaf row is 28px, and
was then forced to the row's full height on top of that — a tall box with
the label riding high in it, since a heading's box is padded pt-2/pb-0.5
rather than symmetrically.

It is now a compact pill: the row's width, its own symmetric padding,
leading-none, and the label centred. Its half-height is measured once
after append rather than read per frame, so the drag loop stays free of
layout.
The reset clears the hand-arranged order alongside the filters, and both
are what defaults means here. Every other filter menu in the app uses the
component's own label, so the override went away rather than this one menu
reading differently.
Dragging and Alt+Arrow already cover reordering, and the verbs pushed a
row's position and neighbours through the wrapper contract into both hosts
just to render two menu items.

The row wrapper goes back to two arguments, so nothing about a row's place
in its zone has to travel with it.
The kebab is now the only way into a row's verbs, and a rail row keeps the
browser's own context menu.

Scoped to SessionRowActions, which is the sidebar's wrapper — the sessions
list, the tab rail and the chat tag bar keep theirs. It also removes a
touch collision: that menu opened on a 700ms hold and the drag starts at
350ms, so the two could not both exist on a finger.
…arately

Both agent lists wrote one zone and read one rank atom, so dragging a
heading in Sessions silently reordered the Agents nav group too. They
answer different questions, and arranging one is not a statement about
the other.

The headings now carry their own zone and reorder like the status headings
do, while the nav group keeps the rank injection. Their DEFAULT order
still agrees — both fall back to the same chat-session counts — so the two
only diverge where the user has actually placed something.

Splitting the zones also retires the reason mergeManualOrder was argued
for; it stays for the real one, which is filters and the fetch window
hiding part of a zone at drop time.
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Railway Preview Environment

Status Destroyed (PR closed)

Updated at 2026-09-05T09:44:47.153Z

…-5659

doc(agent-workflows): remove stale built-in tool guidance
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

📘 Docs preview

Status ✅ Ready
Preview https://pr-6526-agenta-docs-preview.mahmoud-637.workers.dev/docs
Inspect Actions run
Commit 76139d2142e85a06d9ad05c80761b7dd30229ea3

This comment updates in place on every push.

…s-db-manager

fix(api): replace assert with explicit exceptions in db_manager and u…
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
@CLAassistant

CLAassistant commented Sep 4, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
7 out of 8 committers have signed the CLA.

✅ Aman-goel-04
✅ tomatotomata
✅ shafeeq27edu-ai
✅ Christian-Sidak
✅ ashrafchowdury
✅ oforiwaasam
✅ mmabrouk
❌ allcontributors[bot]
You have signed the CLA already but the status is still pending? Let us recheck it.

* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
…6466)

* feat(frontend): add agent version history drawer with config diffs and revert

Replaces the agent header's version dropdown with a drawer that lists every
version and its commit message, shows what a selected version would change
against the configuration on screen, and restores one behind a confirmation.

Revert is a normal commit whose content happens to be old, so history is never
rewritten: the selected version's parameters and schemas are staged onto the
current revision and committed as a new version. It goes through the auto-commit
flush rather than committing directly — staging a draft arms that debounce, and
a second independent commit would race it and could mint two versions from one
click. If the commit does not land, the staged draft is rolled back, unless the
revision was already dirty, so a failed revert never discards the user's edits.

Extracts the commit modal's section list into @agenta/entity-ui/changes as
ChangeSections. AgentChangesSummary statically imports DiffView and the Lexical
editor behind it; the drawer also runs on mobile, which bans Lexical outright.
AgentChangesSummary now wraps the extracted list and keeps the JSON view and
drill-ins, so the commit modal is unchanged.

Both surfaces get this from the shared AgentRevisionStatus, so the desktop
playground has version history for the first time. pickerWorkflowId becomes
historyWorkflowId — it no longer names a picker.

* fix(frontend): pick the latest revision by version, not timestamp

The playground could open on a stale revision and stay there. "Latest" was
scored by created_at with version reachable only when every timestamp was
missing, and the picker compared with a strict >, so revisions an agent commits
itself — which land in bursts that tie to the second — let array order decide
which one won. The wrong pick then stuck: it is written to the latestRevision
key, mirrored to IndexedDB, and that query is disabled whenever the key already
holds a value, so nothing revalidated it.

Version is server-assigned and monotonic, so it decides; timestamps only break
ties within a version. One shared isLaterWorkflowRevision replaces the three
sites that each had the bug: the recency picker, the latestRevision prime guard,
and the by-workflow refs sort. Opening the drawer re-primes latestRevision
through the same picker, so a poisoned cache repairs itself.

The drawer keyed its disabled row, hover, default selection and the footer's
version arithmetic off the revision on screen. With the surface parked on an
older one that auto-selected the latest and offered to revert to it. All four
now key off the latest revision instead.

Also drops the last path from a version to switching the playground: selecting a
version only ever previews it now. That removes the per-row "Open" action, and
with it the mobile top bar's revision pin — SessionWorkspace still pins on
auto-commit, which is what that atom is for.

The desktop agent header loses its variant picker: an agent is edited as one
thing, and the vN chip is now the single control, opening the drawer exactly as
it does on mobile. Other workflow kinds keep SelectVariant.

The save-status tooltip is gone from Saved/Draft/Saving, where it only repeated
the word beside it, and kept on failure, where it carries the error and the
retry hint.

* fix(frontend): name gateway tools in the diff by what they do

The tools section of a config diff read "Gateway", "Gateway", "Gateway
connection" — the type discriminator, not the tool. A reader could see that
something changed but not which tool was added or removed.

Canonical gateway tools carry no function.name, so normalizeTool fell past its
function branch into the bare-type fallback and title-cased raw.type. The
identifying fields were in the object all along; only the label ignored them.
The config panel's describeTool already reads them, so the same tool had two
names depending on where you looked. Both now go through the shared
humanizeActionKey / parseGatewayToolSlug helpers, and the legacy slug encoding
resolves to the same name as the canonical one. The technical action key stays
on rawKey for the detail view.

Also gives canonical gateway tools a stable identity. They had none, so
agentItemIdentity fell to a positional key: replacing one action with another at
the same index read as a single edited slot instead of an add and a remove, and
reordering tools reported false adds and removes. Keyed on
provider:integration:action:connection now — the config panel's per-row draft
indicators share that function and get the fix too.

Advanced parameters still render raw dotted paths; that needs wording decisions
and is left alone here.

* fix(frontend): name advanced settings in the diff, not their storage paths

An advanced-settings row read `runner.permissions.default  allow → allow_reads`
— the JSON path and the stored enum. That describes where the value lives, not
what the setting is, so a reader who has not seen the schema learns nothing.

Adds a label map keyed by exact path: the same leaf means different things under
different parents, so `harness.permissions.default_mode` is not
`runner.permissions.default`. Wording follows the config panel's own controls
so one setting is not called two things in two places. Unmapped keys humanize
(`sandbox.network_access` → "Sandbox › network access") rather than being
hidden — a new template key should read imperfectly, never disappear.

Model & harness reaches the same renderer and had the same problem, so its keys
are mapped too.

The four permission-policy names lived in @agenta/entity-ui, which @agenta/
entities cannot import. Moves that vocabulary to @agenta/shared and leaves the
entity-ui module re-exporting it, so no call site moves and the config drawer,
the /permissions palette and the diff cannot drift.

Labels are ADDITIVE. The first cut overwrote before/after with display strings
and broke sectionChanges.changeFor(), which reads those back to tell the config
panel what a property was committed as — it started receiving "Ask" where it
expected "ask". Stored values are untouched; beforeLabel/afterLabel carry the
display form, and the renderer falls back to the stored value when there is
none.

* fix(frontend): stop the diff depending on an unlanded shared helper

CI failed on four jobs: mobile types, the web unit tests, and both agenta-web
image builds. All of them on one error —

  accessors.ts: Module '@agenta/shared/utils' has no exported member
  'humanizeActionKey'

The gateway-naming fix was written against a two-argument humanizeActionKey in
@agenta/shared. That function is not on this branch or on the release base: it
exists as a one-argument version inside @agenta/entity-ui, and a branch in
flight is moving a two-argument one into shared. Locally it resolved because
that unlanded work is present in the shared working tree, so every local run was
a false green.

Adds humanizeGatewayAction to the diff's own gatewayName module instead, with
the same behaviour, so this branch depends only on itself. @agenta/entities
cannot import @agenta/entity-ui, so the base's copy was never reachable either.
When the shared helper lands, converge on it rather than keeping a third copy —
the comment on the function says so.

* fix(frontend): land the other half of the subagent description diff

Commit otq was meant to carry the whole subagent fix but only its accessors half
reached the branch: subagents were named correctly while classify still skipped
field diffs for non-function tools, so an edited description registered as
"edited" with nothing to show — the exact defect that commit claims to fix. The
three regression tests went missing with it.

Every tool kind now goes through diffToolFields. A subagent has no function
parameters, so the params half finds nothing, but the description half works.

* chore(frontend): cut the new comments to the repo's one-line rule

web/CLAUDE.md: at most one short line per comment, no multi-line blocks
narrating why. The new code broke that in ten places. CodeRabbit flagged nine;
the tenth was the storybook header.

Trimmed every block this branch introduced. Pre-existing blocks elsewhere in
these files are left alone, and function/file JSDoc stays as the repo writes it.

* fix(frontend): resolve the review findings on the version history drawer

Accessibility: the section header, the detail-card header and the edited-tool
row were clickable divs with no tab stop or key handler, so a keyboard user
could not expand a section or open a tool detail at all. They are buttons now,
with a focus ring. The commit modal renders the same component and gets the fix.

Over-engineering: the revert phase and the reverted-from version were global
atom families, exported as package API, read by nobody but the drawer, while
mobileView three lines away was useState. Phase is local state now, and
revertedFrom is gone entirely: it always equalled the selected version, because
a revert never clears the selection.

Correctness:
- diffLoading ignored currentParams, so an unresolved current configuration
  rendered as "nothing to compare" and disabled Revert.
- mobileView never reset on close, so a phone reopened on the diff pane.
- Rows stayed clickable during a revert, and selecting one reset the phase that
  the in-flight revert then overwrote with a result for the abandoned version.
- A failed revert left OUR staged parameters in the draft. It snapshots a
  pre-existing draft and restores it now, so "your agent is unchanged" is true.
  The test mock never modelled the draft atom, which is why this passed before.
- scalarValueLabel saw fmtScalar's output, so the string "false" rendered as
  "Off". It takes the raw value and maps only real booleans.

Also drops `any` from the new test mocks and trims the last long comments, both
per the repo's guidelines.

* fix(frontend): ignore a revert that resolves after the drawer is abandoned

EnhancedDrawer dismisses on Escape and on an outside click, so a commit can
outlive the drawer that started it. handleClose reset the phase to idle, then
the in-flight revert overwrote that with done or failed, and reopening showed a
terminal footer for a flow the user had walked away from.

A run counter, bumped on close and on selecting another version, makes a stale
result recognisable so it is dropped. Same race the row-selection guard closed,
reached the other way.

* fix(frontend): render MCP and Skills rows in the change diff

The section renderer gated its item list on id === "tools", so an expanded
MCPs or Skills section painted a header over an empty body — in the version
history drawer and in the commit modal alike. Rows now render for any section
that has them; the drill-in stays tools-only, since that is the only kind with
a detail view.

* feat(frontend): give subagents their own section in the change diff

Subagents are stored as {type:"reference"} entries inside the agent's tools
array, so the diff listed them among the real tools — "hourly-news-3vhu ·
Subagent" under a Tools header, and "Added 1 tool." in the commit message. The
config panel has kept them apart since #4860; the diff now matches it.

The classifier splits that array by kind and emits Tools and Subagents
separately, so the panel's Subagents accordion also stops borrowing the tools
change key — adding a subagent no longer lights the Integrations header.

* feat(frontend): match the change diff's sections to the config panel

Same icons for the same sections (Integrations, Subagents, Instructions, MCPs,
Skills, Model, Advanced), so the diff reads as the panel it describes, and the
instructions detail card takes its icon from that one map rather than its own.

Tools becomes Integrations only for an agent template. The prompt playground
shares this classifier and has no integrations, so its section keeps the old
name — the commit message follows the same split ("Added 1 integration").

* chore(frontend): reach the version-history panes through the package boundary

The stories imported ChangesPane, RevertFooter and VersionList by relative path
into playground-ui's src/, past an export map that already had a subpath for
them, and Storybook declared neither playground package as a dependency.

The subpath now exports the three panes, and @agenta/storybook depends on the
packages it renders.

* fix(frontend): put the diff, its section headers and the footer on one edge

The ghost accordion bled its header band 8px left and right but hung its body
off a one-sided -ml-3.5, and the text-diff branch added another 14px indent on
top, so the header band, the diff rows and the drawer's footer buttons each
ended on a different line.

The bleed is now one constant applied to both boxes, rows inset by the same
amount instead of a hardcoded 3.5, and the drawer's footer takes the matching
padding.

* fix(frontend): drop the Reverted tag from the version list

A row already says it was a revert — its commit message reads "Revert to v1".
The tag repeated that next to Latest, and it was the only reader of isReverted,
which sniffed the message prefix to derive display state. Both are gone.

* fix(frontend): align the diff bands by filling the pane, not by bleeding out

The previous attempt hung the section bands on negative margins, which cannot
work here: the body sits inside HeightCollapse, whose overflow-hidden clips a
band bled outward, so the header band and the diff rows still landed on
different edges.

The pane now carries the smaller inset and the bands fill it edge to edge,
padding their content inward — no negative margins anywhere, so header band,
diff rows and the footer buttons share one left and one right edge.

* fix(frontend): list a lone first version as a normal row

A single-version agent replaced the whole rail with a notice saying there was
nothing to compare, hiding the one version it does have. v1 now lists like any
other row — disabled, as the latest always is — and the pane beside it still
says there is nothing to compare.

* fix(frontend): title the diff's model section Model, as the config panel does

The section header read "Model & harness" while the panel it mirrors calls the
same grouping "Model". Label only — the harness engine still belongs to this
bucket, exactly as it does in the panel.

* fix(frontend): say the version pane previews the revert, not the version

The rows are the diff between the selected version and what is on screen, so a
tool added in v5 shows as a REMOVAL on v1-v4 — restoring any of them would take
it away. Read as the version's contents, that looks like the tool is in every
version. The pane now names what it is showing.

* fix(frontend): stop an empty diff claiming a version matches the current one

No sections only means nothing the classifier RECOGNISES changed, but the pane
said "This is the current configuration" and the footer disabled Revert — so a
version differing in an unclassified field looked identical and could not be
restored at all.

The stored configurations are now compared directly. Identical and "you are on
this version" each get their own line and keep Revert disabled; a real but
undescribable difference says so and stays restorable.

* fix(frontend): give the version drawer one baseline, the latest version

The pane diffed against the revision the surface happened to sit on while the
footer counted from the latest, so on an older revision the drawer quoted two
different versions without naming either. Everything now measures from the
latest: the diff, the identical check, and the footer's counts.

isCurrent goes with it — nothing rendered it once the list keyed disabling off
isLatest. The done notice stops naming a version too; it fired before the list
refetched, so the number it printed was the version being replaced.

* feat(frontend): show what each version changed, not what restoring it would do

The pane diffed the selected version against the head, so a tool added in v9
appeared as a REMOVAL on every older version, the newest version could never be
inspected at all, and the first version showed a change despite changing
nothing. Four separate confusions, one cause: the rail's messages are history
while the pane answered a question about a button.

Each version is now diffed against its predecessor, so the pane and the message
beside it say the same thing. The latest becomes selectable and is where the
drawer opens; the oldest says it has nothing before it. Revert is unchanged and
still measures against the head, since restoring a version that already matches
it does nothing.

* fix(frontend): make the history trigger and the permission rows say what they are

* fix(frontend): put the per-section change counts back in the drawer

* fix(frontend): let the pane title carry the tense

"Version 9" plus a separate "What v9 changed" line said the same thing twice and
buried the tense in the smallest text on screen. One title now: "What version 9
changed", with the commit message under it.

* feat(frontend): diff an edited skill's body, like the instructions

A skill is prose — its SKILL.md body — but an edit showed only the skill's name
under a pencil, so what actually changed was unreadable. Edited entries now
carry the same folded hunk diff Instructions uses, and the row names which half
moved: description, instructions, or both.

* fix(frontend): read a renamed skill as an edit, not a swap

A list entry is keyed by its name, so renaming one in place read as "1 added,
1 removed" — two rows, and the body diff behind them never computed, which is
the change you most want to see when a skill is rewritten.

Leftover additions and removals are now paired by the slot they occupy. The row
shows both names ("stop-slop-1 → stop-slop-2") and names what moved.

* feat(frontend): describe an integration's permission change

An integration's policy lives on its tools entry as policy.permissions, which
normalizeTool dropped — so the whole-entry fingerprint caught the edit and the
row said only "changed". Every permission edit looked the same and carried no
information.

The policy is now read and diffed: the default reads "permission Ask → Allow",
per-tool overrides become their own field changes, and neither is counted as a
parameter any more.

* fix(frontend): separate the diff gutter from the line it marks

A removed line whose content is a Markdown bullet rendered as "− - Greet the
user warmly", two markers deep. The sign column now sits a clear step away.

* fix(frontend): make the diff describe every change kind it reports

Swept each kind of config change through the classifier and read what the row
actually said. Five carried little or nothing:

- A key no section claims was dropped SILENTLY — the diff reported no change at
  all. The unclaimed rest of an agent template now lands in Advanced.
- Object settings printed their JSON: retry_policy read
  {"max_attempts":3,...}, now "max attempts: 3, backoff: exponential".
- Sandbox rows printed storage paths; they take the control's labels and its
  mode vocabulary ("Filesystem: Read-only", "Network egress: Block all egress").
- harness.permissions.default_mode was labelled "Harness permissions" with a raw
  enum; it is "Permission mode: Accept edits".
- An MCP server has no prose, so an edit showed only its name. The row names the
  field that moved.

* fix(frontend): drop the redundant count and name what every tool edit moved

The "N changed" tag on a scalar section counted rows already listed beneath it.
Lists keep their added/removed counts, which say something the rows do not.

The two findings from the sweep, both of which ended at a bare "changed":
- A single changed parameter is now named rather than counted ("to changed"),
  with the count kept once there are several.
- Builtin, platform and reference entries carry no inspected fields, so only the
  whole-entry fingerprint fired. Their authored fields are now diffed too, so a
  builtin's option or a subagent's version is named.

* style(frontend): square off the change tags

rounded-full read as a pill next to the squared section band it sits on.

* feat(frontend): show what an added or removed skill contains

An added skill listed its name and nothing else, which says nothing about what
the agent gained. The body IS the skill, so it now renders as an all-added diff
(all-removed when the skill goes), with the description on the row.

Whole-body diffs have no unchanged region to fold, so every item diff is capped
at 12 lines with a "… N more lines" marker; the reported counts stay true. Built
without the line differ, which reports an empty side as its own removed line.

* feat(frontend): merge the version chip and save status into one control

The version and its save state describe the same thing, and as two chips beside
each other they competed — and the version chip still had to prove it was not a
dropdown. They are now one button: a history icon, the version, and the save
dot, opening the drawer.

A failed save keeps its own chip: there the status IS the retry control, so it
cannot share a button that opens history.

* style(frontend): shrink the save dot

7px carried more weight than the status needs beside the version it now shares
a control with.

* style(frontend): fill only the version, not the status beside it

The chip now wraps the icon and the version alone. The save state is a fact
about that version, not a second control, so it sits outside the fill as a plain
label — while the whole row stays one button, hovering as a unit.
[feat] Drag-and-drop reorder of agents and sessions in the sidebar
…afety

[fix] Preserve agent drafts during version restores
@mmabrouk
mmabrouk merged commit 508b551 into main Sep 5, 2026
75 of 76 checks passed
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.

8 participants