/** * Persistent configuration for Ory agent plugins. * * Config is stored at ~/.config/ory-agent-plugins/config.json and shared * across all harness plugins. Environment variables always take precedence * over values in the config file. */ export interface OryOAuth2Tokens { accessToken: string; refreshToken?: string; /** Unix epoch seconds at which the access token expires. */ expiresAt: number; /** Subject (sub claim) the token was issued for. */ subject?: string; /** Client ID the token was issued under. In-memory only. */ clientId?: string; /** @deprecated Accepted in memory for older callers; never persisted. */ idToken?: string; /** Bounded display claim extracted before the ID token is discarded. In-memory only. */ displayName?: string; /** Scope string returned by the token endpoint. In-memory only. */ scope?: string; } /** Credentials for the human user principal (interactive login). */ export interface OryUserCredentials { /** Persisted OAuth2 tokens from a browser-based PKCE login. */ oauth2?: OryOAuth2Tokens; } /** * Credentials returned by an OAuth2 Dynamic Client Registration flow * (RFC 7591). Persisted by harness and session so separate hook processes in * one session reuse its identity without sharing it with another session. */ /** * The delegation edge recorded on a dynamically-registered OAuth2 client's * `metadata` at registration time (see {@link OryAgentDynamicCredentials.delegation}). * * This compact local binding lets later processes validate which principal * registered the credential and recover the delegator when no live principal * is available. Ory's public DCR endpoint does not accept arbitrary metadata. */ export interface OryDelegationRecord { /** The delegator, as a namespaced subject: `user:` or `agent:`. */ delegatedBy: string; /** Which edge of the chain this client sits on. */ delegationType: "user-to-agent" | "agent-to-subagent"; /** Harness the delegation was established under (audit aid). */ harness?: string; /** Sub-agent type, present only for `agent-to-subagent`. */ subAgentType?: string; /** ISO-8601 timestamp the delegation was stamped. */ delegatedAt: string; } export interface OryAgentDynamicCredentials { /** Issued client_id. */ clientId: string; /** Issued client_secret. Omitted for public clients. */ clientSecret?: string; /** Bearer used to manage this registration via RFC 7592. */ registrationAccessToken?: string; /** RFC 7592 management URI for this registration. */ registrationClientUri?: string; /** Unix epoch seconds when this registration was issued. */ registeredAt: number; /** Project URL the registration was issued against. */ projectUrl: string; /** Harness name baked into the client_name (audit aid). */ harness?: string; /** * The human-readable OAuth2 `client_name` this registration was issued * under (e.g. `"Agent · claude-code @ host"`). Kept only * on a fresh registration response for immediate display; never persisted. * Later callers reconstruct a best-effort name from the outer harness key. */ clientName?: string; /** * The delegation edge recorded for this client at registration. Persisted * here in local config only — Ory's public DCR endpoint rejects a `metadata` * field, so it is never stored server-side. Absent when the delegator wasn't * known at registration time (e.g. DCR bootstrapped from an out-of-band * initial access token with no user/agent principal). */ delegation?: OryDelegationRecord; /** * The delegation node ids the agent-security broker returned for this agent's * `user → agent` edge, **keyed by session**. * * Persisted at session start so a later sub-agent registration — which may run * in a separate subprocess with no live agent principal — can pass the node id * verbatim as `delegated_by` when recording the `agent → subagent` edge, * without the plugin re-deriving the server's join-key encoding. * * Kept as a map for compatibility with credentials written by the earlier * install-scoped model. A session-scoped credential normally contains only * its own entry. * * Server-assigned and opaque to the plugin — never parsed, only echoed. */ delegationNodeIds?: Record; } /** * How many sessions' delegation node ids to keep per credential. Only the * current session's is ever read; the rest are kept as a small audit tail and * bounded so a long-lived install's config doesn't grow without limit. */ export declare const DELEGATION_NODE_HISTORY = 10; /** * Credentials for the AI agent principal (machine identity). Every harness * session owns a distinct DCR client, and every typed sub-agent in that session * owns a distinct child client. */ export interface OryAgentCredentialsBlock { /** * Agent credentials keyed by harness then session. */ dynamic?: Record>; /** * Sub-agent registrations keyed by harness, session, then type. */ subAgents?: Record>>; /** * Aged-out or superseded registrations queued for best-effort RFC 7592 * revocation. Failed revocations remain here for a later session to retry. */ retired?: OryAgentDynamicCredentials[]; } /** * Key used when a credential record carries no harness of its own — only * reachable by migrating a config written before credentials were keyed by * harness, where the `harness` field had never been persisted. Such a record * is never re-used (no harness matches it), but it stays addressable so * uninstall can still revoke the server-side client rather than orphaning it. */ export declare const UNKNOWN_HARNESS_KEY = "unknown"; /** * Session key for a caller with no session concept — an Agent SDK integration * embedded in a long-running service, or a one-off script. Such a caller gets * one stable identity under this key rather than a new client per call, which * is the only sensible reading of "per session" when there are no sessions. * Credentials migrated from a config written before session keying also land * here: the session they belonged to is not recoverable, but the record stays * addressable so uninstall can revoke it. */ export declare const SESSIONLESS_KEY = "sessionless"; /** * What the plugin does when a permission check returns deny. * * - `observe` — log the denial, emit a `permission.observe_deny` activity event, * allow the tool through. The onboarding default: first-time users * see what Ory would block, without being blocked. * - `enforce` — block the tool. The production posture. * * Only meaningful while Agent Security is connected (see * {@link resolveConfig}); with no project URL no checks run at all, so there is * no deny to have a posture about. */ export type PermissionMode = "observe" | "enforce"; /** * The last permission mode read from the server, cached locally so the * synchronous config read reflects it across process restarts and survives a * temporary loss of connectivity. Written only by the server-read path (see * `permission-mode.ts`); never set by hand — the mode is admin-controlled on the * Ory project, not locally. */ export interface PermissionModeCache { mode: PermissionMode; /** Unix epoch ms when this value was last read from the server. */ fetchedAt: number; /** Project this posture was read from. Absent only on legacy scalar caches. */ projectUrl?: string; /** Per-project/principal values used to avoid applying one scope's fallback to another. */ scopes?: Record; } export interface OryPluginConfig { /** * Ory project API URL — the SDK base path all API calls target. The Ory * Console "Get Started" panel calls this `ORY_SDK_URL`; the env var of that * name is accepted as an alias for `ORY_PROJECT_URL` (see {@link resolveConfig}). */ projectUrl?: string; /** * Canonical Ory Agent Security broker URL. Broker calls resolve this value * independently from {@link projectUrl}, which remains the OAuth2/DCR origin. * Older configs fall back to `projectUrl` until this key is persisted. */ agentSecurityUrl?: string; /** * Ory project UUID — the other half of the Console "Get Started" pair * (`ORY_PROJECT_ID`). Persisted by the interactive installer because the * admin `ory` CLI operations key off it (`--project `). It is an * identifier for admin APIs, **not** an SDK URL source: the SDK base URL is * always the slug-based {@link projectUrl}, and a project id (a UUID) must * never be substituted for a slug when building it. */ projectId?: string; /** * Human-readable project and workspace names, captured at install time for * display only (e.g. the dashboard's connected-stack card). Not used to * address any API — {@link projectUrl} / {@link projectId} do that. */ projectName?: string; workspaceName?: string; /** Opaque broker node ids keyed by harness and session. Contains no credentials. */ delegationAnchors?: Record>; /** * Public OAuth2 client id used by the user PKCE browser flow when * the interactive user login runs (every session). The client must be registered ahead of time * in the Ory project with all four loopback redirect URIs * (`http://127.0.0.1:47823..47826/callback`) and * `token_endpoint_auth_method=none`. The local stack provisions one * automatically; hosted Agent Security uses the reserved * `ory-agent-security-login` client by default. Custom deployments can * override the id here or through `ORY_OAUTH2_CLIENT_ID`. */ oauth2ClientId?: string; /** * The last permission mode read from the Ory project, cached locally. The * mode itself is admin-controlled server-side (a Keto permission — see * `permission-mode.ts`); this is only a cache so the value survives restarts * and brief loss of connectivity. Not user-settable. */ permissionModeCache?: PermissionModeCache; /** * Overrides the namespace the human user is addressed under in permission * checks. Users are always addressed as a SubjectSet `:`; * this only changes the namespace (default `User`) and must match how the * tuples were written in Keto. The local stack seeds tuples as `User:`, * so the local-stack install path persists `"User"` here explicitly. The * `ORY_USER_SUBJECT_NAMESPACE` env var overrides this. Unset ⇒ the `User` * default. */ userSubjectNamespace?: string; /** * Harness plugins currently installed against this shared config, e.g. * `["claude-code", "codex"]`. Written by the install path (see * `wireRuntime`) and pruned by `uninstall`, so uninstalling one plugin can * tell whether it is the *last* consumer of the shared configuration — and * therefore whether wiping it is safe. Absent on configs written before the * registry existed; the uninstall path falls back to the harnesses evidenced * by the runtime manifest and the persisted credentials. */ installedHarnesses?: string[]; /** Credentials for the human user (interactive PKCE login). */ user?: OryUserCredentials; /** Credentials for the AI agent process (machine identity). */ agent?: OryAgentCredentialsBlock; } /** * Single OS-agnostic data directory for *all* Ory agent plugin state: * the shared config file, persisted DCR credentials, and any harness * asset directories. One place per platform — never split across * `~/.ory/` and `~/.config/`. * * - Windows: `%APPDATA%/ory-agent-plugins` (e.g. * `C:\Users\\AppData\Roaming\ory-agent-plugins`). * - Unix (macOS + Linux): `$XDG_CONFIG_HOME/ory-agent-plugins` when set, * otherwise `~/.config/ory-agent-plugins`. * * `XDG_CONFIG_HOME` is also honored on Windows when set — useful for * tests and for users who explicitly opt into XDG layout on Windows. */ export declare function getDataDir(): string; /** * Per-harness sub-directory under the shared data dir. Use this for * harness-owned asset trees (e.g. the Claude Code plugin's marketplace * checkout) so all plugin state lives under one root. */ export declare function getHarnessDataDir(harness: string): string; /** * Return the path to the config file. */ export declare function getConfigPath(): string; /** * Load config from the config file. Returns an empty object if the file * does not exist or is invalid. */ export declare function loadConfig(): OryPluginConfig; /** * The harness plugins recorded as installed against this shared config. * Returns an empty array when nothing has been registered yet (which, on a * config written before the registry existed, is not the same as "nothing is * installed" — callers that must not over-read that should reconcile against * the runtime manifest and persisted credentials; see * `resolveInstalledHarnesses` in `uninstall.ts`). */ export declare function listInstalledHarnesses(): string[]; /** * Record a harness plugin as installed. Idempotent — installing twice leaves * one entry. Returns the full list after the addition. */ export declare function registerInstalledHarness(harness: string): string[]; /** * Forget a harness plugin. Returns the harnesses still installed afterwards — * what the uninstall path uses to decide whether the shared config is now * unreferenced and safe to wipe. */ export declare function unregisterInstalledHarness(harness: string): string[]; /** * Save config to the config file. Merges with existing values — only * provided fields are overwritten. Concurrent processes serialize via a * sibling lockfile and the write is atomic (write-temp + rename). * * To clear a field rather than ignore it, set it to `null` in `update` * (e.g. `saveConfig({ user: null })`); `undefined` is treated as * "leave alone" for backwards compatibility. */ export declare function saveConfig(update: Partial<{ [K in keyof OryPluginConfig]: OryPluginConfig[K] | null; }>): void; /** * Read-modify-write the config under a sibling lockfile. The mutator * receives the current on-disk config and must return the new config. * Concurrent processes serialize on the lock; the write itself is * atomic via write-temp + rename. */ export declare function mutateConfig(mutator: (current: OryPluginConfig) => OryPluginConfig | undefined): void; /** * True when `value` looks like an Ory project **id** (a UUID) rather than a * project **slug**. Project slugs are human-readable tokens (e.g. * `nervous-galileo-1a2b3c`) and are never UUIDs, so this cleanly distinguishes * the two. Used to guard the SDK-URL derivation: the URL subdomain must be the * slug — a project id must never be substituted for it (see * {@link projectUrlFromSlug}). The id addresses project-scoped **admin** APIs * (the `ory` CLI's `--project` flag), which is a different concern from the SDK * base URL. */ export declare function looksLikeProjectId(value: string): boolean; /** * The values Agent Security needs, and whether all are present. * * This is the *only* thing that decides whether the security half of a plugin * runs. There is no stored on/off flag and no env override: a plugin is * connected exactly when it can address a project (`projectUrl`), reach the * canonical broker (`agentSecurityUrl`, with a legacy project URL fallback), * and complete the user's browser sign-in (`oauth2ClientId`). Anything less and the * authentication gates, permission checks, and delegation recording all no-op. * * The developer-experience half — skills, commands, the local Ory stack, the * MCP server, and local activity logging — is installed unconditionally and never * consults this. */ export interface SecurityConnection { /** True when all required values resolve (including the legacy broker fallback). */ connected: boolean; /** * Which required values are missing, in the order a user should supply them. * Empty when {@link connected} is true. Drives every "not connected because…" * message so the reason is never guessed at the call site. */ missing: Array<"projectUrl" | "agentSecurityUrl" | "oauth2ClientId">; } /** Reserved public PKCE client provisioned by Ory Agent Security setup. */ export declare const DEFAULT_OAUTH2_CLIENT_ID = "ory-agent-security-login"; export declare const DEFAULT_AGENT_SECURITY_URL = "https://agents.console.ory.com"; export declare const LOCAL_AGENT_SECURITY_URL = "https://agents.console.ory:8080"; /** * The one predicate the gates ask: is Agent Security connected? * * Shorthand for `resolveConfig().security.connected`, provided so the many * call sites that only need the boolean read as a single question rather than * a property walk. When this is false the plugin still runs — it just logs activity * locally and gates nothing. */ export declare function isSecurityConnected(): boolean; /** * Render why Agent Security isn't running, e.g. * `"missing --oauth2-client-id (ORY_OAUTH2_CLIENT_ID)"`. Returns undefined when * it *is* connected, so callers can `?? "connected"` at the display site. */ export declare function describeSecurityGap(security?: SecurityConnection): string | undefined; /** * Resolve config by checking environment variables first, then the config file. * Returns the merged result with the source of each value. * * `projectUrl` accepts `ORY_SDK_URL` as an alias for `ORY_PROJECT_URL` (the two * names the Console "Get Started" uses). The SDK URL is **always** slug-based; * `projectId` (`ORY_PROJECT_ID`) is a distinct identifier for admin APIs and is * never turned into an SDK URL — a project id is not a slug. */ export declare function resolveConfig(): { projectUrl?: string; agentSecurityUrl?: string; projectId?: string; projectName?: string; workspaceName?: string; oauth2ClientId?: string; /** * Whether Agent Security runs, derived from the project and broker URLs. The * OAuth2 client always resolves through an override or the reserved default. * See {@link SecurityConnection}. */ security: SecurityConnection; /** * The permission mode as last read from the server and cached locally * (`observe` when nothing is cached). The authoritative, live value is * resolved per-check by `resolvePermissionMode`; this synchronous value is the * cached fallback used by display surfaces and the MCP `applyPermissionMode` * path. */ permissionMode: PermissionMode; permissionModeSource: "server" | "cache" | "default"; /** The raw cache entry, exposing `fetchedAt` for freshness display. */ permissionModeCache?: PermissionModeCache; userSubjectNamespace?: string; userSubjectNamespaceSource: "env" | "config" | "none"; projectUrlSource: "env" | "config" | "none"; agentSecurityUrlSource: "env" | "config" | "projectUrl" | "default"; projectIdSource: "env" | "config" | "none"; oauth2ClientIdSource: "env" | "config" | "default"; }; /** * Build the "Agent Security is not connected" message for a harness CLI. * * Deliberately does not frame this as a degraded mode: the plugin is fully * installed and its developer-experience half is working. The message states * the one thing that is off and the values that turn it on. */ export declare function configPromptMessage(binName: string): string;