Skip to content

Release/1.5.0#646

Merged
philmerrell merged 1608 commits into
mainfrom
release/1.5.0
Jul 14, 2026
Merged

Release/1.5.0#646
philmerrell merged 1608 commits into
mainfrom
release/1.5.0

Conversation

@philmerrell

Copy link
Copy Markdown
Contributor

Release 1.5.0

Model-catalog and MCP-admin expansion plus UI polish. Minor bump (1.4.0 → 1.5.0). No CDK deploy, no IAM changes, no data migration, no breaking changes — ships via backend.yml + frontend-deploy.yml.

🚀 Added

✨ Improved

🐛 Fixed

🔒 Security

🔧 CI/CD

📚 Docs


Full detail in CHANGELOG.md and RELEASE_NOTES.md.

After squash-merge into main, the required backmerge maindevelop follows.

🤖 Generated with Claude Code

colinmxs and others added 30 commits June 29, 2026 10:43
docs-site (npm):
- astro 6.3.1 -> 6.4.8: fixes reflected XSS via slot name (#241,
  GHSA-8hv8-536x-4wqp), host-header SSRF in prerendered error page
  (#242, GHSA-2pvr-wf23-7pc7), and XSS via unescaped spread attribute
  names (#243, GHSA-jrpj-wcv7-9fh9).
- esbuild -> 0.28.1 via overrides: fixes dev-server arbitrary file read
  (#240, GHSA-g7r4-m6w7-qqqr).

frontend/ai.client (npm):
- esbuild -> 0.28.1 via overrides (transitive through @angular/build
  21.2.16): fixes dev-server arbitrary file read (#149,
  GHSA-g7r4-m6w7-qqqr).

backend (pip/uv):
- pydantic-settings 2.13.1 -> 2.14.2 (transitive): fixes
  NestedSecretsSettingsSource symlink traversal / local file read
  (#234, GHSA-4xgf-cpjx-pc3j).

Verified: docs-site astro build OK; frontend prod build + 1239 unit
tests OK; backend 4003 tests OK; npm audit clean for both npm projects.
…521)

Security fixes (with regression tests):
- HIGH py/incomplete-url-substring-sanitization: external_mcp_client now
  parses the URL host (urlparse) and matches an anchored suffix instead of
  substring-checking the whole URL, so a marker in a path/query/userinfo
  can't trick SigV4 signing into attaching IAM creds to a non-AWS host.
  Added TestAwsUrlHostSanitization (adversarial URLs).
- HIGH js/regex/missing-regexp-anchor: admin-tool.model parses the host
  (new URL) and anchors the AWS-endpoint regexes ($). Added
  admin-tool.model.spec.ts (13 cases incl. spoofed hosts).
- MEDIUM py/log-injection (24 sites/16 files): new
  apis.shared.security.scrub_log() neutralizes CR/LF/control chars; wrapped
  user-controlled values at every flagged log call. Added test_log_sanitize.py.
- MEDIUM actions/untrusted-checkout: added persist-credentials:false to all
  inputs.ref checkouts in nightly-deploy-pipeline.yml.
- WARNING py/regex/duplicate-in-character-class: removed a bare '[' from a
  re.VERBOSE comment that the regex parser misread as a character class.

NOTE cleanups: removed unused imports (py + 17 TS constructs), a dead local,
redundant '...' after abstractmethod docstrings, unused test imports; added
explanatory comments to 6 intentional empty-except blocks; reworded
commented-out code.

Left intentionally: 4 py/unused-global-variable alerts are FALSE POSITIVES
(_cached_signing_key/_cached_bucket/_client_secret_cache are working lazy
caches — verified read+written); recommend dismissing in the UI.

Verified: backend 4045 passed (3 pre-existing test_backup_coverage failures
are unrelated — a skill-resources backup-coverage gap on develop); frontend
build OK + 1252 unit tests; infra tsc + 406 jest tests; npm/ruff clean for
touched files.
#517 (headline) — assistants can use tools again, reverting the 1.0.0 KB-only restriction (#382). Filed under ✨ Improved, with a deployment-note callout since
  it's a user-visible behavior change.
  - #521 — CodeQL sweep (2 HIGH URL/host findings, 24-site log-injection pass, hardened CI checkout) → 🔒 Security.
  - #520 — 6 Dependabot CVEs (Astro, esbuild, pydantic-settings) → 📦 Dependencies table.
  - #518 — nightly pipeline script-path fix → 🐛 Fixed.
#524)

* chore: prune dead workflow tracks, dedupe test gates, fix/fork-gate docs deploy

- nightly: remove never-useful AI coverage analysis + merge-validation
  tracks (relevant only to the old multi-stack era); delete orphaned
  ai-coverage-analysis.py
- remove dead source-project-prefix input from nightly-deploy-pipeline
  and the now-orphaned promote-ecr-image.sh it fed
- extract duplicated test gates into a reusable tests.yml; wire into
  ci, platform, backend, frontend-deploy, and nightly-deploy-pipeline
- docs-deploy: publish from main (was develop); fork-gate so forks don't
  auto-publish
- release: fork-gate so forks syncing main don't auto-create releases

Verified: repo-shape jest 49/49, supply_chain pytest 31/31.

* ci: re-enable push-triggered deploys, path-scoped

Restore auto-deploy on push to develop/main for the platform, backend, and
frontend workflows (was workflow_dispatch-only since v1.0.0). Each push
trigger is scoped to that workflow's own surface so unrelated changes don't
redeploy:
- backend:  backend/**, scripts/build/**, the workflow file
- platform: infrastructure/lib (stack/constructs/config), bin, bootstrap-assets, scripts/platform/**, the workflow file
- frontend: frontend/**, scripts/frontend/**, the workflow file

develop -> development env, main -> production env (existing per-job mapping).
Forks without AWS secrets fail safely at the credential step; forks that
configured their own secrets get the intended fork-and-deploy behavior, and
manual workflow_dispatch remains available everywhere. docs-deploy was
already scoped (docs-site/** + its workflow file).

* docs: trim verbose/misleading workflow comments

Cut the trigger and fork-gate comments down to one-liners and drop the
inaccurate 'secrets make it fork-safe' framing (forks must explicitly
enable Actions regardless).
…#525)

Put platform.yml and backend.yml in one repo-global concurrency group
(deploy-<ref>) so a CloudFormation deploy and the API-driven backend code
deploys can't run at the same time and stomp on the same ECS service /
AgentCore Runtime / Lambda. They queue instead; order doesn't matter
post-initial-deploy since either order converges. Frontend stays
independent. cancel-in-progress stays false.
* fix(deps,codeql): bump joserfc to 1.7.2; drop unused imports

- joserfc 1.6.3->1.7.2 (backend/uv.lock), 1.6.5->1.7.2 (scripts/backup-data/uv.lock)
  remediates Dependabot GHSA-wphv-vfrh-23q5 / CVE-2026-48990 (#244, #245).
  Targeted 'uv lock --upgrade-package joserfc'; no other packages changed.
- Remove unused 'Optional' import in agents/main_agent/agent_types.py (CodeQL #721).
- Remove unused 'ssm' import in app-api/app-api-environment.ts (CodeQL #722).

* fix(ci): render reusable test-gate job names statically

The reusable tests.yml jobs used an inline label expression in their
'name:'. GitHub does not evaluate name expressions for SKIPPED jobs, so
callers that run only one suite (backend.yml, platform.yml,
frontend-deploy.yml, nightly-deploy-pipeline.yml) showed the raw
${{ ... }} text as job labels for the unselected (skipped) jobs.

Make the three job names static and relocate the nightly track-label
prefix onto the (never-skipped) caller jobs in nightly-deploy-pipeline.yml,
which matches the existing '[label] Deploy PlatformStack' convention and
evaluates reliably. Drops the now-unused 'label' input from tests.yml.
Patch release: CI/CD pipeline cleanup, re-enabled path-scoped auto-deploys,
serialized platform+backend deploys, and a joserfc CVE / CodeQL dependency sweep.
No application code or user-facing behavior changes.

- Bump VERSION to 1.0.3; sync manifests + lockfiles
- Brief RELEASE_NOTES.md and CHANGELOG.md entries (#524, #525, #526)
- Steering: scale release-notes depth to release size (brief patches, deep features)
AWS docs require an identity-based policy on the gateway service role
in addition to the per-target resource policy (lambda:AddPermission).
Without this the Gateway gets 403 when trying to invoke Lambda targets.
AgentCore Memory strategy discovery (MemoryClient.get_memory_strategies)
calls bedrock-agentcore:GetMemory, but neither the App API task role nor the
AgentCore Runtime execution role allowed it — every other memory data-plane
action was granted. The denied GetMemory made strategy discovery fail
silently:

- App API: GET /memory returned empty facts/preferences with 200, so the
  Settings memories/preferences page rendered blank despite stored records.
- Runtime: _discover_strategy_ids() failed, leaving retrieval config empty, so
  the agent kept writing events (CreateEvent) but never recalled long-term
  memories.

Add bedrock-agentcore:GetMemory to the AgentCoreMemoryAccess statement on both
roles (app-api scoped to the memory ARN; runtime scoped to memory/*). No other
actions changed. Validated against the AWS Service Authorization Reference
(GetMemory = Read on the memory resource type) and the live AccessDenied in
app-api logs.

Bumps VERSION to 1.0.4 + brief RELEASE_NOTES/CHANGELOG entries. Infra (IAM)
change — deploys via the platform (CDK) pipeline.
…-memory-getmemory-permission

fix(iam): grant bedrock-agentcore:GetMemory to app-api + runtime roles
An interrupt-resume turn (OAuth consent or tool approval) has no new user
message, and the resumed Strands stream does not replay the interrupted
tool_use block — it emits only the tool_result plus a fresh assistant
message with the final text. The default sync truncates everything after
the last user message and replaces it with the resumed stream, discarding
the assistant message that holds the paused tool card. The reset parser
also drops the incoming tool_result (no matching tool_use in its fresh
builder). Net: the tool card disappears even though the call succeeded.

Both resume paths now:
- use beginContinuationStreaming() instead of startStreaming(), pinning the
  existing messages (including the tool card) as a prefix and appending the
  resume after them — same pattern as the max_tokens "Continue" flow.
- reconcile from persisted memory after the resume stream closes via the new
  MessageMapService.reloadMessagesForSession(), so the card flips from
  "Running..." to its completed result (memory holds tool_use + tool_result).

reloadMessagesForSession force re-fetches (bypasses the "already loaded"
guard) without flipping the skeleton loading state; the shared fetch/reconcile
logic is extracted into fetchAndApplyMessages(). Removed the now-unused
StreamParserService injection from ChatRequestService.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rrupt-resume-tool-card

fix: keep tool card after OAuth/tool-approval resume
Generated by the kaizen-research skill. Top 5 ideas appended to
docs/kaizen/review-queue.md for the kaizen-review-prep run later this morning.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Catch-up review consuming this morning's fresh research/2026-07-03.md
(no 06-12/06-19 reviews ran). 10 proposals ranked; top item is the
bedrock-agentcore 1.17.0 bump (closes the #482 SSE deadlock we're
exposed to today + retires a queued guard). Queue trimmed: superseded
Strands/agentcore bump duplicates consolidated into the two 07-03
bumps, Fable 5 un-withdrawn (reinstated Jul 1), starlette + PR-gate
marked shipped. Stacked on the research PR (#534).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The chat streaming layer used global singletons that broke when two
conversations streamed concurrently (ask in A, navigate to B, ask again).
Make streaming state per-session end to end, and fix the UX gaps that
surfaced once background streaming actually worked.

Per-session streaming:
- ChatStateService holds per-session state (loading, stop reason,
  cost/context aggregates, Continue affordance, AbortController) behind a
  viewed-session facade, so existing consumers are unchanged.
- StreamParserService keeps a Map<sessionId, ParserSessionState> with a
  per-stream id; ChatHttpService captures it per request and drops late
  events from a superseded stream (same-session resubmit guard).
- MessageMapService runs one sync effect per active stream; endStreaming,
  abort, and loading are all keyed by session. Stop and double-submit only
  affect their own conversation; navigating away no longer aborts the
  in-flight stream (backend still completes and persists the turn).

Navigation scroll policy:
- New ScrollPositionService remembers per-conversation scroll offset for the
  SPA session. Returning restores where you were; first open (or after a
  reload) anchors the latest turn instead of the top.

Streaming-text replay fix:
- StreamingTextComponent seeds its display from whatever text already exists
  at mount, so navigating back to a streaming conversation shows the current
  state instead of re-typing the whole response from character zero.

Sidebar in-progress indicator:
- Conversation rows show a pulsing dot while a response streams (reads the
  same per-session loading state), replacing the hover ellipsis menu while
  in progress so the two never overlap and actions can't target a
  mid-response conversation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nt-chat-streaming

feat: isolate chat streaming per conversation
Include only user and assistant messages by default in conversation exports. Tool calls, images, and citations must now be explicitly selected by the user.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…efaults-messages-only

feat: default export to messages only
Add a dropdown to the top-nav session title (arrow/chevron trigger) that
offers the same actions as the sidenav ellipsis menu — Rename, Share,
Save to…, and Delete — reusing SessionService and the existing dialogs.

Also fixes title-transition polish surfaced while building this:

- Consolidate the two <app-topnav> instances (skeleton vs loaded branch)
  in chat-container into a single persistent instance so the fixed
  .chat-topnav-wrapper no longer remounts on skeleton→loaded, which was
  restarting its left-transition and sweeping the whole bar ~288px.
- Optimistically set currentSession on sidenav click so the title updates
  instantly instead of lingering until metadata loads.
- Optimistic inline rename (revert on failure) to remove the save lag, and
  reload the sessions resource so the sidenav list reflects the new title.
- Gentle keyed slide+fade title entry animation (component-scoped).
- Title skeleton while the session metadata is loading (hard refresh) or a
  brand-new chat's title is still generating.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
onSessionClick now takes the clicked session and optimistically sets it as
currentSession; update the spec to pass a session and assert the set.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ession-title-menu

feat: expose session options menu on top-nav title
The OAuth "Authorization needed" banner is held in a root-singleton
OAuthConsentService keyed by providerId (not sessionId), and its
pending() signal drives the banner regardless of the originating
session. Every other per-session UI surface (compaction, artifacts,
MCP app frames/cards/consent) is reset in the route subscription on
conversation change, but OAuthConsentService was not — so a prompt
raised in one session persisted onto the next, including the blank
welcome screen.

Clear it fail-closed alongside the other resets. This runs before
loadMessagesForSession, which re-seeds the new session's own pending
interrupts from persisted server metadata, so a session that
legitimately needs authorization still shows its banner. Clearing
seenInterruptIds also fixes a latent bug where a genuinely-needed
re-prompt for the same provider in a later session was silently
suppressed by the dedup guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t-banner-session-leak

fix: clear OAuth consent state on session switch
New sessions were only renamed from "New Conversation" after the agent
stream closed, even though the backend already generates the title
concurrently with the stream. This surfaces that title while the response
is still pending.

- inference-api keeps the concurrent title-generation task handle and
  interleaves a one-shot `session_title` SSE event between agent events
  once it resolves (non-blocking done-check, same drain pattern as the
  MCP Apps broker). Never emits the "New Conversation" placeholder.
- Fix latent bug: generate_conversation_title ran sync boto3 converse on
  the event loop, stalling the live agent stream for the whole Nova Micro
  round-trip. Now runs via asyncio.to_thread.
- SPA parses session_title, applying it to both the sidebar cache and the
  currentSession signal (new SessionService.applyServerTitle) so the
  top-nav header renames too. Allowlisted past Completed-state gating
  since it can arrive just after `done`.
- Post-close refreshTitleFromServer remains the fallback for streams that
  outrun generation.

Documented the new event in the CLAUDE.md SSE contract table. Adds
backend + frontend test coverage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…op-nav

Surfaces the "title generating" state cleanly now that titles arrive
mid-stream via the session_title SSE event.

Sidebar (session-list):
- Show a shimmer skeleton instead of "Untitled Session" while a new
  conversation's title is still generating (titleless + streaming).
- Darken the shimmer only on the selected row, whose active highlight
  (bg-gray-200 / dark:bg-white/5) would otherwise hide a light skeleton.
  Keyed off a plain .session-row--active sentinel added to the same
  routerLinkActive that paints the highlight, coloured in component CSS
  (no arbitrary Tailwind variant), with class-based dark handled via
  :host-context(html.dark).
- Row is now flex/min-h-8 so the skeleton→title swap causes no vertical
  jank, and the generated title reveals with the top-nav's slide+fade
  (session-title-enter), kept truncatable via min-w-0.

Top-nav:
- Gate the title skeleton on a proper titlePending signal (metadata
  loading OR the session actively streaming) so it can no longer shimmer
  forever; once resolved with no title it falls back to "Untitled
  Session" instead of an eternal skeleton.

Adds/updates topnav + session-list specs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-session-title

feat: name sessions in parallel with the pending agent response
philmerrell and others added 27 commits July 11, 2026 12:03
The version-sync script only ran inside the dev container / CI (GNU coreutils);
on macOS (BSD sed/grep) it errored out and silently left the manifests
un-synced, so a release cut locally had to hand-edit every manifest.

Replace the three GNU-only constructs with POSIX equivalents:
- `grep -oP ... \K` (Perl regex) -> `sed -n 's/.../\1/p'` / awk field split
- `sed -i "expr"` (GNU in-place) -> sed_inplace helper (temp file + mv)
- `sed "0,/re/s/..."` (GNU-only address) -> awk first-match replace

Behavior is unchanged on GNU; the script now runs identically on macOS.
Verified both --check and the write path (incl. shields.io `--` hyphen
doubling and SemVer->PEP 440 lock conversion) round-trip on BSD tools.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-sync-version

fix(scripts): portable sync-version.sh (GNU + BSD)
Pin the admin layout aside below the sticky top bar so the section nav
stays in view while the content area scrolls. Uses lg:self-start so the
aside shrinks to its content (flex items stretch to full height by
default, which defeats position:sticky), plus a max-height + overflow so
a long nav scrolls internally. Mobile dropdown is untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rework the not-found page onto the same design system as the login and
first-boot pages: the primary-derived lava-lamp parallax backdrop (six
depth-tiered morphing blobs), the masked graph-paper grid overlay, and
the frosted-glass card. The oversized 404 sits above the card where the
auth pages place the logo, so all three screens read as one system.

Preserves existing behavior (sidenav hide/show, Return Home, Go Back)
and respects prefers-reduced-motion. Classes are nf-prefixed and
component-scoped via view encapsulation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ide-menu-admin-a1cc71

feat(admin): make admin sidebar nav sticky on desktop
…redesign-6f0086

feat(frontend): redesign 404 page to match auth screens
The admin aside's lg:sticky never engaged because its nearest scrolling
ancestor was the app shell's `flex-1 overflow-y-auto` div, which had no
bounded height — it grew to content and the window scrolled instead, so
sticky bound to a box that never moved. Pin <main> to h-dvh so that div
becomes a genuine scroll container; the admin aside and top bar now stick.

Also apply the admin bar's frosted-glass treatment (bg-*/opacity +
backdrop-blur-sm) to the session topnav so the two surfaces match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nav-sticky-scroll

fix(frontend): engage sticky admin nav + frosted session topnav
…ttee one-pager

Replaces the hard monthly quota cutoff with a three-layer model:
anchored 5-hour cooldown windows (Claude-style, exact reset times),
a hard admin-adjustable platform-wide monthly ceiling as the fiscal
guarantee, and the per-user monthly limit demoted to a generous
anti-runaway backstop with degrade-to-economy-model as the target
behavior. Backstop horizon (monthly vs weekly) is a per-tier choice.
Includes an admin pilot tuning playbook with an observe-only phase,
a user-facing quota status endpoint, recommended opening numbers,
and a 7-PR implementation breakdown. The one-pager is the
committee-facing rationale.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ldown-strategy-40f6a0

docs(specs): quota cooldown windows + platform ceiling (spec + committee one-pager)
…Location flake

The unit-test builder keeps Angular packages external, so vitest evaluates
raw fesm2022 chunks whose partial declarations (ɵɵngDeclareInjectable/
ɵɵngDeclareFactory) compile eagerly and require @angular/compiler. Its
presence was incidental — loaded transitively via @angular/core/testing in
the builder's init-testbed setup — so specs with no static Angular imports
(app.spec.ts dynamic-imports './app') could evaluate an unlinked
@angular/common chunk first and fail with "The injectable
'PlatformLocation' needs to be compiled using the JIT compiler, but
'@angular/compiler' is not available" (angular/angular-cli#31993).

- add src/test-setup.ts importing @angular/compiler, wired via the test
  target's setupFiles and included in tsconfig.spec.json
- add src/test-setup.spec.ts guarding the invariant deterministically
- bump the first shared-view.page spec to 15s: it pays the one-time
  dynamic page-chunk import, which can exceed 5s under full-suite load

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… scroll container

Replace the fixed viewport-tall bottom spacer in the message list with a
min-height on the last turn group (user message + its assistant
responses). The response streams into the reserved space instead of
pushing a static spacer further down: a short response leaves exactly
the room needed to pin the user message at the top, and a response
taller than the viewport leaves zero dead scroll below it. Turn groups
are keyed by their first message id so a finished turn's DOM (including
live MCP App iframes) never remounts when the next turn starts, and the
end-of-conversation sections (loader, consent/approval prompts,
compaction, orphan artifacts) render inside the reserved space so they
stay visible next to the response.

Also adapt the session page to the real shell scroll container
introduced by #634 (frosted sticky nav): the window no longer scrolls,
which had silently broken submit scroll-to-message and scroll
save/restore. scrollToMessage now uses scrollIntoView with a
scroll-mt-20 header offset, and save/restore reads the shell
container's scrollTop via a stable #app-scroll-container hook.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…last-turn-spacer

fix(frontend): size chat scroll space to the response, adapt scrolling to shell scroll container
…ompiler-flake

fix(frontend): guarantee JIT compiler in vitest runs to stop PlatformLocation flake
Mirror the admin layout change (#632): pin the settings aside below the
sticky top bar so the section nav stays in view while the content area
scrolls. Uses lg:self-start so the aside shrinks to its content (grid
items stretch to full row height by default, which defeats
position:sticky), plus a max-height + overflow so a long nav scrolls
internally. Mobile dropdown is untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ide-user-settings

feat(settings): make user settings sidebar nav sticky on desktop
…vaulted token

The admin tool "Discover" flow refused OAuth-gated MCP servers outright, so
servers like the GitHub remote MCP server (api.githubcopilot.com/mcp/) could
not be discovered — discovery either 400'd on auth_type=oauth2 or connected
unauthenticated and got a 401 from the server (wrapped to a 400).

Discovery now accepts the OAuth provider id and connects using the admin's own
vaulted 3LO token for that provider, fetched via AgentCore Identity
(get_token_for_user) and injected as a bearer — mirroring how the agent loop
attaches the end-user's provider token at runtime, and reusing the exact path
connector_status already uses. This validates the admin's own connection and
lists the tools their token can see (providers such as GitHub scope-filter the
tool list to the token's grants). It fetches the admin's token only; it cannot
mint an arbitrary end-user's token.

Backend:
- Add requires_oauth_provider (alias requiresOauthProvider) to MCPDiscoverRequest.
- Handler loads the provider, fetches the admin's vaulted token, injects it as
  oauth_token into create_external_mcp_client. requires_consent -> 409, unknown
  provider / conflict with forward_auth / oauth2-without-provider -> 400.

Frontend:
- Send requiresOauthProvider in the discover payload (the form control already
  existed) and the OAuth2CallbackUrl header (bare /oauth-complete, no query
  string) so the backend can resolve the admin's token.

Tests: 5 backend tests for the OAuth-provider discovery path; 2 SPA specs for
the discover payload.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-oauth-mcp-admin-token

feat(admin-tools): discover OAuth-gated MCP servers with the admin's vaulted token
…apability

Add two curated model catalog cards:
- Claude Sonnet 5 (bedrock, global.anthropic.claude-sonnet-5) — 1M context,
  effort-based reasoning, caching on.
- GPT-5.4 (mantle, openai.gpt-5.4) — Responses API surface; the openai.gpt-5.*
  model id matches the SDK's /openai/v1 routing prefixes, so one-click create
  routes correctly (unlike the commented-out Gemma card).

Order the Bedrock Claude cards most-capable-first (Opus 4.7, Sonnet 5,
Sonnet 4.6, Haiku 4.5) and place GPT-5.4 ahead of Qwen in the Mantle list.
Move the "Bedrock Mantle" provider tab next to "Bedrock" in the catalog
selector.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…models-sonnet5-gpt54

feat(manage-models): add Sonnet 5 + GPT-5.4 curated cards, order by capability
Gemma 4 is served ONLY on Mantle's /openai/v1 path (per its AWS model
card), but the Strands SDK's _OPENAI_PATH_MODEL_PREFIXES ships only
"openai.gpt-5.", so google.gemma-4-* fell through to /v1 and inference
401'd with access_denied ("... is not enabled for this account").

Append "google.gemma-4-" to the SDK's prefix table at build time
(_ensure_gemma4_openai_v1_routing: lazy, idempotent, guarded) until it
lands upstream. Scoped to the 4.x family — Gemma 3 stays on /v1.

- Guard tests: prefix registers on build, all three Gemma 4 variants
  resolve to /openai/v1, Gemma 3 stays on /v1, registration idempotent.
- Correct the stale curated-models.ts note (the "would fail at chat
  time" claim is obsolete; its "google.gemma-" re-add hint would have
  misrouted Gemma 3).
- Add design note proposing mantleEndpointPath as a live admin setting
  as the durable alternative to chasing the SDK's hardcoded table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…models-sonnet5-gpt54

fix(mantle): route google.gemma-4-* to /openai/v1 (+ curated Sonnet 5 / GPT-5.4 cards)
Newer reasoning / Responses-API models (GPT-5.x, Claude with adaptive
thinking) don't publish a discrete max-output-tokens value — output shares
the context budget with reasoning tokens, so there's no fixed cap to enter.
Our own GPT-5.4 curated card already carries a decorative value with no
backing max_tokens spec.

maxOutputTokens is only a ceiling for the admin-configured max_tokens
inference param and is never sent to the provider, so leaving it unset is
safe at inference time. This makes the admin form field optional to match.

- ManagedModelCreate / ManagedModel: max_output_tokens -> Optional[int]
- DynamoDB write: omit maxOutputTokens when absent (matches other optionals)
- Form control: drop Validators.required, default null (number | null); the
  0-default + min(1) combo would otherwise still block submit
- SPA interfaces typed number | null; catalog card null-guarded (shows "— out")
- Both ceiling validators already skipped an absent value — no change needed

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-max-output-tokens

feat(manage-models): make max output tokens optional
Debian removes the superseded point version of curl from the trixie
mirror on each security update, so an exact +deb13uN pin breaks every
build once the next CVE lands. Pin to +deb13u* to track the live patch
while keeping the minor version fixed; the digest-pinned base image is
what actually provides reproducibility.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-max-output-tokens

feat(manage-models): optional max output tokens + fix(docker): float curl security patch
Model-catalog and MCP-admin expansion plus UI polish (1.4.0 → 1.5.0, minor).

- Discover OAuth-gated MCP servers with the admin's vaulted 3LO token (#639)
- Curated Sonnet 5 + GPT-5.4 model cards, capability ordering (#641)
- Optional max output tokens for reasoning/Responses-API models (#643, #644)
- Sticky admin/settings sidebars, redesigned 404, chat-scroll/sticky-nav fixes
  (#632, #633, #634, #637, #638)
- Mantle google.gemma-4-* → /openai/v1 routing fix (#641)
- Vitest JIT/PlatformLocation flake fix (#636)
- Docker curl security patch floats with Debian mirror (#645)
- Portable sync-version.sh across GNU/BSD (#631)

No CDK deploy, no data migration, no breaking changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@philmerrell
philmerrell requested a review from a team July 14, 2026 01:48
@philmerrell
philmerrell merged commit bc1744f into main Jul 14, 2026
11 checks passed
@philmerrell
philmerrell deleted the release/1.5.0 branch July 14, 2026 01:54
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.

3 participants