Commit graph

39036 commits

Author SHA1 Message Date
Ishaan Jaffer
92bc874efc
merge: integrate litellm_lit-2877-proxy-api-surface into cursor-sdk-integration 2026-05-06 16:12:56 -07:00
Ishaan Jaffer
ecf62c5fa8
test(cascade_delete): regression test for status-based cascade filter
Greptile P3 (regression coverage): the cascade filter in
delete_agent was changed from terminated_at is None to
status not in SESSION_TERMINAL_STATUSES in commit a0015e8564.
Add an explicit test that exercises the bug surface — a session
flipped to error (terminal) but with terminated_at deliberately
left None. The legacy filter would have re-terminated it; the
status-based filter must skip it.
2026-05-06 16:10:00 -07:00
Ishaan Jaffer
a1aaf55e45
test(agent_session_endpoints): regression tests for narrowed seq-collision catch
Asserts:
  * _is_seq_collision matches RuntimeError('event_seq_collision') and
    prisma.errors.UniqueViolationError, but rejects unrelated errors.
  * Real collision: insert at seq=N when seq=N already exists, retry
    at N+1 succeeds (no 409 surfaced).
  * Unrelated DB outage: a non-collision RuntimeError propagates as
    itself, NOT misclassified as 409 event_seq_collision.

Greptile follow-up regression coverage.
2026-05-06 16:01:40 -07:00
Ishaan Jaffer
e489150883
test(cleanup_sweeper): assert session flips to ready after stuck-run sweep
Greptile P1 regression coverage. The sweeper test only checked
run.status == RUN_STATUS_ERROR, leaving the session-status gap
undetected. Add a force-busy step before the sweep + assert the
session transitions to ready after.
2026-05-06 16:01:32 -07:00
Ishaan Jaffer
124c73c1ed
fix(internal_endpoints): only treat seq-collisions as 409 in events:append retry
The seq-collision retry in daemon_append_event previously caught all
Exception types and re-raised them as 409 event_seq_collision. A
transient DB error during retry would surface to the daemon as a
misleading 409, hiding the real outage and the daemon would respond
incorrectly (they treat 409 as 'data conflict, drop the event' rather
than 'retry').

Add a narrow _is_seq_collision predicate that matches:
  * prisma.errors.UniqueViolationError (production)
  * RuntimeError('event_seq_collision') marker (test stand-in)

Other errors bubble up unchanged so callers can distinguish a real
seq conflict from a real outage.

Greptile (review #PRR_kwDOKALCgc78u_NS — overly broad exception
catch in seq-collision retry).
2026-05-06 16:01:25 -07:00
Ishaan Jaffer
164afd4a18
fix(cleanup): drive session busy->ready after sweeper reaps stuck run
_sweep_stuck_runs marks idle-timeout runs as error but never called
refresh_session_status_from_runs, leaving the parent session
permanently busy. Every other run-terminal path (cancel_run,
daemon_append_event, /followup) calls the helper to flip
busy -> ready; the sweeper was the only path that skipped it.

After flipping each run to error, call refresh_session_status_from_runs
inside the loop so a session whose only active run was reaped here
transitions back to ready.

Greptile P1 (review #PRR_kwDOKALCgc78u_NS, inline comment line 172).
2026-05-06 16:01:16 -07:00
Ishaan Jaffer
7a23f04849
test(agent_session_endpoints): regression tests for session busy/ready oscillation
Walks the SDK-visible session.status across:
  * POST /v2/sessions/{sid}/runs        — ready -> busy
  * POST /v2/sessions/{sid}/runs/{rid}/cancel — busy -> ready
  * POST /v2/sessions/{sid}/followup    — ready -> busy via /followup
  * Cancellation via /followup-created run — busy -> ready

Plus two helper tests:
  * idempotent (no-op when no transition is needed)
  * quiet on missing session (race with cascade delete)

Greptile P1 regression coverage.
2026-05-06 15:52:06 -07:00
Ishaan Jaffer
79642036e6
test(cleanup_sweeper): assert provider.terminate called for dead-daemon sessions
Greptile P1 regression coverage: dead-daemon sweep must route through
_terminate_session_internal so provider.terminate gets called. Without
this assertion the regression silently returned (NoopVMProvider would
still mark rows correctly via update_many).
2026-05-06 15:51:58 -07:00
Ishaan Jaffer
a0015e8564
fix(agent_endpoints): use status check (not terminated_at) for cascade filter
delete_agent's cascade filter previously used 'terminated_at is None'
to find non-terminal sessions. The fix is safe in practice because
_terminate_session_internal has its own SESSION_TERMINAL_STATUSES guard,
but it's inconsistent with the rest of the module which uses
'status in/notin SESSION_TERMINAL_STATUSES' everywhere else.

Switch to the status-based check to match.

Greptile P3 (review #PRR_kwDOKALCgc78u9En).
2026-05-06 15:51:52 -07:00
Ishaan Jaffer
b7e971032b
fix(cleanup): route dead-daemon sweep through _terminate_session_internal
_sweep_dead_daemons previously ran update_many directly on the session
row, skipping provider.terminate. With NoopVMProvider this was harmless,
but once Epic B swaps in a real VM provider it would orphan EC2
instances every time a daemon stopped heartbeating.

Mirror the pattern from _sweep_expired_sessions: call
_terminate_session_internal per-row so the provider is notified, then
explicitly downgrade status from 'terminated' to 'error' (both are
terminal — no further state transitions). Also drops the per-run
update loop since _terminate_session_internal already cancels active
runs and emits run_cancelled events.

Greptile P1 (review #PRR_kwDOKALCgc78u9En).
2026-05-06 15:51:45 -07:00
Ishaan Jaffer
6ec39988f4
fix(session_endpoints): drive session busy state on /followup new-run path
When /followup creates a fresh queued run (terminal-or-absent latest_run
branch), call refresh_session_status_from_runs so the session moves
'ready' -> 'busy'. Match the same hook added to POST /runs.

Greptile P1 (review #PRR_kwDOKALCgc78u9En).
2026-05-06 15:51:36 -07:00
Ishaan Jaffer
b007da3dc9
fix(run_endpoints): drive session busy<->ready oscillation on run create/cancel
Two call sites added:
  * After POST /v2/sessions/{sid}/runs creates a queued run, call
    refresh_session_status_from_runs so the parent session moves
    'ready' -> 'busy'.
  * After POST /v2/sessions/{sid}/runs/{rid}/cancel marks a run
    cancelled, call the same helper so the session moves
    'busy' -> 'ready' if no other active runs exist.

Greptile P1 (review #PRR_kwDOKALCgc78u9En).
2026-05-06 15:51:30 -07:00
Ishaan Jaffer
f3101be1a1
fix(internal_endpoints): drive session busy<->ready oscillation from daemon callbacks
Two call sites added:
  * After _claim_next_queued_run flips a queued run to running, call
    refresh_session_status_from_runs so a session that was 'ready'
    transitions to 'busy'. Idempotent for already-busy sessions.
  * After daemon_append_event finalizes a terminal event (run_finished /
    run_cancelled / run_error), call refresh_session_status_from_runs so
    a session with no remaining active runs transitions back to 'ready'.

Without these hooks, session.status stayed permanently 'busy' after the
first run started — clients polling GET /v2/sessions/{id} always saw
'busy' regardless of run state.

Greptile P1 (review #PRR_kwDOKALCgc78u9En).
2026-05-06 15:51:24 -07:00
Ishaan Jaffer
f68869071b
feat(agent_session_endpoints): add session_status helper for busy/ready oscillation
Pure-function logic for the session status state machine already lives
in state_machine.derive_session_status_from_runs. This module is the
I/O wrapper that reads the session row, counts active runs, and
persists the new status only when the helper says it should change.

Used by every code path that flips a run's status:
  * POST /v2/sessions/{sid}/runs (queued -> session busy)
  * POST /v2/sessions/{sid}/followup (new run -> same)
  * GET  /v2/sessions/{sid}/runs/next/internal/poll (queued -> running)
  * POST /v2/sessions/{sid}/runs/{rid}/cancel (terminal -> ready)
  * POST /v2/sessions/{sid}/runs/{rid}/events:append (terminal -> ready)

Greptile P1 (review #PRR_kwDOKALCgc78u9En).
2026-05-06 15:51:16 -07:00
oss-agent-shin
b318231fe9
Add Azure Sentinel audit log support (#27280)
* Add Azure Sentinel audit log callback support

Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>

* Fix Azure Sentinel audit log batching

Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>

* Fix Azure Sentinel CI checks

Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>

---------

Co-authored-by: oss-agent-shin <279349115+oss-agent-shin@users.noreply.github.com>
Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>
2026-05-06 15:50:06 -07:00
Ishaan Jaffer
c2e8539f1e
fix(agent_session_endpoints): remove unused imports flagged by ruff
Pre-existing F401 violations cleaned up so the lint job stays green:
  * agent_endpoints.py: unused List, Optional from typing
  * cleanup.py: unused SESSION_STATUS_TERMINATED
  * internal_endpoints.py: unused SESSION_TERMINAL_STATUSES
  * session_endpoints.py: unused SESSION_STATUS_READY

Tests still 58/58 green.
2026-05-06 15:41:42 -07:00
Ishaan Jaffer
19c95a53f0
test(agent_session_endpoints): regression test for /followup concurrency gap
Reproduces the original race deterministically: a session has both
an OLDER active run AND a NEWER terminal run. The buggy code path
used latest_run (newest by created_at) to decide whether to fall
through to the create-new-run branch — and since the newest is
terminal, it skipped the busy check and would have inserted a
duplicate run.

Three tests:
  * 409 run_busy when an older active run exists.
  * Happy path: empty session creates the first run.
  * Happy path: latest run is active so /followup appends a
    user_message event (unchanged by the busy guard).

Greptile P1 regression coverage (validation #16).
2026-05-06 15:38:14 -07:00
Ishaan Jaffer
fb4b74a2bf
test(agent_session_endpoints): regression tests for view-only admin write bypass
Exercise every state-mutating endpoint as PROXY_ADMIN_VIEW_ONLY and
confirm they all return 403:
  * POST   /v2/agents
  * PATCH  /v2/agents/{id}
  * DELETE /v2/agents/{id}
  * POST   /v2/sessions
  * DELETE /v2/sessions/{id}
  * POST   /v2/sessions/{id}/runs
  * POST   /v2/sessions/{id}/runs/{rid}/cancel
  * POST   /v2/sessions/{id}/followup

Plus a happy-path test confirming view-only admins still get
cross-tenant READ access (the whole point of the role).

Greptile P1 SECURITY regression coverage (validation #14).
2026-05-06 15:38:06 -07:00
Ishaan Jaffer
ac5adafaed
test(agent_session_endpoints): regression tests for required JWT secret
Asserts:
  * _get_signing_secret raises AgentJWTSecretNotConfiguredError when
    LITELLM_AGENT_JWT_SECRET is unset, even with LITELLM_MASTER_KEY set
    (no silent fallback).
  * is_agent_jwt_secret_configured returns False for unset / empty,
    True for any non-empty value.
  * mint_daemon_token and decode_daemon_token both surface the same
    error when the env var is missing.

Greptile P1 SECURITY regression coverage (validation #15).
2026-05-06 15:37:53 -07:00
Ishaan Jaffer
18c47bcf79
test(agent_session_endpoints): add view_only_admin_client fixture
Used by test_view_only_admin_no_writes.py to exercise every
mutating endpoint as a view-only admin and confirm they get 403.
2026-05-06 15:37:46 -07:00
Ishaan Jaffer
d908e9940b
fix(proxy_server): refuse to mount agent_session routers when JWT secret is unset
When LITELLM_AGENT_JWT_SECRET is not set, the daemon JWT auth layer
cannot operate safely (no master-key fallback). Refuse to mount the
four /v2/agents+/v2/sessions routers in that case and log a clear
error pointing operators at the env var. Mounting them anyway would
expose an auth surface that can never validate a token and crash on
every request.

Greptile P1 SECURITY follow-up.
2026-05-06 15:37:42 -07:00
Ishaan Jaffer
1d44980be2
fix(internal_endpoints): use asyncio.get_running_loop() not get_event_loop
asyncio.get_event_loop() is deprecated inside a running coroutine
(Python 3.10+). Replace the two call sites in
daemon_get_next_queued_run with asyncio.get_running_loop().

Greptile P2.
2026-05-06 15:37:35 -07:00
Ishaan Jaffer
1717078971
fix(run_endpoints): use asyncio.get_running_loop() and gate writes for view-only admins
Two fixes in this file:

1. asyncio.get_event_loop() is deprecated inside a running coroutine
   (Python 3.10+). Replace both call sites in _stream_run_events with
   asyncio.get_running_loop().

2. Call assert_caller_can_mutate on create_run and cancel_run so
   view-only admins get 403 instead of bypassing ownership.

Greptile P2 (deprecation) + P1 SECURITY (view-only admin write bypass).
2026-05-06 15:37:29 -07:00
Ishaan Jaffer
19ada25e59
fix(session_endpoints): close /followup concurrency gap and gate writes for view-only admins
Two fixes in this file:

1. /followup new-run branch was bypassing the run-busy concurrency
   guard. When latest_run was terminal-or-absent, the endpoint went
   straight to litellm_agentrun.create without calling
   _has_active_run. Two concurrent /followup calls on an idle session
   both passed the latest_run.status check and both inserted runs,
   breaking the 'one active run per session' invariant that POST /runs
   enforces via 409 run_busy. Add the same _has_active_run check
   before the fallthrough create, plus a defensive insert-time retry
   that re-queries for active runs after IntegrityError and surfaces
   409 run_busy if it lost the race.

2. Call assert_caller_can_mutate on create_session, delete_session,
   and followup so view-only admins get 403 instead of bypassing
   ownership.

Greptile P1 (concurrency) + P1 SECURITY (view-only admin write bypass).
2026-05-06 15:37:23 -07:00
Ishaan Jaffer
f893105116
fix(agent_endpoints): call assert_caller_can_mutate on every write endpoint
Block PROXY_ADMIN_VIEW_ONLY from create_agent / update_agent /
delete_agent. Reads (GET) still pass through is_proxy_admin_read
so view-only admins keep cross-tenant visibility for the support UI.

Greptile P1 SECURITY follow-up (view-only admin write bypass).
2026-05-06 15:37:12 -07:00
Ishaan Jaffer
303f9a5d80
fix(agent_session_endpoints): block view-only admins from mutating other tenants' rows
is_proxy_admin previously returned True for both PROXY_ADMIN and
PROXY_ADMIN_VIEW_ONLY, letting view-only admins skip
assert_caller_owns_agent / assert_caller_owns_session on every write
endpoint and create / update / delete other tenants' agents,
sessions, and runs.

Split the helpers:
  * is_proxy_admin: now full-admin only (used for write paths via the
    fall-through to per-tenant ownership; view-only fails and gets 404).
  * is_proxy_admin_read: full + view-only, used on read paths so the
    support UI can still render any tenant's resources.
  * assert_caller_can_mutate: explicit 403 guard for view-only on
    every state-mutating endpoint.

The mutating endpoints in agent/session/run files call
assert_caller_can_mutate before any DB write — see follow-up commits.

Greptile P1 SECURITY (review #PRR_kwDOKALCgc78uM7F).
2026-05-06 15:37:07 -07:00
Ishaan Jaffer
5037026d75
fix(agent_session_endpoints): require LITELLM_AGENT_JWT_SECRET, no master-key fallback
The daemon JWT secret must be a SEPARATE credential from the proxy
master key. The previous fallback to LITELLM_MASTER_KEY conflated
two distinct auth surfaces — a captured daemon JWT could be used
to mint regular API keys with master-key authority.

Replace _get_signing_secret with a strict check that raises
AgentJWTSecretNotConfiguredError if the dedicated env var is unset.
Add is_agent_jwt_secret_configured() so proxy_server.py can refuse
to mount the routers when the secret is missing.

Greptile P1 SECURITY (review #PRR_kwDOKALCgc78uM7F).
2026-05-06 15:36:58 -07:00
ishaan-berri
aba131d3cf
fix: Vertex Anthropic streaming status error hangs (#27310)
* Fix streaming HTTP status error hangs

Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>

* Fix sync streaming HTTP status error hangs

Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>

* Cap sync streaming error read workers

Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>

---------

Co-authored-by: oss-agent-shin <279349115+oss-agent-shin@users.noreply.github.com>
Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>
2026-05-06 15:32:55 -07:00
ishaan-berri
c15718f9d1
Fix Anthropic streaming reasoning token usage (#27319)
* fix anthropic streaming reasoning token usage

Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>

* test anthropic streaming reasoning usage end to end

Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>

* address anthropic reasoning token text split

Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>

* harden anthropic reasoning usage for mocked tokens

Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>

---------

Co-authored-by: oss-agent-shin <279349115+oss-agent-shin@users.noreply.github.com>
Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>
2026-05-06 15:28:22 -07:00
ishaan-berri
bd1a05aed9
Fix MCP DB reload partial failures (#27314)
* Fix MCP database reload partial failures

Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>

* Avoid staged MCP registry exposure

Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>

---------

Co-authored-by: oss-agent-shin <279349115+oss-agent-shin@users.noreply.github.com>
Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>
2026-05-06 15:18:18 -07:00
ishaan-berri
924c141843
Add new chat model metadata (#27313)
* add new model metadata

Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>

* address review feedback

Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>

---------

Co-authored-by: oss-agent-shin <279349115+oss-agent-shin@users.noreply.github.com>
Co-authored-by: ishaan-berri <ishaan-berri@users.noreply.github.com>
2026-05-06 15:15:21 -07:00
Ishaan Jaffer
f6e5951556
test(agent_session_endpoints): validate cleanup sweeper for expiry/dead-daemon/stuck-runs (LIT-2877 #13) 2026-05-06 15:10:37 -07:00
Ishaan Jaffer
6a815d2b1f
test(agent_session_endpoints): validate JWT scope/exp/cross-session/terminated rejection (LIT-2877 #12) 2026-05-06 15:10:35 -07:00
Ishaan Jaffer
306df1aecc
test(agent_session_endpoints): validate cascade delete + provider.terminate (LIT-2877 #11) 2026-05-06 15:10:34 -07:00
Ishaan Jaffer
5e93b44177
test(agent_session_endpoints): validate cross-tenant isolation at all 3 levels (LIT-2877 #10) 2026-05-06 15:10:32 -07:00
Ishaan Jaffer
3f60ae6a90
test(agent_session_endpoints): validate session + run idempotency (LIT-2877 #9) 2026-05-06 15:10:31 -07:00
Ishaan Jaffer
1b481e4949
test(agent_session_endpoints): validate SSE resume + Last-Event-ID header (LIT-2877 #8) 2026-05-06 15:10:29 -07:00
Ishaan Jaffer
8feafae5b9
test(agent_session_endpoints): validate concurrent run-create returns 409 run_busy (LIT-2877 #7) 2026-05-06 15:10:28 -07:00
Ishaan Jaffer
42d8fdb741
test(agent_session_endpoints): validate /followup smart inject vs new-run (LIT-2877 #6) 2026-05-06 15:10:26 -07:00
Ishaan Jaffer
1567c00c20
test(agent_session_endpoints): validate run state transitions + cancel (LIT-2877 #5) 2026-05-06 15:10:25 -07:00
Ishaan Jaffer
a271a73276
test(agent_session_endpoints): validate session state transitions (LIT-2877 #4) 2026-05-06 15:10:23 -07:00
Ishaan Jaffer
46266b1dd3
test(agent_session_endpoints): validate agent reused across sessions (LIT-2877 #3) 2026-05-06 15:10:22 -07:00
Ishaan Jaffer
1801c1cf15
test(agent_session_endpoints): add in-memory Prisma fake + multi-tenant TestClient fixtures 2026-05-06 15:10:21 -07:00
Ishaan Jaffer
b483dbe7e9
test(agent_session_endpoints): add package marker 2026-05-06 15:10:19 -07:00
Ishaan Jaffer
266081ff30
feat(proxy): mount /v2/agents+sessions routers + start cleanup sweeper 2026-05-06 15:03:25 -07:00
Ishaan Jaffer
68d11f445f
feat(agent_session_endpoints): add cleanup sweeper for expired sessions, dead daemons, stuck runs 2026-05-06 15:03:24 -07:00
Ishaan Jaffer
d8c66623ee
feat(agent_session_endpoints): add daemon callbacks (register/heartbeat/next-run/events:append) 2026-05-06 15:03:22 -07:00
Ishaan Jaffer
6ee24a5cbc
feat(agent_session_endpoints): add /v2/sessions/{sid}/runs CRUD + SSE + cancel 2026-05-06 15:03:21 -07:00
Ishaan Jaffer
bca9abeaef
feat(agent_session_endpoints): add module init exposing routers 2026-05-06 15:03:19 -07:00
Ishaan Jaffer
f69ac9e029
feat(agent_session_endpoints): add /v2/agents CRUD endpoints 2026-05-06 14:59:53 -07:00