UNPKG

@adonis-agora/agent

Version:

Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.

111 lines • 4.03 kB
import { SignJWT } from 'jose'; import { importSigningKey } from './keys.js'; import { nowSeconds, randomId, requireSecureUrl } from './util.js'; /** Lifetime of every assertion this agent signs. The browser assertion caps at 60s (section 5). */ export const ASSERTION_TTL_SECONDS = 60; /** * The Personal Agent's identity (4.1): its `client_id` URL, its metadata document, and the * keys from `jwks_uri` that sign client assertions, Session assertions and browser assertions. */ export class AgentIdentity { baseUrl; clientName; logoUri; keys; extensions; constructor(cfg, sec = {}) { const base = requireSecureUrl(cfg.baseUrl, 'identity base URL', sec); if (base.search || base.hash) throw new TypeError('identity base URL must not have a query'); this.baseUrl = base.toString().replace(/\/+$/, ''); this.clientName = cfg.clientName; this.logoUri = cfg.logoUri ?? `${this.baseUrl}/logo.svg`; if (cfg.keys.length === 0) throw new TypeError('identity needs at least one signing key'); this.keys = cfg.keys; this.extensions = cfg.extensions; } /** `client_id` is the URL the metadata document is fetched from (4.1). */ get clientId() { return `${this.baseUrl}/agent.json`; } get jwksUri() { return `${this.baseUrl}/jwks.json`; } /** Same origin as `client_id`, as 4.1 requires for `redirect_uris`. */ get redirectUri() { return `${this.baseUrl}/oauth/callback`; } metadata() { const doc = { client_id: this.clientId, client_name: this.clientName, logo_uri: this.logoUri, jwks_uri: this.jwksUri, redirect_uris: [this.redirectUri], token_endpoint_auth_method: 'private_key_jwt', }; if (this.extensions && Object.keys(this.extensions).length > 0) { doc.extensions = this.extensions; } return doc; } /** The public key set. Never contains private members. */ jwks() { return { keys: this.keys.map((k) => ({ ...k.publicJwk })) }; } async sign(payload, typ) { const key = this.keys[0]; if (!key) throw new TypeError('identity has no signing key'); const header = { alg: 'ES256', kid: key.kid }; if (typ) header.typ = typ; return new SignJWT(payload).setProtectedHeader(header).sign(await importSigningKey(key)); } /** * `private_key_jwt` client assertion (RFC 7523 section 2.2): `iss` = `sub` = `client_id`, * `aud` = the authorization server (its token endpoint by default), fresh `jti`. */ clientAssertion(audience) { const iat = nowSeconds(); return this.sign({ iss: this.clientId, sub: this.clientId, aud: audience, iat, exp: iat + ASSERTION_TTL_SECONDS, jti: randomId(), }); } /** * The JWT bearer grant assertion that starts or renews a Session (4.2): names the agent in * `iss` and the User ID in `sub`; `aud` is the token endpoint as a single string. */ sessionAssertion(userId, tokenEndpoint) { const iat = nowSeconds(); return this.sign({ iss: this.clientId, sub: userId, aud: tokenEndpoint, iat, exp: iat + ASSERTION_TTL_SECONDS, jti: randomId(), }); } /** Browser assertion (section 5), header `typ: poppy-browser+jwt`, `exp` <= `iat` + 60. */ browserAssertion(input) { const iat = nowSeconds(); return this.sign({ iss: this.clientId, sub: input.userId, aud: input.endpoint, session_id: input.sessionId, return_to: input.returnTo, iat, exp: iat + ASSERTION_TTL_SECONDS, jti: randomId(), }, 'poppy-browser+jwt'); } } //# sourceMappingURL=identity.js.map