feat(agents): add Agent Auth blueprints, tokens, instances and sessions - #104
Conversation
Implements the 13 Agent Auth endpoints (blueprint CRUD, all four token grants plus validation, instance and session listing/lookup/deletion/ revocation), the seven agent.* lifecycle events, and an agentBlueprints seed key. Agent Registration is intentionally left unimplemented. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
Original prompt from madison.packer
|
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Match production's AgentInstancesDeleter: deleting an organization or a membership deletes the agent instances that reference it (firing their deleted and session-revoked events), deactivating a membership revokes its delegated sessions, and deleting a permission drops the slug from every blueprint so no later mint can grant it. A chained session whose parent row is missing now reads as revoked instead of posing as a root. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
I found four API-parity cases worth addressing: 1. Accept user tokens with an explicit
|
- Accept user access tokens that carry an explicit `sub_profile: 'user'` for the user_delegated grant; other subject profiles are still rejected. - Delete the instances delegated from a user's memberships when the user is deleted, the way membership and organization deletion already do. - Leave already-expired sessions untouched in revoke cascades so they stay `expired` with a null `revoked_at`, while still walking their descendants. - Reject `description: null` and partial `session_settings` on blueprint create; both remain valid on PATCH. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
@gjtorikian re #104 (comment) — thanks, all four were right and each is fixed in 3973335 with a regression test:
Note the |
|
tighten that too, of course |
The agentBlueprints seed key now rejects a null description and requires all three session_settings values when the object is present, matching POST /agents/blueprints. Omitting session_settings still applies production's defaults. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
@gjtorikian done in c8e0dcd — the |
Summary
Adds Agent Auth to the emulator: the 13 endpoints under
agents.blueprints,agents.blueprints.tokens,agents.instancesandagents.sessions, the sevenagent.*events (constants already existed ingenerated/events.ts), and anagentBlueprintsseed key. SUPPORTED.md goes from Agents 0/16 to Agent Auth 13/13; Agent Registration (PATCH /agents/claims/attempts,POST /agents/credentials/validate) is intentionally left unimplemented and listed as its own row.Behaviour was modelled on the API's
agent-blueprints-token.controller.ts/ minter service and the OpenAPI spec; the non-obvious parts:Token grants (
POST /agents/blueprints/:id/tokens,typediscriminator):Every session
expires_at = min(now + refresh_token_ttl, root.created_at + max_age), so neither chaining nor refreshing can outlive the chain root's max-age window. Delegated permissions andactare recomputed from the member's current role on every mint/refresh; a delegated chain also fails withuser_session_endedonce the backing user session is gone, and revoking a user session (/user_management/sessions/revoke, logout, user delete) cascades to its agent sessions.JWT:
JWTManager.signgains an optionaltypheader so agent tokens aretyp: at+jwt(default staysJWT), plussub_profile,act,intentclaim types. Agent tokens are signed with the environment key so JWKS validation works.audis theworkos-emulateplaceholder — production mintsenvironment.clientId, but nothing at the API-key-authenticated token endpoint names a client; documented in README/SUPPORTED.Session model:
WorkOSAgentInstanceSessionkeepsrefresh_token,parent_session_id,user_session_id,permissions,intentinternally;statusis derived (revoked/expired/active), never stored. Revoke cascades through descendants and is idempotent (200 with the existingrevoked_at). Deleting an instance revokes-then-deletes its sessions; deleting a blueprint does the same for its instances, soagent.instance.session.revoked/agent.instance.deleted/agent.blueprint.deletedfire in that order.Seed:
agentBlueprints[]withinvocable_by.organizationsas org names (resolved to ids like feature-flag targets); validator checks referenced permissions/roles/orgs, duplicate names/ids and session-setting ranges (max_age ≤ 31536000,access_token_ttl ≤ 3600,refresh_token_ttl ≤ 5184000). Instances and sessions are never seeded.Testing
New:
src/workos/routes/agents.spec.ts(36 tests: CRUD/pagination/validation, all four grants, chain depth, root max-age anchoring, refresh rotation + replay, validate, cascades, event payloads) andsrc/workos/seed-agent-blueprints.spec.ts(6).Link to Devin session: https://app.devin.ai/sessions/598441040892444abdd5d09e78b2379c
Open in Devin Desktop: https://app.devin.ai/desktop/session/598441040892444abdd5d09e78b2379c?variant=devin
Requested by: @m0tzy