Plugin API
Provider plugins live in @valetkey/core types. A plugin is a plain object; there is no class hierarchy or registration step.
ProviderPlugin
Section titled “ProviderPlugin”type ProviderPlugin<TConfig = unknown> = { name: string; config: TConfig; detect?: (projectDir: string) => Promise<boolean>; seed?: (ctx: MintContext, persona: Persona) => Promise<void>; mint: (ctx: MintContext, persona: Persona) => Promise<SessionArtifact>; verify?: (ctx: MintContext, artifact: SessionArtifact) => Promise<SessionStatus>; revoke?: (ctx: MintContext, artifact: SessionArtifact) => Promise<void>;};
type SessionStatus = "valid" | "expired" | "invalid";mint is the only required hook. seed provisions the persona’s user and must be idempotent. verify lets valetkey ask whether a stored session is still good. revoke runs on logout and should invalidate the session server-side. detect exists for future init auto-detection of the auth system in a project.
MintContext
Section titled “MintContext”type MintContext = { projectDir: string; origins: Array<string>; secrets: { get: (key: string) => Promise<string> }; fetch: typeof fetch; browser: AgentBrowser; log: (event: string, data?: Record<string, unknown>) => void; notify: (message: string) => void;};
type AgentBrowser = { withPage: <T>(fn: (page: CdpPage) => Promise<T>) => Promise<T>; readCookies: (origins: Array<string>) => Promise<Array<StorageStateCookie>>; readLocalStorage: ( origins: Array<string>, ) => Promise<Array<{ origin: string; entries: Record<string, string> }>>;};
type CdpPage = { goto: (url: string) => Promise<void>; evaluate: <T>(expression: string) => Promise<T>;};secrets.get reads the project vault and registers every returned value for output redaction. log writes provider-scoped audit events. notify shows a message to the person running valetkey, which interactive providers use to ask for a sign-in; under the MCP server it is a no-op.
browser is the managed Chrome profile. It is only live when the browser is running, which means valetkey login --browser, an MCP valetkey_login_as call, or an already-open browser; otherwise every method throws with a message telling the user to start it. Anything a plugin does through withPage happens in valetkey’s process and never enters agent context, which is what makes interactive sign-in safe. See the record provider for a plugin built on it.
Persona and SessionArtifact
Section titled “Persona and SessionArtifact”type Persona = { name: string; seed: Record<string, unknown>;};
type SessionArtifact = { persona: string; provider: string; origins: Array<string>; createdAt: string; expiresAt: string | null; cookies: Array<StorageStateCookie>; localStorage: Array<{ origin: string; entries: Record<string, string> }>; headers: Record<string, string>; meta: { userId?: string; email?: string; role?: string };};
type StorageStateCookie = { name: string; value: string; domain: string; path: string; expires: number; httpOnly: boolean; secure: boolean; sameSite: "Strict" | "Lax" | "None";};Build artifacts with createSessionArtifact from @valetkey/custom (re-exported from core), which fills createdAt, empty localStorage, headers, and meta. Leave persona and provider alone; the core stamps them after mint returns. headers is reserved for the planned API-delivery mode and is ignored by browser delivery.
What the core enforces around your plugin
Section titled “What the core enforces around your plugin”expiresAtis clamped to the session TTL.nullmeans “use the full TTL”.- Every cookie’s
domainmust match a configured origin’s hostname, or the whole artifact is rejected. - Artifacts are validated against a schema when read back, so a corrupt or tampered file loads as no session rather than a broken one.
- Anything you throw is surfaced to the user with vault values redacted.