Skip to content

[release] v0.114.1 - #6250

Open
github-actions[bot] wants to merge 124 commits into
mainfrom
release/v0.114.1
Open

[release] v0.114.1#6250
github-actions[bot] wants to merge 124 commits into
mainfrom
release/v0.114.1

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

New version v0.114.1 in

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

AJ-ing and others added 30 commits August 2, 2026 11:54
Expose the existing GenerateResetLinkModal / PasswordResetLinkModal from
the workspace members Actions dropdown and call POST /api/profile/reset-password.
Permission-gated via invite/role-modify rights. Addresses #2572 (prior #4561
was closed for missing demo/template).

Co-authored-by: Cursor <cursoragent@cursor.com>
Demo artifact for #2572 / PR contribution check (members Actions →
confirm modal → generated reset link modal).

Co-authored-by: Cursor <cursoragent@cursor.com>
# Conflicts:
#	web/oss/src/components/pages/settings/WorkspaceManage/cellRenderers.tsx
The rebase onto main surfaced more than a conflict: main's WorkspaceManage
screen moved through a shadcn migration since this branch was cut, and the
antd Actions dropdown this PR wired reset-password into no longer exists —
cellRenderers.tsx's Actions component was removed and WorkspaceManage.tsx
only imports Roles from it now. Merging as-is would have resurrected a dead,
unimported component; the feature would not have appeared in the app.

Ported the same permission gate, modals, and handler onto the actions column
that replaced it (declarative items array on WorkspaceManage.tsx's "actions"
column, alongside rename/resend/remove). Since the confirm+link flow now
runs outside any per-row scope, added resetTarget state so the modals know
which member they're acting on.

Also excluded the org owner as a target, matching the existing gate on
`remove`: the backend has no owner-protection on reset-password either
(api/oss/src/services/user_service.py), so an admin resetting the owner's
password would hand them a login link into the owner's account.

Fixed a pre-existing type error in services/profile/index.ts: fetchJson
isn't generic (`Promise<any>`), so `fetchJson<string>(...)` didn't compile.
The frontend hides the "Reset password" action for the org owner, but that's
a UI nicety only — nothing stopped an admin from calling this endpoint
directly with the owner's user_id and minting a login link into their
account. Reject the target at the API boundary, where it's actually
enforced.

Same gap likely exists on DELETE /workspaces/{id}/users (remove_user_from_workspace
has no owner check either), but that's pre-existing and out of scope here.
Per web/CLAUDE.md, new API code goes through the Fern-generated client, not
raw fetchJson. A generated accessor for this exact route already existed
unused (client.users.resetUserPassword -> POST profile/reset-password);
added the getUsersClient() resource accessor (matching every other resource
in resources.ts) and switched resetPassword to use it, validating the
response with zod/safeParseWithLogging instead of coercing whatever comes
back with String(), which could silently turn an error payload into a
displayed "[object Object]" link.
onGenerateLink called onOk then immediately onCancel, which looked like it
closed the confirmation modal before the reset request resolved. It never
actually ran: it was wired as onOk={onGenerateLink}, but {...props} spreads
after it in the JSX and every real caller passes its own onOk, so the
caller's handler always wins and onGenerateLink is unreachable. The actual
caller (WorkspaceManage.tsx) already closes the modal itself, only on
success. Removed the dead wrapper so the code doesn't misrepresent what
runs.
The Rename session modal has a single text field, but Enter did nothing:
the only way to confirm was clicking the Rename button.

Wire onPressEnter on the Input to the same submit path as onOk, closing
the dialog first so the flow matches a button click. A blank name is
ignored, exactly as onOk already did.

Fixes #5951
Address review: the Enter path guarded a blank name but onOk did not, so
the two confirmation paths could drift. The Rename button is now disabled
while the field is blank, which is the same condition Enter checks, and
the test unmounts its React root in afterEach like ProjectWatch.test.tsx.
The blank-name cases deliberately leave the confirm dialog open, and root.unmount() does not close it: modal.confirm instances live in antd's global destroy registry, not the React root. Call Modal.destroyAll() before unmounting so no dialog leaks into the next test.
Enter destroyed the dialog before submitting, so it skipped the loading state and error handling that clicking Rename gets from onOk. Both paths now share one confirm() that keeps the dialog open while the rename is in flight.
The OK button's loading state blocks a second click while a rename is in
flight, but the input stays enabled, so repeated Enter presses could each
call submit() and issue duplicate rename mutations.
submit() returned normally when setSessionHeader() reported failure, so
both confirmation paths treated a failed rename as a success: the button's
onOk closed the dialog, and Enter's catch block never ran, discarding the
typed name behind an error toast.

submit() now rejects on failure. The OK button confirms through the same
confirm() helper as Enter rather than through onOk, because onOk's only
way to hold the dialog open is a rejected promise, which antd re-raises
as an unhandled rejection.
Confirming through antd's own OK button keeps submit, the loading state
and close-on-success on the existing onOk path instead of reimplementing
them for the keyboard.
Disable the Rename button while the field is blank, so both Enter (which
clicks it) and the button itself are no-ops and leave the dialog up.
…space-reset-password

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	web/packages/agenta-sdk/src/resources.ts
The thumb travel was written as translate-x-[calc(theme(width.switch)...)],
which only Tailwind v3 resolves. web/mobile runs v4, where neither theme()
nor the --tw-translate-* chain resolves, so the composed transform computed
to `none` and the thumb never moved on ANY switch in that app -- the track
changed colour but the knob stayed put.

Emit a self-contained transform: translateX(<px>) instead, the same literal-px
bridge mobile/src/styles/globals.css already uses for w-switch. v3 geometry is
byte-identical (22/12px travel, 23.4/15.6px press-stretch), so the antd parity
baseline is unaffected.
Voice input was gated behind both a per-user Settings toggle and
NEXT_PUBLIC_AGENT_VOICE_INPUT; both are gone, so the composer mic is
unconditional on desktop and mobile alike. The voice-message mode is
hidden behind VOICE_MESSAGE_MODE_ENABLED in VoiceInputButton: the agent
service still cannot take audio attachments, so the recorder, the takeover
bar and the send-vs-attach path stay wired but unreachable (D14).

Adds push-to-talk - hold Ctrl+Option (macOS) or Ctrl+Alt (Windows/Linux)
to dictate, release to stop, transcript left in the composer for review.
That chord is AltGr on European layouts and VoiceOver's VO modifier on
macOS, so it arms only after a 300ms hold, cancels on any other keypress,
and binds the left Alt only off-Mac. It lives in @agenta/chat next to the
dictation state it drives, rather than with the app's session shortcuts:
useSessionShortcuts has had no caller since the package carve.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Closes #6026. The playground build kit had a single switch: all 16 platform
tools or none. Each platform tool now carries its own switch, with an
"N of M enabled" count and an Enable all / Disable all bulk control.

The master switch keeps its existing meaning (the whole kit: platform tools,
embedded tools/skills and sandbox permissions) and gates the rows; it is not
tri-state, and switching every tool off does not flip it. Embedded tools and
skills stay locked -- Agenta owns them.

Frontend-only. The wire tool config is extra="forbid", so a switched-off op is
expressed by omitting it from the throwaway run copy, which is what the overlay
merge already does for anything absent. Per-tool state is in-memory and keyed by
revision, matching the existing master flag.

RailField gains an opt-in `wide` prop (these rails are full-width lists, not
form inputs that read better capped at prose width) and ItemRow gains `extra`
and `inactive` for the per-row control and its off state.
Stage 2's voice half has landed: the flag is gone and dictation ships with
a push-to-talk chord. Recordings stay parked behind
VOICE_MESSAGE_MODE_ENABLED pending the D6 workspace-only path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A skill whose name is not a lowercase-hyphen slug (e.g. "Weather", "My Skill")
saved fine — the config is just JSON to the backend — and then failed the whole
agent run the first time it was invoked: AgentTemplate.from_params parses
parameters.agent.skills through parse_skill_templates, whose pydantic model pins
name to ^[a-z0-9]+(-[a-z0-9]+)*$, and raises SkillValidationError. The runner
re-checks the same pattern on the untrusted wire and silently skips a skill that
fails it. The slug rule is not ours to relax: the harnesses key a skill by its
directory name and invoke it as /skill:name.

Mirror those rules client-side in skillName.ts and wire them into the skill
drawer, so the mistake surfaces inline at authoring time instead of as an opaque
run failure. Save is now blocked on the same fields the SDK requires (name,
description, SKILL.md body), each with a terse message under its field, and a bad
name offers its slugified form as a one-click fix.
Config edits in the agent playground landed in a browser-local draft and stayed
there until someone clicked Commit and filled in a modal. An agent could run with
a changed model, granted permissions and rewritten instructions while none of it
had reached triggers, schedules, or a second browser.

A per-revision engine now watches the entity draft and commits it ~1.5s after the
last edit, with the message classifyAgentChanges/buildCommitSummaryMessage already
generate. One subscription covers every write path, because the drawers buffer in
their own scoped draft and only touch the entity draft on Save.

The Commit button and its modal leave the agent header. Save stays only where
auto-commit deliberately will not act: a non-latest revision (so an accidental
edit cannot rewrite history), a failed save, or a draft stranded by a run.

Auto-commit is held while a run owns the revision — the agent's own
commit_revision checks HEAD, so a concurrent unattended commit would fail it.
…ed on

saveSection re-read revisionIdRef.current, so if the active revision changed
while the Advanced drawer was open, the old draft was written to the NEW
revision build-kit atoms. openSectionDrawer already snapshots the revision;
store it on sectionBaseline and write back to that one.

Raised by CodeRabbit on #6145.
The overlay check ran only on keydown, so a dialog opening inside the 300ms
arm delay let the mic open behind it. Recheck when the timer fires.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The tracker marked Stage 2 in progress while the State section still pointed
at Stage 0/1 and the next-actions list treated Stage 2 as untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review follow-up on #6142.

skillNameError trimmed before testing the pattern, but the Name field stores
e.target.value untrimmed. So "weather " passed the form and was saved, then
failed the SDK's pattern at run time: exactly the failure this validation
exists to prevent. Validate the raw string instead, and give surrounding
whitespace its own message, since "weather " looks correct and the generic
rule reads like a false positive on it. The slug suggestion strips the spaces,
so the one-click fix still resolves it.

Also compare the suggestion against the raw name, not the trimmed one, or the
fix button hides itself in exactly the whitespace case that needs it.

An upload now settles the touched state whatever it carried, so a package with
no name or description shows "Required." instead of only a dead Save button.
The slug button gains hover, focus-visible and disabled states, and the longer
comment blocks come down to one line each.
The HTTP MCP server form could only pick an existing project secret, so a user
who did not have one yet had to abandon the flow, create it in Settings, and
come back. The Project secret picker now pins a Create secret action that opens
a nested drawer; on save the secret is written to the project vault and selected
in the MCP draft. Refs #6059.

To avoid a second copy of the form, the Settings create/edit form is extracted
into a shared SecretForm + useSecretForm in @agenta/entity-ui and both surfaces
render it; ConfigureSecretModal becomes a thin EnhancedModal wrapper. slugifyBase
moves to @agenta/shared/utils alongside a new toEnvVarName.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ashrafchowdury and others added 5 commits August 26, 2026 12:28
… empty

A user turn whose timestamp resolved to nothing still drew the dot before the
rewind button, leaving a separator with nothing to separate.

The footer inferred emptiness from its inputs, which cannot be right: only the
segment itself knows whether it rendered. Every leading separator now carries
`first:hidden`, so a dot that lands as the row's first element hides itself.
TurnMetrics emits its own leading dot as a sibling in the footer row rather than
inside a wrapper, so `:first-child` resolves against the row, and
ExecutionMetricsDisplay loses the `separator` prop — the caller owns the dot in
front of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The copy button was assistant-only on the desktop, and the shared footer had
carried that rule over to /m, which used to offer it on both roles. Copying back
what you asked for is useful on either side of the conversation, so the button
now shows wherever there is text to copy.

Both apps already derive copy text from every text part of the message, so this
is only the gate in the shared footer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copying mid-stream takes whatever half of the reply had arrived, which reads as
a broken copy rather than a partial one. The footer now hides the button until
the turn settles.

Only copy is gated. Rewind keeps its existing busy guard in the handler, and the
trace button is unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The menu is short enough that clearing a facet by hand is no slower than
finding a reset, and the row sat greyed out most of the time.

Its `dirty` prop goes with it — inside FilterMenu that flag only decides
whether the reset row is disabled, so with no row it did nothing. `dirty`
itself stays: it still lights the dot on the trigger. FilterMenu keeps
`onReset` optional, so other surfaces can still opt in.
[feat] Simplify the agent turn footer and gate the trace button
ashrafchowdury and others added 5 commits August 26, 2026 15:57
…ession

The Pinned heading already says a row is pinned, so the pin glyph repeated it
— and cost real information: it replaced the status dot, so a pinned session
waiting on you looked identical to an idle one.

Mobile already worked this way ("No pin glyph: pinned rows sit under their own
heading, which says it once"); this brings the desktop registry in line.
…g a list

Clicking Sessions on the icon rail dropped a fifteen-row popover next to the
cursor. That is a list to read, not a menu to pick from, and it is not what the
icon implies — a nav icon means "take me to this section".

Set hideChildrenWhenCollapsed on the three parents that carry dynamic children
(Prompts, Agents, Sessions) in both hosts, so a collapsed rail renders them as
plain tooltipped links to their section. The flag and its render path already
existed and nothing had ever set it; every one of these parents already carries
the right `link`. Expanded rails are untouched.
A collapsed rail renders no inline children — a group is either a flyout or a
plain link there — but the gate decided purely from open-state, which knows
nothing about collapse. Sessions is `alwaysOpen`, so it kept fetching and
polling on a 15s interval with nowhere to render. Making the parents navigate
instead of flyout removed the last consumer of that data.

The shell publishes its collapsed state per scope and the gate treats inline
open-state as false while collapsed; an open flyout still subscribes, which is
the only way children render there. Per scope, not a global read: each host
hands SidebarShell its own collapsed atom — the mobile drawer's expanded flag
is not the desktop rail's — so only the shell knows.
…mmit

[feat] Auto-commit agent playground config changes
[feat] Plain-language approval card for the agent chat
ashrafchowdury and others added 5 commits August 26, 2026 17:53
The PR's base moved from main to the release branch. One file conflicted,
useSessionActions.tsx, where the release branch had refined the rename modal
(Enter-to-confirm, disabled OK on a blank name) and we had rewritten the same
region. Resolved by dropping the incoming refactor and deleting the modal
outright — the rail already renamed in place, so the popup was a second way to
do one thing.

Deleting it would have left the Sessions page and the mobile list with a dead
Rename entry, since neither had inline editing. So the rail's implementation is
now a shared `useInlineRename` + `InlineRenameInput`, and both surfaces render
the same edit: Enter saves, Escape abandons, blur saves, a blank or unchanged
name is not a rename. `commitRename` is unchanged and still the single write
path, so a rename from any list retitles an open chat tab.

SessionsListView grew a per-row component to own that state: a row is offered
"rename" by both its kebab and the right-click menu wrapping it, and one
`useInlineRename` above both is what makes them drive the same edit.

The release branch's five modal tests go with the modal.
Group by defaults to None, and None means a flat list. Last activity stays at
7 days and now declares it as its default, so neither reads as a filter you
applied; the reset row is back to return both.

Pinned only and Archived only are gone — neither answered a question you would
ask of a nav rail — and archived sessions are simply never listed. In their
place, Show automations: off by default like the sessions page, because a
schedule running hourly buries the conversations you are having. It maps to the
server's origin predicate rather than filtering after the fetch. A bolt icon,
not the robot: the robot means AGENT everywhere else in the rail.

Pinned first is retired too. Pins lead under every grouping, so it grouped by
nothing. A stored value from before it went falls back to the default rather
than rendering a facet the menu cannot show.

The Agent facet listed nothing, which is why it did not work: its options were
gated on the persisted open-groups set, and Sessions renders alwaysOpen, so its
key is never written there. Long agent names now truncate — the label always
had `truncate`, but the submenu had no max width, so it widened to fit instead.

The dirty dot is off the button; it still gates the reset row.
The shell opens a group four ways — persisted, defaultOpen, alwaysOpen, and the
ROUTE landing inside it — and the gate knew only the first three. Navigating to
/agents expands the Agents group without writing anything persisted, so its
query stayed idle under a visibly open group and the rail read Open to load
next to a page already listing those agents. Clicking the caret hid it, because
that does persist.

The shell now publishes its route-opened ancestors per scope and the gate ORs
them in, the same contract alwaysOpen and the collapsed state already use.

Also covers the Group by: None regression in tests. `groupedChildren` renders
group by group and drops any row whose key it was not given, so declaring only
the pinned group hid every other row — the rail showed pins and nothing else.
Two tests pin the contract: declare a group for every row, or declare none and
fall through to the flat list.
…s page

The toggle widened the list: on, it showed your sessions AND the automation
runs together. The sessions page's toggle swaps instead, flipping between
exclude-trigger and trigger-only, so the same words meant two different things
on two surfaces.

The rail now swaps too. Off lists what you started, on lists what your triggers
ran. Both directions are the server's own origin predicate, so neither narrows
a page after fetching it and the row cap still means what it says.
feat(frontend): Group and filter sessions in the nav sidebar
@dosubot dosubot Bot removed the size:M This PR changes 30-99 lines, ignoring generated files. label Aug 26, 2026
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Aug 26, 2026
ashrafchowdury and others added 2 commits August 26, 2026 19:05
Contributors, and increasingly AI coding agents, attach screenshots of a
mock-up built to imitate the screen rather than a capture of the real app.
Those images prove nothing about the change and hide the layout, theme, and
data problems a reviewer needs to see. Spell the rule out in the PR template
(where agents read it) and in CONTRIBUTING.md, and reword the demo checklist
item to match.

Also tighten the issue assignment window from one week to two days of visible
progress, so stalled issues return to the pool sooner.
…reenshots

docs(contributing): Require demo screenshots from the running app
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants