From e1df24ccb5653acc2a47af565d3b64d58c5c1f07 Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Mon, 15 Jun 2026 11:49:54 +0200 Subject: [PATCH 01/14] fix(hosting): scope gh.local traefik to its own compose project --- hosting/docker-compose/ee/docker-compose.gh.local.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/hosting/docker-compose/ee/docker-compose.gh.local.yml b/hosting/docker-compose/ee/docker-compose.gh.local.yml index 7e72548082..20465efb50 100644 --- a/hosting/docker-compose/ee/docker-compose.gh.local.yml +++ b/hosting/docker-compose/ee/docker-compose.gh.local.yml @@ -409,6 +409,7 @@ services: - --api.dashboard=true - --api.insecure=true - --providers.docker + - --providers.docker.constraints=Label(`com.docker.compose.project`,`${COMPOSE_PROJECT_NAME:-agenta-ee-gh-local}`) - --entrypoints.web.address=:80 # === STORAGE ============================================== # volumes: From 47f113d44fd304862dd82cea5b1fd5ee013c58fc Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Tue, 16 Jun 2026 22:34:24 +0600 Subject: [PATCH 02/14] feat(editor): enhance sticky header behavior and improve paste handling --- .../PlaygroundVariantConfig/index.tsx | 4 ++++ .../components/PlaygroundConfigSection.tsx | 18 +++++++++++++++-- .../Editor/plugins/code/utils/pasteUtils.ts | 16 ++++++++++----- .../src/SharedEditor/SharedEditorImpl.tsx | 20 +++++++++++++++---- 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/web/oss/src/components/Playground/Components/PlaygroundVariantConfig/index.tsx b/web/oss/src/components/Playground/Components/PlaygroundVariantConfig/index.tsx index 90e2bf80ee..0d943a1480 100644 --- a/web/oss/src/components/Playground/Components/PlaygroundVariantConfig/index.tsx +++ b/web/oss/src/components/Playground/Components/PlaygroundVariantConfig/index.tsx @@ -217,6 +217,10 @@ const PlaygroundVariantConfig: React.FC< revisionId={variantId} onRefinePrompt={handleRefinePrompt} viewMode={viewMode} + // Embedded (drawer) renders the variant config + // header non-sticky, so the section headers have + // nothing to clear — pin them at the scroll top. + stickyHeaderTop={embedded ? 0 : 48} /> diff --git a/web/packages/agenta-entity-ui/src/DrillInView/components/PlaygroundConfigSection.tsx b/web/packages/agenta-entity-ui/src/DrillInView/components/PlaygroundConfigSection.tsx index 0618b14bd8..a77bcad86e 100644 --- a/web/packages/agenta-entity-ui/src/DrillInView/components/PlaygroundConfigSection.tsx +++ b/web/packages/agenta-entity-ui/src/DrillInView/components/PlaygroundConfigSection.tsx @@ -451,6 +451,15 @@ export interface PlaygroundConfigSectionProps { onRefinePrompt?: (promptKey: string) => void /** View mode controlled from parent (form/json/yaml) */ viewMode?: ConfigViewMode + /** + * Top offset (px) for the sticky section headers. Defaults to 48 to clear + * the sticky `PlaygroundVariantConfigHeader` (h-[48px], `sticky top-0`) that + * sits above the config in the full playground. In the embedded drawer that + * header is rendered non-sticky (`grow`), so there is nothing to clear — + * pass 0 there to keep the section headers flush with the scroll top instead + * of floating 48px down into the editor content. + */ + stickyHeaderTop?: number } function PlaygroundConfigSection({ @@ -461,6 +470,7 @@ function PlaygroundConfigSection({ moleculeAdapter, onRefinePrompt, viewMode: externalViewMode, + stickyHeaderTop = 48, }: PlaygroundConfigSectionProps) { const {llmProviderConfig} = useDrillInUI() @@ -1362,7 +1372,8 @@ function PlaygroundConfigSection({ return (
toggleSection(fieldKey)} >
@@ -1578,7 +1589,10 @@ function PlaygroundConfigSection({ ) : ( <> {!hasTopLevelObjectSection && ( -
+
Config
)} diff --git a/web/packages/agenta-ui/src/Editor/plugins/code/utils/pasteUtils.ts b/web/packages/agenta-ui/src/Editor/plugins/code/utils/pasteUtils.ts index 39c12219d0..10f0055617 100644 --- a/web/packages/agenta-ui/src/Editor/plugins/code/utils/pasteUtils.ts +++ b/web/packages/agenta-ui/src/Editor/plugins/code/utils/pasteUtils.ts @@ -117,8 +117,13 @@ export function $insertLinesWithSelectionAndIndent({ } } - // Clone trailing lines before removal - const clonedTrailingLines = linesAfter.map((l) => $copyNode(l)) + // Capture trailing-line TEXT before removal, then rebuild fresh nodes after + // insertion (see re-insertion below). We can't element-clone these lines: + // `$copyNode` assigns the clone a fresh key, and Lexical's ElementNode only + // carries child pointers when the key is unchanged — so a cloned CodeLineNode + // comes back EMPTY, wiping every line below the paste point. Rebuilding from + // text mirrors the large-paste fast path and preserves the content faithfully. + const trailingLineTexts = linesAfter.map((l) => l.getTextContent()) linesAfter.forEach((l) => l.remove()) // Remove current line currentLine.remove() @@ -174,11 +179,12 @@ export function $insertLinesWithSelectionAndIndent({ insertIdx++ } - // Add trailing lines (use clones) - clonedTrailingLines.forEach((l, i) => { + // Add trailing lines (rebuilt from captured text — see note above) + const trailingLanguage = parentBlock.getLanguage() + trailingLineTexts.forEach((text, i) => { const prevChild = $getLineAtIndex(parentBlock, insertIdx - 1 + i) if (prevChild) { - prevChild.insertAfter(l) + prevChild.insertAfter($createNodeForLineWithTabs(text, trailingLanguage)) } }) diff --git a/web/packages/agenta-ui/src/SharedEditor/SharedEditorImpl.tsx b/web/packages/agenta-ui/src/SharedEditor/SharedEditorImpl.tsx index 8c6a2e027f..ab93140354 100644 --- a/web/packages/agenta-ui/src/SharedEditor/SharedEditorImpl.tsx +++ b/web/packages/agenta-ui/src/SharedEditor/SharedEditorImpl.tsx @@ -453,12 +453,24 @@ const SharedEditor = ({ style={ { ...props.style, - interpolateSize: "allow-keywords", height: "var(--editor-h, auto)", overflow: "hidden", - transitionProperty: "height", - transitionDuration: "300ms", - transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)", + // The height transition exists only to animate the collapse + // toggle (which sets `--editor-h` to a fixed px ↔ auto). Code + // editors never use that toggle, so `--editor-h` stays `auto` + // and `interpolate-size: allow-keywords` would instead animate + // EVERY content-height change while typing/pasting — making the + // editor grow over 300ms and lurching the surrounding drawer's + // scroll. Disable the animation for codeOnly so growth is + // instant; keep it for the collapsible rich-text/prompt editors. + ...(editorProps?.codeOnly + ? {} + : { + interpolateSize: "allow-keywords", + transitionProperty: "height", + transitionDuration: "300ms", + transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)", + }), } as React.CSSProperties } onPasteCapture={handlePasteCapture} From 69088bde006a2cc28ddb3f8e20e325da59f83a60 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 16 Jun 2026 11:18:30 +0200 Subject: [PATCH 03/14] fix(sdk): normalize chat_v0 output to the canonical Message shape chat_v0 returned the raw LiteLLM message, leaking provider-specific fields (provider_specific_fields, annotations) into outputs. Validate through the SDK Message model so the output is the canonical {role, content, name, tool_calls, tool_call_id}, matching what a hook/custom workflow returns. --- sdks/python/agenta/sdk/engines/running/handlers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sdks/python/agenta/sdk/engines/running/handlers.py b/sdks/python/agenta/sdk/engines/running/handlers.py index fbc515674a..ac1174c9ad 100644 --- a/sdks/python/agenta/sdk/engines/running/handlers.py +++ b/sdks/python/agenta/sdk/engines/running/handlers.py @@ -2220,7 +2220,10 @@ async def chat_v0( message = response.choices[0].message # type: ignore - return message.model_dump(exclude_none=True) # type: ignore + # Normalize to the canonical Message shape (drops provider-specific fields). + return Message.model_validate(message.model_dump(exclude_none=True)).model_dump( + exclude_none=True + ) @instrument(ignore_inputs=["parameters"]) From 3bc43ae7e267fbc0922c035f324de205f6cbea50 Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Wed, 17 Jun 2026 20:24:55 +0200 Subject: [PATCH 04/14] fix(frontend): cap sidebar banner queue --- .../components/SidebarBanners/state/atoms.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/web/oss/src/components/SidebarBanners/state/atoms.ts b/web/oss/src/components/SidebarBanners/state/atoms.ts index 6724400836..8697bd6e11 100644 --- a/web/oss/src/components/SidebarBanners/state/atoms.ts +++ b/web/oss/src/components/SidebarBanners/state/atoms.ts @@ -19,6 +19,13 @@ export const PRIORITY_ORDER: Record = { trial: 3, // Lowest priority - show after other banners are dismissed } +/** + * Maximum number of dismissible sidebar banners a user should have to clear. + * Apply this before dismissal filtering so older changelog entries do not + * backfill the sidebar after each close. + */ +export const MAX_DISMISSIBLE_SIDEBAR_BANNERS = 2 + /** * Persisted atom for dismissed banner IDs. * Uses localStorage to remember which banners the user has dismissed. @@ -107,8 +114,17 @@ export const activeBannersAtom = atom((get) => { export const visibleBannersAtom = atom((get) => { const allBanners = get(activeBannersAtom) const dismissedIds = get(dismissedBannerIdsAtom) + const sortedBanners = [...allBanners].sort( + (a, b) => PRIORITY_ORDER[a.type] - PRIORITY_ORDER[b.type], + ) + + const cappedDismissibleBanners = sortedBanners + .filter((banner) => banner.dismissible) + .slice(0, MAX_DISMISSIBLE_SIDEBAR_BANNERS) + + const nonDismissibleBanners = sortedBanners.filter((banner) => !banner.dismissible) - return allBanners + return [...cappedDismissibleBanners, ...nonDismissibleBanners] .filter((banner) => !dismissedIds.includes(banner.id)) .sort((a, b) => PRIORITY_ORDER[a.type] - PRIORITY_ORDER[b.type]) }) From e4fb43ee631c1e0a1e9c68356cc19be0a08005b6 Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Wed, 17 Jun 2026 20:25:02 +0200 Subject: [PATCH 05/14] fix playwright browser cache fallback --- .github/workflows/12-check-unit-tests.yml | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/workflows/12-check-unit-tests.yml b/.github/workflows/12-check-unit-tests.yml index 157d81e470..a2a5f3e103 100644 --- a/.github/workflows/12-check-unit-tests.yml +++ b/.github/workflows/12-check-unit-tests.yml @@ -88,7 +88,9 @@ jobs: uses: actions/cache@v4 with: path: ~/.cache/ms-playwright - key: ${{ runner.os }}-playwright-browsers + key: ${{ runner.os }}-playwright-browsers-${{ hashFiles('web/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-playwright-browsers- # System deps for chromium only (not the full webkit/firefox apt set, # which pulls in the gstreamer/audio/video stack and takes ~14m). Runs @@ -98,10 +100,23 @@ jobs: working-directory: web/tests run: pnpm exec playwright install-deps chromium - - name: Install Playwright if not cached - if: (github.event_name != 'workflow_dispatch' || contains(fromJSON('["all","web-only"]'), inputs.packages)) && steps.restore-playwright-cache.outputs.cache-hit != 'true' + - name: Install Playwright browser + if: github.event_name != 'workflow_dispatch' || contains(fromJSON('["all","web-only"]'), inputs.packages) working-directory: web/tests - run: pnpm exec playwright install chromium + run: | + for attempt in 1 2 3; do + echo "::group::playwright install chromium (attempt ${attempt}/3)" + if timeout 180 pnpm exec playwright install chromium; then + echo "::endgroup::" + echo "browser install succeeded on attempt ${attempt}" + exit 0 + fi + echo "::endgroup::" + echo "attempt ${attempt} stalled or failed; retrying after 5s..." + sleep 5 + done + echo "playwright browser install failed after 3 attempts" >&2 + exit 1 - name: Run web unit tests if: github.event_name != 'workflow_dispatch' || contains(fromJSON('["all","web-only"]'), inputs.packages) From 492a8b4e1a4401bd9c5e89bc2829b695d049c044 Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Wed, 17 Jun 2026 20:46:02 +0200 Subject: [PATCH 06/14] fix(railway): select service when redeploying previews --- hosting/railway/oss/scripts/deploy-from-images.sh | 2 +- hosting/railway/oss/scripts/deploy-services.sh | 2 +- hosting/railway/oss/scripts/smoke.sh | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hosting/railway/oss/scripts/deploy-from-images.sh b/hosting/railway/oss/scripts/deploy-from-images.sh index 400869f4c2..80cdd1baf9 100755 --- a/hosting/railway/oss/scripts/deploy-from-images.sh +++ b/hosting/railway/oss/scripts/deploy-from-images.sh @@ -45,7 +45,7 @@ redeploy_service_if_exists() { local service="$1" if railway_call service "$service" >/dev/null 2>&1; then - railway_call redeploy --yes >/dev/null + railway_call redeploy --service "$service" --environment "$ENV_NAME" --yes >/dev/null fi } diff --git a/hosting/railway/oss/scripts/deploy-services.sh b/hosting/railway/oss/scripts/deploy-services.sh index 72fcf5e135..e9e5c4fe85 100755 --- a/hosting/railway/oss/scripts/deploy-services.sh +++ b/hosting/railway/oss/scripts/deploy-services.sh @@ -17,7 +17,7 @@ fi railway link --project "$PROJECT_NAME" --environment "$ENV_NAME" --json >/dev/null # Bring stateful infra up first so credentials/volumes are applied. -railway service "$POSTGRES_SERVICE" >/dev/null && railway redeploy --yes +railway service "$POSTGRES_SERVICE" >/dev/null && railway redeploy --service "$POSTGRES_SERVICE" --environment "$ENV_NAME" --yes if railway service "$REDIS_SERVICE" >/dev/null 2>&1; then railway up hosting/railway/oss/redis --path-as-root --service "$REDIS_SERVICE" --detach fi diff --git a/hosting/railway/oss/scripts/smoke.sh b/hosting/railway/oss/scripts/smoke.sh index 27bdf681fa..1a9e23f928 100755 --- a/hosting/railway/oss/scripts/smoke.sh +++ b/hosting/railway/oss/scripts/smoke.sh @@ -92,16 +92,16 @@ repair_path() { case "$path" in "/w") - railway service web >/dev/null && railway redeploy --yes >/dev/null - railway service gateway >/dev/null && railway redeploy --yes >/dev/null + railway service web >/dev/null && railway redeploy --service web --environment "$ENV_NAME" --yes >/dev/null + railway service gateway >/dev/null && railway redeploy --service gateway --environment "$ENV_NAME" --yes >/dev/null ;; "/api/health") - railway service api >/dev/null && railway redeploy --yes >/dev/null - railway service gateway >/dev/null && railway redeploy --yes >/dev/null + railway service api >/dev/null && railway redeploy --service api --environment "$ENV_NAME" --yes >/dev/null + railway service gateway >/dev/null && railway redeploy --service gateway --environment "$ENV_NAME" --yes >/dev/null ;; "/services/health") - railway service services >/dev/null && railway redeploy --yes >/dev/null - railway service gateway >/dev/null && railway redeploy --yes >/dev/null + railway service services >/dev/null && railway redeploy --service services --environment "$ENV_NAME" --yes >/dev/null + railway service gateway >/dev/null && railway redeploy --service gateway --environment "$ENV_NAME" --yes >/dev/null ;; *) return 1 From 1465b062c206821f02d8be5723a0ba276ec5ef61 Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Wed, 17 Jun 2026 20:47:41 +0200 Subject: [PATCH 07/14] fix(hosting): scope every traefik instance to its own compose project --- hosting/docker-compose/ee/docker-compose.gh.yml | 1 + hosting/docker-compose/oss/docker-compose.gh.local.yml | 1 + hosting/docker-compose/oss/docker-compose.gh.yml | 1 + hosting/docker-compose/oss/ssl/traefik.yml | 3 ++- 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hosting/docker-compose/ee/docker-compose.gh.yml b/hosting/docker-compose/ee/docker-compose.gh.yml index a74e799626..83adfeda76 100644 --- a/hosting/docker-compose/ee/docker-compose.gh.yml +++ b/hosting/docker-compose/ee/docker-compose.gh.yml @@ -400,6 +400,7 @@ services: command: - --api.dashboard=true - --providers.docker + - --providers.docker.constraints=Label(`com.docker.compose.project`,`${COMPOSE_PROJECT_NAME:-agenta-ee-gh}`) - --entrypoints.web.address=:80 # === STORAGE ============================================== # volumes: diff --git a/hosting/docker-compose/oss/docker-compose.gh.local.yml b/hosting/docker-compose/oss/docker-compose.gh.local.yml index 4bc21b5293..98efae76df 100644 --- a/hosting/docker-compose/oss/docker-compose.gh.local.yml +++ b/hosting/docker-compose/oss/docker-compose.gh.local.yml @@ -409,6 +409,7 @@ services: command: - --api.dashboard=true - --providers.docker + - --providers.docker.constraints=Label(`com.docker.compose.project`,`${COMPOSE_PROJECT_NAME:-agenta-oss-gh-local}`) - --entrypoints.web.address=:80 # === STORAGE ============================================== # volumes: diff --git a/hosting/docker-compose/oss/docker-compose.gh.yml b/hosting/docker-compose/oss/docker-compose.gh.yml index d39ffb8643..b217180177 100644 --- a/hosting/docker-compose/oss/docker-compose.gh.yml +++ b/hosting/docker-compose/oss/docker-compose.gh.yml @@ -430,6 +430,7 @@ services: command: - --api.dashboard=true - --providers.docker + - --providers.docker.constraints=Label(`com.docker.compose.project`,`${COMPOSE_PROJECT_NAME:-agenta-oss-gh}`) - --entrypoints.web.address=:80 # === STORAGE ============================================== # volumes: diff --git a/hosting/docker-compose/oss/ssl/traefik.yml b/hosting/docker-compose/oss/ssl/traefik.yml index e479f9af83..acbe3fa6f7 100644 --- a/hosting/docker-compose/oss/ssl/traefik.yml +++ b/hosting/docker-compose/oss/ssl/traefik.yml @@ -25,4 +25,5 @@ certificatesResolvers: entryPoint: "web" # Validates domain ownership via port 80 providers: - docker: {} + docker: + constraints: "Label(`com.docker.compose.project`,`agenta-gh-ssl`)" From f8034fe4a7ebe48eec564c2733db2cd6f09c6eeb Mon Sep 17 00:00:00 2001 From: junaway <7041392+junaway@users.noreply.github.com> Date: Thu, 18 Jun 2026 11:59:36 +0000 Subject: [PATCH 08/14] v0.104.1 --- api/pyproject.toml | 2 +- api/uv.lock | 6 +++--- clients/python/pyproject.toml | 2 +- clients/python/uv.lock | 2 +- hosting/kubernetes/helm/Chart.yaml | 4 ++-- sdks/python/pyproject.toml | 2 +- sdks/python/uv.lock | 4 ++-- services/pyproject.toml | 2 +- services/uv.lock | 6 +++--- web/ee/package.json | 2 +- web/oss/package.json | 2 +- web/package.json | 2 +- web/packages/agenta-api-client/package.json | 2 +- 13 files changed, 19 insertions(+), 19 deletions(-) diff --git a/api/pyproject.toml b/api/pyproject.toml index d35b5004b1..49b67d707e 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "api" -version = "0.104.0" +version = "0.104.1" description = "Agenta API" requires-python = ">=3.11,<3.14" authors = [ diff --git a/api/uv.lock b/api/uv.lock index caf5b6e239..46e0565926 100644 --- a/api/uv.lock +++ b/api/uv.lock @@ -8,7 +8,7 @@ resolution-markers = [ [[package]] name = "agenta" -version = "0.104.0" +version = "0.104.1" source = { editable = "../sdks/python" } dependencies = [ { name = "agenta-client" }, @@ -70,7 +70,7 @@ dev = [ [[package]] name = "agenta-client" -version = "0.104.0" +version = "0.104.1" source = { editable = "../clients/python" } dependencies = [ { name = "httpx" }, @@ -259,7 +259,7 @@ wheels = [ [[package]] name = "api" -version = "0.104.0" +version = "0.104.1" source = { virtual = "." } dependencies = [ { name = "agenta" }, diff --git a/clients/python/pyproject.toml b/clients/python/pyproject.toml index 3a2a8eb324..8c6f2dd026 100644 --- a/clients/python/pyproject.toml +++ b/clients/python/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "agenta-client" -version = "0.104.0" +version = "0.104.1" description = "Fern-generated Python client for the Agenta API." requires-python = ">=3.11,<3.14" authors = [ diff --git a/clients/python/uv.lock b/clients/python/uv.lock index e1a385a8fb..bc64e2f822 100644 --- a/clients/python/uv.lock +++ b/clients/python/uv.lock @@ -4,7 +4,7 @@ requires-python = ">=3.11, <3.14" [[package]] name = "agenta-client" -version = "0.104.0" +version = "0.104.1" source = { editable = "." } dependencies = [ { name = "httpx" }, diff --git a/hosting/kubernetes/helm/Chart.yaml b/hosting/kubernetes/helm/Chart.yaml index d38313fb77..1905796e59 100644 --- a/hosting/kubernetes/helm/Chart.yaml +++ b/hosting/kubernetes/helm/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: agenta description: A Helm chart for deploying Agenta (OSS or EE) on Kubernetes type: application -version: 0.104.0 -appVersion: "v0.104.0" +version: 0.104.1 +appVersion: "v0.104.1" keywords: - agenta - llm diff --git a/sdks/python/pyproject.toml b/sdks/python/pyproject.toml index e5ef492fb9..49333aad01 100644 --- a/sdks/python/pyproject.toml +++ b/sdks/python/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "agenta" -version = "0.104.0" +version = "0.104.1" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" requires-python = ">=3.11,<3.14" diff --git a/sdks/python/uv.lock b/sdks/python/uv.lock index aa2895ade4..37f42a9323 100644 --- a/sdks/python/uv.lock +++ b/sdks/python/uv.lock @@ -4,7 +4,7 @@ requires-python = ">=3.11, <3.14" [[package]] name = "agenta" -version = "0.104.0" +version = "0.104.1" source = { editable = "." } dependencies = [ { name = "agenta-client" }, @@ -83,7 +83,7 @@ dev = [ [[package]] name = "agenta-client" -version = "0.104.0" +version = "0.104.1" source = { editable = "../../clients/python" } dependencies = [ { name = "httpx" }, diff --git a/services/pyproject.toml b/services/pyproject.toml index 20a4d1c799..8572ba45e7 100644 --- a/services/pyproject.toml +++ b/services/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "services" -version = "0.104.0" +version = "0.104.1" description = "Agenta Services (Chat & Completion)" requires-python = ">=3.11,<3.14" authors = [ diff --git a/services/uv.lock b/services/uv.lock index 4aa66882b9..e0b40277f3 100644 --- a/services/uv.lock +++ b/services/uv.lock @@ -8,7 +8,7 @@ resolution-markers = [ [[package]] name = "agenta" -version = "0.104.0" +version = "0.104.1" source = { editable = "../sdks/python" } dependencies = [ { name = "agenta-client" }, @@ -70,7 +70,7 @@ dev = [ [[package]] name = "agenta-client" -version = "0.104.0" +version = "0.104.1" source = { editable = "../clients/python" } dependencies = [ { name = "httpx" }, @@ -2378,7 +2378,7 @@ wheels = [ [[package]] name = "services" -version = "0.104.0" +version = "0.104.1" source = { virtual = "." } dependencies = [ { name = "agenta" }, diff --git a/web/ee/package.json b/web/ee/package.json index cfd9748b6d..c75047b2d3 100644 --- a/web/ee/package.json +++ b/web/ee/package.json @@ -1,6 +1,6 @@ { "name": "@agenta/ee", - "version": "0.104.0", + "version": "0.104.1", "private": true, "engines": { "node": "24.x" diff --git a/web/oss/package.json b/web/oss/package.json index ad5f9e31ed..118c6b6dc7 100644 --- a/web/oss/package.json +++ b/web/oss/package.json @@ -1,6 +1,6 @@ { "name": "@agenta/oss", - "version": "0.104.0", + "version": "0.104.1", "private": true, "engines": { "node": "24.x" diff --git a/web/package.json b/web/package.json index 92c533d843..2053606d57 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "agenta-web", - "version": "0.104.0", + "version": "0.104.1", "workspaces": [ "ee", "oss", diff --git a/web/packages/agenta-api-client/package.json b/web/packages/agenta-api-client/package.json index 88e8c6a0dc..e4566dcda4 100644 --- a/web/packages/agenta-api-client/package.json +++ b/web/packages/agenta-api-client/package.json @@ -1,6 +1,6 @@ { "name": "@agentaai/api-client", - "version": "0.104.0", + "version": "0.104.1", "private": true, "type": "module", "main": "./dist/index.js", From 3abf559899e43b9e696fa691a071246bbf8ccfc3 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 18 Jun 2026 21:25:06 +0600 Subject: [PATCH 09/14] fix(SessionDrawerContent): default-select the "Session" root node for better UX fix(SessionsTable): share page store to resolve session data correctly in table cells --- .../components/SessionDrawerContent.tsx | 5 ++++- .../components/SessionsTable/index.tsx | 14 +++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/web/oss/src/components/SharedDrawers/SessionDrawer/components/SessionDrawerContent.tsx b/web/oss/src/components/SharedDrawers/SessionDrawer/components/SessionDrawerContent.tsx index 2bb7320975..606c3c05b5 100644 --- a/web/oss/src/components/SharedDrawers/SessionDrawer/components/SessionDrawerContent.tsx +++ b/web/oss/src/components/SharedDrawers/SessionDrawer/components/SessionDrawerContent.tsx @@ -23,7 +23,10 @@ interface TraceDrawerContentProps { } const SessionDrawerContent = ({onClose, onToggleWidth, isExpanded}: TraceDrawerContentProps) => { - const [selected, setSelected] = useState("") + // Default-select the "Session" root node so the tree opens with a selection + // instead of nothing highlighted. The root node's key is always "root", and + // selecting it is a no-op in handleSelect (it early-returns for "root"). + const [selected, setSelected] = useState("root") const {isLoading} = useSessionDrawer() if (isLoading) { return ( diff --git a/web/oss/src/components/pages/observability/components/SessionsTable/index.tsx b/web/oss/src/components/pages/observability/components/SessionsTable/index.tsx index 0d15650646..188b362b27 100644 --- a/web/oss/src/components/pages/observability/components/SessionsTable/index.tsx +++ b/web/oss/src/components/pages/observability/components/SessionsTable/index.tsx @@ -2,7 +2,7 @@ import {useCallback, useEffect, useMemo, useState} from "react" import {InfiniteVirtualTableFeatureShell} from "@agenta/ui/table" import type {TableFeaturePagination, TableScopeConfig} from "@agenta/ui/table" -import {useAtomValue, useSetAtom} from "jotai" +import {useAtomValue, useSetAtom, useStore} from "jotai" import dynamic from "next/dynamic" import {SessionDrawer} from "@/oss/components/SharedDrawers/SessionDrawer" @@ -49,6 +49,14 @@ const SessionsTable: React.FC = () => { resetSessionPages, } = useSessions() + // The per-session cells (Traces count, First input, metrics, …) read their + // data from page-level atoms keyed by session id (e.g. `sessionsSpansAtom`). + // Without this, the table mounts its rows inside an isolated Jotai store + // (`useIsolatedStore` when no `store` is passed), where those atoms are empty + // — so every cell renders 0/"-" even though the data is loaded in the page + // store. Sharing the page store lets the cells resolve the real data. + const store = useStore() + const isNewUser = useAtomValue(isNewUserAtom) const onboardingStorageUserId = useAtomValue(onboardingStorageUserIdAtom) const openDrawer = useSetAtom(openSessionDrawerWithUrlAtom) @@ -111,7 +119,7 @@ const SessionsTable: React.FC = () => { const isEmptyState = sessionIds.length === 0 && !isLoading return ( -
+
{ ) : ( + store={store} tableScope={tableScope} columns={columns} rowKey="session_id" pagination={pagination} - autoHeight={false} resizableColumns enableExport={false} useSettingsDropdown={false} From f7797af69cb18d360e71c233152a26eebd3d0414 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 18 Jun 2026 21:34:08 +0600 Subject: [PATCH 10/14] fix(SessionIdCell): adjust Tag styling for improved layout and alignment --- .../SessionsTable/components/Cells/SessionIdCell.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/oss/src/components/pages/observability/components/SessionsTable/components/Cells/SessionIdCell.tsx b/web/oss/src/components/pages/observability/components/SessionsTable/components/Cells/SessionIdCell.tsx index 0fcd143913..be8dc956e3 100644 --- a/web/oss/src/components/pages/observability/components/SessionsTable/components/Cells/SessionIdCell.tsx +++ b/web/oss/src/components/pages/observability/components/SessionsTable/components/Cells/SessionIdCell.tsx @@ -4,7 +4,10 @@ import {Tag} from "antd" export const SessionIdCell = ({sessionId}: {sessionId: string}) => { return ( - + # {sessionId} From 1fe344b8e4e2297494e78c4ab27646036eaee383 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 18 Jun 2026 21:35:24 +0600 Subject: [PATCH 11/14] non --- web/oss/src/state/newObservability/atoms/queries.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/web/oss/src/state/newObservability/atoms/queries.ts b/web/oss/src/state/newObservability/atoms/queries.ts index 29b74ee98c..c63e3c1d85 100644 --- a/web/oss/src/state/newObservability/atoms/queries.ts +++ b/web/oss/src/state/newObservability/atoms/queries.ts @@ -509,15 +509,18 @@ export const sessionTraceCountAtomFamily = atomFamily((sessionId: string) => }), ) -// Sorted traces are required for time-based metrics (Start/End/Duration) -// We memoize this to avoid re-sorting for every time-related cell +// Sorted traces are required for time-based metrics (Start/End/Duration) and for +// the First input / Last output cells. Sort by `start_time` (falling back to +// `created_at`) to match the Session drawer's tree ordering +// (SessionTree sorts its "Trace N" nodes by start_time). Sorting by a different +// key here made the table's first/last trace diverge from the drawer's. const sessionSortedTracesAtomFamily = atomFamily((sessionId: string) => atom((get) => { const traces = get(sessionTracesAtomFamily(sessionId)) if (!traces.length) return [] - return [...traces].sort( - (a, b) => new Date(a.created_at || 0).getTime() - new Date(b.created_at || 0).getTime(), - ) + const sortKey = (t: (typeof traces)[number]) => + new Date(t.start_time || t.created_at || 0).getTime() + return [...traces].sort((a, b) => sortKey(a) - sortKey(b)) }), ) From 44dd417b18f868e15829f6de3bb43464ea1256b8 Mon Sep 17 00:00:00 2001 From: KoushikReddy Date: Thu, 18 Jun 2026 13:31:22 -0700 Subject: [PATCH 12/14] [4645] fix(frontend): wrap API key inside the create-key modal The API-key creation success popup rendered the full key in a flex row without any break rule. A real key is an 8-char prefix plus a 64-char hex secret (one '.' separator), so the unbreakable hex tail forced the flex item past its container and the key overflowed the modal edge. Let the key wrap within the available width and keep the copy icon pinned beside it: - key span: min-w-0 + break-all so it can shrink and break mid-token - icon span: shrink-0 so it is never squeezed out - row: items-start so the icon aligns to the first line when wrapping Fixes #4645 --- web/oss/src/components/pages/settings/APIKeys/APIKeys.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/oss/src/components/pages/settings/APIKeys/APIKeys.tsx b/web/oss/src/components/pages/settings/APIKeys/APIKeys.tsx index 37425bc426..0684f398f9 100644 --- a/web/oss/src/components/pages/settings/APIKeys/APIKeys.tsx +++ b/web/oss/src/components/pages/settings/APIKeys/APIKeys.tsx @@ -100,8 +100,9 @@ const APIKeys: React.FC = () => { Make sure to copy your API Key now. You won’t be able to see it again!
-
+
{ > {data} - + copyToClipboard(data)} From d8405d54d7782aef0bb747995b984d5db71d5498 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 19 Jun 2026 10:57:22 +0200 Subject: [PATCH 13/14] test(api): align default-workspace test with oldest-membership behavior get_default_workspace_id no longer prefers owner-role (multi-org: an invitee owns their own empty personal workspace). Update the stale test to assert the oldest membership wins regardless of role. Co-Authored-By: Claude Opus 4.8 (1M context) --- api/oss/tests/pytest/unit/services/test_db_manager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/api/oss/tests/pytest/unit/services/test_db_manager.py b/api/oss/tests/pytest/unit/services/test_db_manager.py index 0e380438ef..8b93b80261 100644 --- a/api/oss/tests/pytest/unit/services/test_db_manager.py +++ b/api/oss/tests/pytest/unit/services/test_db_manager.py @@ -55,7 +55,9 @@ def _patch_core_session(monkeypatch, memberships): @pytest.mark.asyncio -async def test_get_default_workspace_id_prefers_owner_membership(monkeypatch): +async def test_get_default_workspace_id_ignores_owner_role(monkeypatch): + # Owner-role is NOT preferred: under multi-org an invitee owns their own + # empty personal workspace, so the oldest membership wins regardless of role. owner_workspace_id = uuid4() editor_workspace_id = uuid4() @@ -77,7 +79,7 @@ async def test_get_default_workspace_id_prefers_owner_membership(monkeypatch): workspace_id = await db_manager.get_default_workspace_id(str(uuid4())) - assert workspace_id == str(owner_workspace_id) + assert workspace_id == str(editor_workspace_id) @pytest.mark.asyncio From de228efb8b005d3a012e476f03afa60178756f31 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Mon, 22 Jun 2026 14:06:24 +0200 Subject: [PATCH 14/14] Revert "[fix] Select Railway services when redeploying previews" --- hosting/railway/oss/scripts/deploy-from-images.sh | 2 +- hosting/railway/oss/scripts/deploy-services.sh | 2 +- hosting/railway/oss/scripts/smoke.sh | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hosting/railway/oss/scripts/deploy-from-images.sh b/hosting/railway/oss/scripts/deploy-from-images.sh index 80cdd1baf9..400869f4c2 100755 --- a/hosting/railway/oss/scripts/deploy-from-images.sh +++ b/hosting/railway/oss/scripts/deploy-from-images.sh @@ -45,7 +45,7 @@ redeploy_service_if_exists() { local service="$1" if railway_call service "$service" >/dev/null 2>&1; then - railway_call redeploy --service "$service" --environment "$ENV_NAME" --yes >/dev/null + railway_call redeploy --yes >/dev/null fi } diff --git a/hosting/railway/oss/scripts/deploy-services.sh b/hosting/railway/oss/scripts/deploy-services.sh index e9e5c4fe85..72fcf5e135 100755 --- a/hosting/railway/oss/scripts/deploy-services.sh +++ b/hosting/railway/oss/scripts/deploy-services.sh @@ -17,7 +17,7 @@ fi railway link --project "$PROJECT_NAME" --environment "$ENV_NAME" --json >/dev/null # Bring stateful infra up first so credentials/volumes are applied. -railway service "$POSTGRES_SERVICE" >/dev/null && railway redeploy --service "$POSTGRES_SERVICE" --environment "$ENV_NAME" --yes +railway service "$POSTGRES_SERVICE" >/dev/null && railway redeploy --yes if railway service "$REDIS_SERVICE" >/dev/null 2>&1; then railway up hosting/railway/oss/redis --path-as-root --service "$REDIS_SERVICE" --detach fi diff --git a/hosting/railway/oss/scripts/smoke.sh b/hosting/railway/oss/scripts/smoke.sh index 1a9e23f928..27bdf681fa 100755 --- a/hosting/railway/oss/scripts/smoke.sh +++ b/hosting/railway/oss/scripts/smoke.sh @@ -92,16 +92,16 @@ repair_path() { case "$path" in "/w") - railway service web >/dev/null && railway redeploy --service web --environment "$ENV_NAME" --yes >/dev/null - railway service gateway >/dev/null && railway redeploy --service gateway --environment "$ENV_NAME" --yes >/dev/null + railway service web >/dev/null && railway redeploy --yes >/dev/null + railway service gateway >/dev/null && railway redeploy --yes >/dev/null ;; "/api/health") - railway service api >/dev/null && railway redeploy --service api --environment "$ENV_NAME" --yes >/dev/null - railway service gateway >/dev/null && railway redeploy --service gateway --environment "$ENV_NAME" --yes >/dev/null + railway service api >/dev/null && railway redeploy --yes >/dev/null + railway service gateway >/dev/null && railway redeploy --yes >/dev/null ;; "/services/health") - railway service services >/dev/null && railway redeploy --service services --environment "$ENV_NAME" --yes >/dev/null - railway service gateway >/dev/null && railway redeploy --service gateway --environment "$ENV_NAME" --yes >/dev/null + railway service services >/dev/null && railway redeploy --yes >/dev/null + railway service gateway >/dev/null && railway redeploy --yes >/dev/null ;; *) return 1