Centralizes dayjs.extend(relativeTime) so fromNow() is typed and
loaded across the agents components. relativeOrAbsolute() falls back
to '—' for null/invalid timestamps so callers don't have to
re-implement the guard.
Exercises the auth gate plumbing — present a fake token, navigate, then
swap to a fresh token and reload. Real backend partitioning is gated
on Epic A; the spec is structured so its assertions can be tightened
once the real /v2/ endpoints land.
Type a message, send; assert the user_message bubble count strictly
grows. Mock provider acks the user_message synchronously — that's
enough to verify the composer plumbing without Epic A.
Mid-stream, page.context().setOffline(true)/(false); event count never
regresses and continues climbing after reconnect. Exercises the seq
dedup branch of useSessionEventStream.
Inside a session, poll until ≥3 events have rendered in the conversation
pane (combination of message bubbles and tool-call cards). Mock provider
ticks every 400ms so this lands well under the 10s budget.
Routes load — /agents, /agents/{aid}, /agents/{aid}/sessions/{sid}
each render their primary container. Captures console errors and
asserts none on the list view.
useAuthorized requires an unexpired JWT in the `token` cookie before it
renders. We mint an unsigned 1-hour token here — jwt-decode never
verifies the signature, so any structurally valid base64 payload works.
Helper plus an AGENTS_DEV_URL constant (overridable via env).
Targets http://localhost:3000 (Next.js dev server) directly. The agents
UI lives in App Router routes which only render under `next dev`, not
the proxy's static export — separate config skips the proxy globalSetup
that the rest of the suite needs.
Renders the agent identity, system prompt, and SessionList for this
agent. Settings hand-off link points at /settings/cloud-agents/ (Epic
G's territory) per coordination note in E1.md.
Owns the SessionList sidebar, conversation snapshot fetch, run snapshot
fetch, and SSE subscription. Distributes events to Conversation and
RightPanel as props — the children are presentational.
Read-only ANSI tail of terminal_chunk events. Tiny SGR parser handles
foreground colors and bold; resets on \x1b[0m. Anything else (cursor
moves, 256-color, truecolor) is dropped. data-testid='ansi-#ff0000'
exposes the red span for Validation #7.
Combines the active Run's git.branches with live git_commit / pr_opened
events. Shows branches, PR link (live event wins over snapshot), and
commits sorted newest-first.
Middle pane unioning the initial conversation snapshot with live SSE
events. user_message and assistant_message render as MessageBubble;
tool_call as ToolCallCard; file_diff folds into FilesChangedAccordion.
Aggregates file_diff events into a 'N Files Changed' collapsible at
the bottom of the conversation pane. Cumulative across the run per
LIT-2881 spec — latest patch wins, additions/deletions sum per path.
Single sidebar entry showing session title, status pill (antd Tag, gold for
provisioning), branch and last-updated timestamp. Links to the three-pane
view at /agents/{aid}/sessions/{sid}.
Renders the AgentList with a 'New Agent' affordance. Uses useAuthorized
for the access token and the cloud-agents-client for the fetch. In mock
mode this works without a backend so the UI can be developed alongside
Epic A.
antd Table that renders cloud-agent definitions and links each row to
/agents/{agent_id}. Uses Tag for the model and dayjs.fromNow for the
last-activity column. Empty state is the antd Empty placeholder.
EventSource hook with auto-reconnect, seq-cursor resume, and dedup.
On error closes the stream and re-opens after 1s backoff, passing
since_seq=<lastSeq> so the server replays missed events. In mock mode
replays MOCK_RUN_EVENTS at 400ms cadence so the UI looks live, and
listens for window offline/online so Playwright can exercise the
reconnect path.
Proxy-routed fetches for agents, sessions, runs, conversation, and
followup. Mock-aware: short-circuits to mock-agents.ts when
NEXT_PUBLIC_USE_MOCK_AGENTS=true. Centralizes the SSE URL shape via
buildRunEventStreamUrl so the hook just opens the EventSource.
Wired via NEXT_PUBLIC_USE_MOCK_AGENTS=true. Temporary shim until Epic A
(LIT-2877) lands the real /v1/agents, /v1/sessions, conversation, and
event-stream endpoints. Shapes mirror the API spec for a one-line swap.
Includes a canned MOCK_RUN_EVENTS sequence used by the SSE hook.
Mirrors the API spec from LIT-2877 (Epic A). Single source of truth for
the dashboard so components don't redefine shapes inline. Namespaced
`Cloud*` to avoid colliding with the legacy proxy-side Agent type.
* fix(auth): pass team_id in member-level model access check
_check_team_member_model_access calls _can_object_call_model without
team_id, so access groups defined via model_info.access_groups cannot
resolve for team-scoped DB models (their internal router name is
model_name_<team>_<uuid>, not the public name). The team-level check
already passes team_id; this mirrors that.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test(auth): add tests for member-level access group resolution with team_id
Eight tests covering _can_object_call_model and
_check_team_member_model_access with team-scoped DB models:
- access group resolves when team_id is passed
- access group fails without team_id (pre-fix behavior)
- literal model name still works with team_id (no regression)
- denied model still denied with team_id
- second model in group also reachable
- end-to-end member access via access group (mocked membership)
- end-to-end member denied for model not in allowed list
- no-override member inherits team-level check
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* proxy: hot-reload config YAML when --reload is set
Uvicorn's --reload only watches *.py by default, so editing the
--config YAML did not restart the proxy. _get_reload_options() now
extends reload_dirs/reload_includes with the config file's directory
and basename when --config is provided.
* proxy: qualify reload_includes with absolute config path
Address Greptile review on PR #27274. When the --config file lives
outside cwd, reload_includes previously stored only the basename, which
meant uvicorn/watchfiles would also reload on edits to any same-named
file inside cwd. Use the absolute config path as the include pattern in
that case so only the actual proxy config triggers a restart.
Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
* fix(proxy): use basename for reload_includes config pattern
Uvicorn's resolve_reload_patterns() calls pathlib.Path.glob(), which
raises NotImplementedError on absolute patterns (uvicorn discussion
2156). Passing config_abs (an absolute path) when the config file lived
outside cwd crashed startup under --reload. The config_dir is already
added to reload_dirs, so using just the basename as the include pattern
is sufficient to match the specific config file.
* fix: make it reload app when yaml changes
* style: remove unneeded comments
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
* Include model name + configured TPM/RPM in priority rate-limit 429 errors (#27215)
* Include model name + configured TPM/RPM in priority rate-limit 429 errors
The current 429 message ('Priority-based rate limit exceeded. Priority: prod,
Rate limit type: tokens, Remaining: -664145, Model saturation: 86.3%') doesn't
tell the operator which model was hit or what the configured limit is, so they
can't tell whether the priority allocation needs tuning or the model TPM is
just too small.
Add Model, Model TPM, and Model RPM to both the priority-based 429 and the
sibling Model-capacity 429 in dynamic_rate_limiter_v3._check_rate_limits.
Pure error-message change — no behavior or schema impact.
* test: assert priority 429 includes model name + configured TPM/RPM
Adds a regression test for the new fields in the priority-based 429 detail
('Model:', 'Model TPM:', 'Model RPM:'). Verified locally that the test
fails against the unpatched dynamic_rate_limiter_v3.py and passes after
the patch.
---------
Co-authored-by: shin-watcher <ext-agent-shin@berri.ai>
* Update litellm/proxy/hooks/dynamic_rate_limiter_v3.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update litellm/proxy/hooks/dynamic_rate_limiter_v3.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
---------
Co-authored-by: shin-watcher <ext-agent-shin@berri.ai>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>