Skip to content

[release] v0.118.4 - #6923

Merged
ashrafchowdury merged 158 commits into
mainfrom
release/v0.118.4
Sep 17, 2026
Merged

ashrafchowdury merged 158 commits into
mainfrom
release/v0.118.4

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

New version v0.118.4 in

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

Onboarding created the agent first and left connections to the builder, which
asked for them mid-run via request_connection — one per turn, several turns in.

Add a setup step between describing an agent (or picking a template) and
creating it: the accounts it will need, connected while it is still a draft.

Template-declared integrations gate Create; accounts detected from free text
never do. The frontend can only string-match a description, so a guess is
allowed to offer and never to obstruct — requiredAccounts() returns only
template-declared entries, so the rule holds in the types, not just the UI.

Detection is pure and network-free (detectAccounts), so a backend that plans
the account list pre-commit can replace the text half behind the same return
type. Word boundaries alone were not enough: "linear algebra", "the notion
that" and "cut some slack" are all whole-word hits, so a small per-slug
negative-context table rejects the idiomatic readings.

The step's answers reach the agent through the seed message, written in the
user's voice — it renders in the transcript as their own first turn. The
config-write alternative does not exist for this step: a gateway tool is
per-action, and choosing actions is the builder's job via discover_tools.

AccountRow is extracted from TemplateSetupDrawer's IntegrationRow rather than
duplicated, so the drawer and the new card render one row.

Behind NEXT_PUBLIC_AGENT_CONNECT_STEP (default on); off restores instant
create on every path. Mid-run request_connection is unchanged and remains the
safety net for anything skipped.

Design workspace: docs/design/onboarding-connect-step/

Closes #6043
…its first run

Wires the pre-create connect step into /m behind the same
NEXT_PUBLIC_AGENT_CONNECT_STEP flag the desktop reads, via lib/connectStep.ts
(the browseLayout.ts precedent — mobile may not import @/oss/*).

/m's first run offered three plain text chips and a "Browse templates" button
where the desktop offers the real template strip. It now renders the shared
TemplateCard with the category filter and the desktop's composer placeholder:
a snap scroller on a phone, a three-across grid from lg up, so /m at desktop
width is a desktop layout rather than a phone scroller with cards running off
the right edge.

Four defects live QA caught that the unit tests did not:

- The footnote read "Nothing to do here." directly under two Connect buttons;
  setupStatus returns "ready" there and the copy fell through to the all-set
  line.
- Row subtitles truncated mid-word and every detected row repeated the same
  sentence. A detected row now carries no subtitle at all; the card's lead says
  where the rows came from once. Template rows keep their real scope line.
- Edit lost the description. The step REPLACES the composer, so setMarkdown ran
  against an unmounted editor; the text is parked and refilled on the render
  that remounts it.
- The lead claimed "From your description." for a template's declared accounts,
  which is false — that "description" is the builder message the user never
  wrote. setupLead now takes fromTemplate.
The desktop's first run redirects into a project-scoped playground on an
ephemeral agent (OnboardingEntry -> useAgentOnboarding) so the config pane is
live before anything exists: you see and change the model, instructions and
tools of the agent you are about to build. /m had none of that machinery — its
first run was a plain document that minted and committed on submit, so there
was nothing to inspect and nothing to change.

/m now mints the same local-only agent up front (useEphemeralAgent) and mounts
the real SessionWorkspace around it, with the create surface in the
conversation slot until the agent exists. The ephemeral is a normal entry in
the workflow molecule, so every entity-id-driven pane renders against it with
no special casing; only useAgentEntity's two queries cannot resolve a `local-*`
id, which is why the id is passed in rather than resolved from the session.

Submitting commits THAT ephemeral (createFromPrompt now takes entityId) instead
of minting a second one, so the agent the user configured is the agent they
get. The session id is minted once by the screen and shared with the composer,
which stages attachments against it, and with the workspace, which keys its
panes off it.

A failed mint reports itself with a retry that releases the once-guard, rather
than leaving the surface spinning — the desktop's equivalent path currently
hard-fails on a user-preference lookup and shows only "Couldn't set up your
agent".
Four defects in the playground-native port, all found in the browser.

The config pane showed Advanced alone — no Model, Instructions, Tools or
Skills. The cause is not the mint: the ephemeral is fully seeded and
`/services/agent/v0/inspect` returns the right schema, whose `properties.agent`
carries `x-ag-type-ref: "agent-template"`. `parametersSchema` enriches such a
node by merging in that type's schema, but its query atom sits `pending`
forever on this surface — mounted, no request, no error — so the node stays a
bare stub with no properties and there are no fields to render. Seeding the
type schema under the same cache key gives the enrichment its data; the request
now fires (200, 5.9KB) and the pane renders Model and Instructions.

`deferInspect` is also dropped: the config pane is the point of this surface and
its sections are schema-driven, so the mint waits for the schema it displays.

Two nav rails: `SessionWorkspace` renders its own `AppShell` and Home already
wraps first run in one. It takes a `bare` prop now for hosts that provide the
shell themselves.

Editor placement: the create surface stacked from the top. Hero, then a flex
spacer, then templates and the composer docked at the bottom, as the desktop
does — with the named "Create agent" action in the composer's trailing slot
instead of a bare send arrow, and the desktop's category counts.
Two layout defects in the templates row, both measured in the browser.

The card's monogram overhangs its top edge by 20px and carries a 2px ring, so
`pt-5` left it flush: the ring sat 1px from the row's top and clipped. `pt-7`
gives it 9px of clearance.

The grid overflowed its container by 25px, cutting the cards off on the right.
A grid child defaults to `min-width: auto`, so the card's footer string
("2 GitHub tools · On release") set a min-content floor the track could not
shrink below. `lg:min-w-0` lets the columns size themselves; they now resolve to
three even 285px tracks inside the 880px column.
…track

The cards did not line up: in a three-column grid whose tracks measured an even
285.3px each, the cards themselves came out 307 / 295 / 311px, and each one's
right edge crossed its neighbour's left. Narrow categories were worse, because
the overflow scales with the footer string.

`TemplateCard` is a flex container, so it defaults to `min-width: auto` and its
own min-content width wins over the track it is placed in. `w-full min-w-0`
lets the track govern; the three cards now measure exactly 285px with even 12px
gaps, no overlap, and the `truncate`/`line-clamp` inside finally take effect
instead of pushing the box wider.

Verified in the browser across every category filter (3 cards, identical
widths, overlap=false) and in `TemplateGallery`, the other consumer, where all
28 cards render a uniform 435px with no overlap.
There is exactly one draft session before the agent is created and no history to
switch to, so the strip was a rail with nothing in it.
The desktop strip can be dismissed ("Don't show again") and restored from a
"Templates hidden · show again" line; /m had neither. Same localStorage key, so
the preference is shared across both surfaces rather than one per app.
…next

At lg the cards now page 3-at-a-time behind a "1-3 of 28" counter and chevrons,
so the full set is reachable instead of three cards and a Browse link. Every card
stays mounted; only the window changes, so the narrow snap scroller is untouched
and the pager hides below lg where there are no discrete pages.
…olumn

The gallery is already shared, but /m put the page cap and gutters on its header
only, so the card grid below ran past the viewport and clipped the right column.
Both boxes now carry them, as pageWidth documents and as the desktop's PageLayout
already did. /m also never rendered the page's subtitle; the toolbar frame leaves
that to the host, so the copy moves into @agenta/home-ui and both hosts read it
from there rather than keeping a second literal.

Also gives the gallery's toolbar column min-w-0: a flex child defaults to
min-width:auto, so its grid could push the row wider than the page regardless of
who hosts it.
… row

At lg the label, category chips and controls now share one row like the desktop
strip, instead of chips wrapping to a second line under them. Below lg there is
no width for that, so it stays two rows and the chips keep bleeding to the screen
edge; lg:contents is what lets the chips sit between label and controls at lg
while those two still share a row of their own on a phone.
…first run

First run leads with the question and the composer; a form for an agent that does
not exist yet does not need half the screen, and the pane is one " " away.

Expressed as a third default in resolveConfigPanelCollapsed rather than a flag in
the component, because that function already owns "the default when nothing is
stored" — so a stored preference still beats it exactly as it beats the phone
default, and opening the pane here never writes over what was chosen elsewhere.
…the tab strip

The reveal only ever existed as the tab rail's leadingExtra, so hiding the strip
on first run and collapsing the pane by default combined into a one-way door.

Extracted as ConfigRevealButton with two homes: the rail, where the pane
disappeared from, and the workspace column itself when there is no rail.
…way back

A lone chevron on empty canvas reads as decoration, not as a control. Where there
is no tab rail to give it context, the reveal now sits in the top bar's actions
slot as a labelled "Configuration" button; the rail keeps the compact icon, which
its row of neighbours already explains.
…e left behind

Reverts the top-bar detour. The empty strip beside the chat IS where the pane was,
so it becomes the pane's collapsed edge: a rail that says "Configuration" and
takes the whole strip as its target, rather than a chevron floating in dead canvas.

md and up only — a phone has no column to spare, and its reveal is unchanged.
…nnect-step

# Conflicts:
#	web/mobile/src/features/chat/SessionWorkspace.tsx
#	web/mobile/src/features/onboarding/FirstRunComposer.tsx
#	web/packages/agenta-entity-ui/package.json
…ort and by intent

Two reports, one cause: whether the config pane shows was a single sticky global
that no context and no action could speak for.

#6378 — one localStorage key served every breakpoint. Opening the pane on a
desktop stores false, and a stored value beats the per-device default in both
directions, so the same browser at phone width opened the pane over the whole
screen and hid the playground. The preference is now per breakpoint; the wide
one keeps the original key, since a value stored before the split was almost
certainly set on a desktop and still means what it meant there.

#6381 — the agent overview's Edit navigated to the playground and nothing else,
so anyone who had ever collapsed the pane (or left Build/Chat maximized) landed
there with no configuration in sight and nothing saying why. Edit asks for the
configuration, so it now clears both things that hide it.

resolveConfigPanelCollapsed keeps its signature — only the choice of WHICH
stored answer to hand it moved — so the host default the onboarding surface
layers in is untouched.
The mobile overview passed no onEditConfig, commented 'configuration is edited in
the desktop playground'. That stopped being true: a session IS the playground on
/m, with the same config pane. The comment outlived the constraint, so the Edit
action was missing for no reason.

Edit opens a BLANK session for the agent — the id is client-side and nothing
reaches the backend until a message lands, so reading the configuration costs no
session (verified: the landing session is absent from /sessions/query).

The reveal itself moves into revealConfigPaneAtom, shared by both hosts' Edit.
Two persisted flags hide the pane and an action promising configuration has to
clear both; having written that logic twice was how #6381 could be fixed on one
surface and not the other.
…the step opened

The connect step docks above the composer and leaves it editable, but Create
committed the seed captured when the step opened — an edit made while choosing
accounts was silently dropped, template provenance included. Re-read the live
composer at Create and resolve the template name against that text.
…isibility

# Conflicts:
#	web/mobile/src/features/agents/AgentOverviewScreen.tsx
… tests

CodeQL js/insecure-randomness: generateLocalId's Math.random reached the first-run
composer's entity id. Same shape, same `local-` prefix, crypto-backed source.
The three setup test files also failed the repo-wide prettier check.
…o ask

Ashraf's report: writing a description and pressing Create produced a card
saying "We didn't spot a specific service in your description" and "Nothing
required." — a blocking step announcing it was not needed — with a second
"Create agent" button below it.

Two changes:

- open() now reports whether detection found anything, and every host commits
  straight through when it did not. The step interrupts only when there is a
  concrete account to connect, which is when the interruption pays for itself.
- the composer is hidden while the card is up, as it already is on /m. Leaving
  both up put two identically-labelled Create buttons on screen, and the
  composer's one merely re-opened the step it was already showing, so the
  button the user reaches for first appeared to do nothing.
A template's providers were two hand-kept lists: requiredIntegrations (what
gates) and logoSlugs (what the card draws). Neither could say that GitLab
STANDS IN FOR GitHub, so the PR reviewer drew both marks, listed only GitHub,
and its setup step demanded GitHub of a GitLab user.

TemplateConnection names the slot by what the playbook needs it for and holds
the interchangeable options, which is what the playbooks have always said in
prose: "GitHub (or GitLab) to read the diff", "Slack or Discord are optional".

Card marks are now DERIVED from the slots, so a logo cannot advertise a
provider no slot accepts. Additive for now: templateConnections() reads an
unmigrated template as one required single-option slot per integration, so the
other 27 are untouched until they move over and the legacy fields go.
pr-reviewer now states its slot and nothing else — the legacy requiredIntegrations
twin is gone, because keeping both let a template disagree with itself: a test
fixture overrode the legacy field on a migrated template and the override was
silently ignored. Every consumer reads templateConnections() instead, so a
migrated and an unmigrated template are the same thing to a caller.

- the detail page labels a slot with every provider that satisfies it — GitHub
  or GitLab — and lists optional slots separately instead of dropping them
- detectAccountGroupsFromTemplate exposes the options so the setup card can
  offer the choice; the flat view keeps returning the primary, since an
  alternative rendered as a second row reads as more work, not a substitute
- tools front the primary option only, for the same reason
ashrafchowdury and others added 13 commits September 16, 2026 18:17
A new session from `+` or the shortcut, and a switch to an existing
one, put the caret in the composer once it can take input. Not while
the caret is already in another field: a self-commit remounts the
screen mid-edit in the config pane.
The composer already knew it had no hardware keyboard — it hid the
shortcut hints on that — but `submitOnEnter` stayed at its default, so
a phone's Enter sent the message with no way to make a newline. It now
follows the same test: Enter sends only where Shift/⌘ can be held; on
touch the send button sends and Enter inserts the paragraph.
The composer bound the head of a newest-first roster, so a workspace with two agents opened every visit on the one created last, whichever was in use (#6741). The start is what is remembered, per project, on this device; HomeFocus takes it as preferredAgentId and falls back past an agent no longer listed.
…select text

A session opened before its row landed rendered a chip with no context menu, so a right-click on it did nothing and the menu read as broken; repeated clicks on any chip painted its label (#6379). The rail's close verbs move to railTabMenu.tsx so the unlisted chip can offer them without a row view-model, and the chip is select-none, with a rename input keeping its own selection.
…le does

The composer's export is markdown with Lexical's escapes in it, so a literal render showed hi\_name for a typed hi_name. Through the same pipeline the assistant uses, minus the typewriter.
It was colorTextLightSolid — white on both fills. colorBgContainer is white on the light theme's ink and ink on the dark theme's yellow, the pairing Home's send already uses.
…own branch

markdownIcons lives on the files-pane branch, which this one does not sit on; CI's typecheck had no such name.
…lidator

useProjectWatch now calls invalidateAgentCommittedRevisionCache on a workflow change; the desktop suite's mock of @agenta/entities/workflow lacked it.
Every mounted sub-app under the services container answered 404 to an
authenticated request. /services/health worked, so the container looked fine,
but /services/agent/v0/* — invoke, inspect, runtime/subscription-status — all
returned {"detail":"Not Found"}. On /m that surfaces as an agent that is
created and then cannot run: the first message comes back "was not sent".

The prefix is handled twice already. Traefik strips /services before the
request reaches the container (services-strip), and the app strips it again
itself for ingresses that cannot rewrite paths
(ServicesPrefixStripMiddleware, added in #6702). Passing --root-path /services
on top of that breaks routing into the Mount()ed sub-apps.

Proved by running the same app inside the same container on a second port
without the flag: the identical authenticated request returns 200 with a live
runner body for both /agent/v0/... and /services/agent/v0/... path shapes.

The api container keeps its --root-path /api: it has no mounted sub-apps, so
the same interaction does not arise there.
Home's template rows bound the composer to a whole template but sent only its
NAME on submit, so a template picked there was the one create path the session
could not gate: the key never reached the stashed task, and the connect step
had nothing to look up.

HomeFocus already read binding.template.key a few lines below, so this is that
same value through three signatures — HomeFocus, useHomeHandoff, and
createFromPrompt, which hands it to run() where templateKey was already
supported and already stashed.

Free text on Home stays ungated on purpose. It declares no accounts, the
builder decides its tools during the very turn being held, and the mid-run
connect dock covers what it turns out to need.

All four create paths now behave the same: template entries ask for the
template's accounts in the session, free text does not, and the step runs in
exactly one place.
Six cases for useSessionSetupStep, the one piece of this flow with no coverage.
The step's own rules live in useAgentSetupStep and are tested in entity-ui;
what is this hook's own is narrower and is what these assert: it reads the
template key off the stashed task, waits for the connections query before
deciding, and reports blocking so the held first message is not sent under it.

The two that would bite in production: a session with no template never holds,
and a step that declines to open releases at once rather than parking a message
behind a card that never appears.

Note for anyone running these: use `pnpm exec vitest`, not `npx vitest`. npx
resolves the workspace-root vitest, which has no jsdom, and the seven jsdom
suites are then skipped silently — 28 files instead of 35.
Retargets off release/v0.118.1, which is finished — zero commits ahead of main,
39 behind, nothing open on it. release/v0.118.2 is the live one.

One conflict, in SessionWorkspace.tsx, and both sides were keepers. Ashraf's
"automation row actions run and open history on /m" added useTriggerTestRun;
ours replaced the plain configPanelCollapsedAtom read with the resolved
collapse state (override, preference, viewport). Kept the hook and kept the
resolved state, since it supersedes the read the other side still used.

pnpm install: the release branch added sonner, which the app-message store and
the toast component now import.
@coderabbitai

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Advanced

Run ID: f0b47a63-d9cd-41b2-9417-882bfe7f073b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

ashrafchowdury and others added 3 commits September 17, 2026 12:11
[feat] Fold each mobile turn into one activity timeline
… draws a landed chip twice

Review: keepPreviousData held the previous set's pages while a new set loaded, so a tab closed a moment ago could leave useSessionTabRows. And the commit a row landed in drew its chip from both the rows and the unlisted state, one effect late. The rows filter at the hook; the rail derives what it renders synchronously.
…he task

Review: a start whose navigation was cancelled restored the draft but had already written the memory.
@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

Railway Preview Environment

Status Destroyed (PR closed)

Updated at 2026-09-17T12:12:49.462Z

[fix] Session tabs on /m stay put, follow renames and revisions, and close cleanly
@ashrafchowdury
ashrafchowdury changed the base branch from release/v0.118.3 to main September 17, 2026 09:37
@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

📘 Docs preview

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

This comment updates in place on every push.

@ashrafchowdury
ashrafchowdury merged commit e83525a into main Sep 17, 2026
75 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.

2 participants