Tags: aegra/aegra
Tags
test(runs): settle run via join before asserting interrupt status (#411) The wait endpoint can return 200 before the run row transitions out of "running" (the status write races the HTTP response), so listing the run immediately sometimes read "running" and failed the terminal-status assert. Join the run to block until it reaches a terminal state, then re-read the settled status. Verified stable across repeated runs. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(db): switch UUID defaults from uuid-ossp to gen_random_uuid (Azur… …e compat) (#391) * fix(db): switch UUID defaults from uuid-ossp to gen_random_uuid uuid-ossp is blocked on managed Postgres services that restrict the extension allowlist (notably Azure Database for PostgreSQL), so deploys crash on the initial migration when it runs CREATE EXTENSION. The only thing we used from the extension was uuid_generate_v4() as the server_default for the assistant.assistant_id and runs.run_id primary keys — both pure random UUIDs. gen_random_uuid() ships in Postgres 13+ core and produces the same shape (random v4), so no behavior change beyond losing the extension dependency. Three changes: 1. ORM models (assistant, runs) point at gen_random_uuid() so future metadata operations and any tooling that reads the model see the current shape. 2. Initial migration drops the CREATE EXTENSION statement and uses gen_random_uuid() in the server_default literals. Fresh installs on Azure (or any managed PG with a restricted extension list) now complete the initial migration cleanly. 3. New migration f1a2b3c4d5e6 alters the live server_default on both tables for deployments that already ran the old initial migration. The uuid-ossp extension is left installed — dropping it risks breaking other applications on the same database, and keeping it is harmless once nothing depends on it. Existing thread/run/assistant rows are untouched; the column default only governs new inserts that omit an explicit ID. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(db): drop extension recreation from downgrade, trim comments Greptile P1 catch on PR review: CREATE EXTENSION IF NOT EXISTS "uuid-ossp" in downgrade only suppresses the "already installed" error, not the allowlist-violation error Azure raises. On the exact platforms this PR fixes, downgrade would abort before restoring the column defaults. Drop the CREATE EXTENSION call. Downgrade now only touches schema (column defaults), which works on every platform. Extension state is operator-managed: if you downgrade onto a deployment where uuid-ossp isn't installed, install it out-of-band — the migration shouldn't silently assume it can. Also trims oversized comments across orm.py + initial migration + new migration to keep them informational, not narrative. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: drop stale "earlier revisions" comment from initial migration Comment narrated git history that git blame already holds. Future readers don't need to know what the file used to do. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: bump version to 0.9.17 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
fix(auth): dispatch handle_event on assistant version/schema/graph en… …dpoints (#386) * fix(auth): dispatch handle_event on assistant version/schema/graph endpoints POST /assistants/{id}/latest, POST /assistants/{id}/versions, GET /assistants/{id}/schemas, /graph, /subgraphs all skipped `handle_event` entirely. Any auth handler scoping assistant visibility (tenant guards, role gates, metadata constraints) was silently bypassed on these five endpoints while the canonical CRUD surface enforced it. Wire each endpoint through the existing `build_auth_context` + `handle_event` pattern. The four read-shaped endpoints share a single `_authorize_assistant_read(user, assistant_id)` helper so adding the sixth read endpoint in future is one line, not eight. set_latest fires `assistants.update` (it mutates which version is served); the other four fire `assistants.read`. Default-allow remains: deployments with no handler see zero behavior change. Deployments with a handler see it invoked on the full read surface instead of three out of seven endpoints. Regression tests pin the (resource, action) and payload shape per endpoint plus the 403 propagation path. Parametrized so adding the next read endpoint costs one row. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(auth): stop swallowing programmer errors in handle_event `except Exception` wrapped TypeError / AttributeError / ValueError into `HTTPException(500, "Authorization error: <repr>")`. Two problems: 1. Detail leak. The original exception's `str(e)` was echoed back to the API client — handler internals (variable names, types, etc.) became visible over HTTP. 2. Lost traceback. The standard error path logs the full stack; wrapping replaced it with a generic message. Operators saw a 500 with no clue where it came from. Drop the broad catch. Keep the specific `Auth.exceptions.HTTPException` and `AssertionError` paths — those are how handlers signal auth decisions. Everything else propagates to the standard error handler: generic 500 to the client, full stack in logs. Also matches the CLAUDE.md rule against broad `except` blocks. Regression test now asserts the underlying exception propagates instead of getting wrapped. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: revert version bump (0.9.16 not yet released) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
fix(orm): strip NULL bytes from JSONB writes (#370) (#376) * fix(orm): strip NULL bytes from JSONB writes (#370) Postgres JSONB rejects U+0000 with asyncpg UntranslatableCharacterError. When a LangGraph node returns state containing a literal NULL byte (from agent hallucination or untrusted input), the runs.output JSONB UPDATE fails and the run is marked `error` even though the graph completed. Add a `JsonbSafe` TypeDecorator that recursively strips NULL bytes from strings (and dict keys) at the bind-param boundary, then swap it in for every JSONB column on Assistant, AssistantVersion, Thread, and Run. The read path is untouched — only writes are filtered, so existing rows are unaffected. Stripping at the type boundary covers all ~10 JSONB columns uniformly and protects future JSONB additions automatically, instead of scattering strip helpers across services. Bumps both packages to 0.9.15. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(orm): guard recursion depth and log JSONB key collisions Addresses two P2 review findings on the JsonbSafe TypeDecorator: 1. Unbounded recursion: deeply nested adversarial payloads could hit Python's ~1000-frame limit and convert the original asyncpg error into a RecursionError at bind time. Add a 200-level depth guard — well above any legitimate agent payload, well below the interpreter ceiling — and return untouched past that, letting Postgres surface the real complaint. 2. Silent data loss on key collision: if two raw dict keys differ only by stripped NULL bytes (e.g. "a\x00" and "a"), last-wins dropped the earlier value with no signal. Log a structured warning when a collision is detected so it's visible in observability. Adds depth-guard and collision-warning tests; full unit suite (1031) still green. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
PreviousNext