Here's a scenario that plays out every day as teams ship longer-running AI agents.
At 9am, a user kicks off an agent. The agent authenticates against GitHub — gets a token, starts working. It reads repos, opens issues, drafts a PR description. Everything works.
By noon, the agent is deep into a multi-step task. It's summarizing code, running checks, waiting on an API. The token is still valid.
At 2pm, it needs to push a commit. The token is now 5 hours old. GitHub's OAuth tokens don't last forever. The push fails with a 401. The agent crashes or silently swallows the error and returns garbage.
This is the agent token-freshness problem. And it's more widespread than most teams realize until they hit it in production.
Why it's harder than it looks
The instinctive fix is: just refresh the token. And yes, you can do that. But doing it correctly at the scale of a real agent system is where things get complicated.
Problem 1: Agents capture tokens at startup
Most agent code does something like this:
// At agent initialization
const token = await getGithubToken(userId)
// Hours later, in a tool:
await github.createPR({ token, ... }) // 401
The token was valid when the agent started. It's invalid by the time the tool runs. If the token expires during a long task, there's no recovery — the agent didn't capture a refresh token, and there's no hook to re-authenticate mid-run.
Problem 2: Durable execution makes it worse
Cloudflare Durable Objects, Inngest, Temporal — any system that hibernates and resumes an agent between steps — will kill you here. The agent serializes its state (including any captured token) before hibernating. When it wakes up hours later, the token in state is stale. There's no signal that it happened.
This is not a bug in these platforms. It's the expected tradeoff: durable execution is great for resilience, but any in-memory credential is now a liability.
Problem 3: Concurrent tool calls race to refresh
When you do add refresh logic, a new problem appears: parallel tool calls. If three tools run concurrently and all detect a stale token, you get three simultaneous refresh requests to your token endpoint. Depending on your provider, this can revoke earlier tokens, trigger rate limits, or leave you with inconsistent state.
This requires single-flight deduplication — one refresh in flight, all callers waiting for the same result. It's a small piece of code that most teams don't write until after their first incident.
Problem 4: You're probably multi-provider
Real agents don't just touch GitHub. They touch GitHub and Google Drive and Slack. Each connection has its own token, its own expiry, its own refresh flow. You need this logic per-connection, not per-token. Building a generic refresh layer that works across providers is a meaningful project — not a morning's work.
What nominee does
nominee is a small library that sits between your agent and your users' third-party accounts. It solves the token lifecycle problem with one primitive: call-time token resolution.
Instead of capturing a token at agent startup, you ask nominee for a fresh token at the moment of each tool call:
import { Nominee, tokens } from 'nominee'
const nominee = new Nominee({
strategy: tokens(async ({ user, connection }) =>
db.getToken(user, connection)
)
})
// Inside any tool — called right before you need it:
const token = await nominee.token({ user: 'alice', connection: 'github' })
nominee caches the token, tracks its expiry, refreshes when needed — with single-flight deduplication. Whether the agent runs for 30 seconds or 30 hours, every tool call gets a live token.
Durable execution: transparent
Because tokens are resolved at call time, not captured at startup, hibernation and resume are transparent. The agent wakes up, calls a tool, nominee fetches a fresh token. No stale state. No 401.
We built a live demo of this: two agents race across a long session. One uses a captured token. One uses nominee. Watch what happens when the token expires. It takes about 5 seconds and is more convincing than any diagram.
Human approval: first class
nominee also handles the case where a tool call requires explicit human sign-off before it runs:
await nominee.approve({
user: 'alice',
action: 'push-to-main',
meta: { repo: 'acme/api', branch: 'main' }
})
// Blocks until alice approves or denies via webhook or push notification
// Token is refreshed at the moment of approval, not before
The agent pauses. The user gets a notification — email, phone, whatever your approval backend sends. They approve. The agent continues with a fresh token. The whole interaction is logged to an audit trail.
Five ways to handle agent tokens — honestly
nominee isn't the only approach. Here's the honest comparison:
| Approach | Token refresh | Durable resume | Single-flight | Approval | Provider-neutral |
|---|---|---|---|---|---|
| Capture at startup | ✗ | ✗ | ✗ | ✗ | ✓ |
| Roll your own refresh | ✓ | Depends | Rarely | Manual | ✓ |
| Hard-wire one IdP SDK | ✓ | Depends | Often | Varies | ✗ |
| AI SDK built-ins | ✗ | ✗ | ✗ | Partial | ✓ |
| nominee | ✓ | ✓ | ✓ | ✓ | ✓ |
Rolling your own is a legitimate choice — especially if your token lifetimes are long, your agent is short-lived, or you have exactly one provider. nominee's value compounds as run times get longer and providers multiply.
When you don't need nominee
I want to be explicit about this because I think it's important for credibility.
If your agent completes in under a minute, if you use exactly one identity provider, and if your token outlives the agent run — you probably don't need nominee. The overhead isn't worth it.
nominee is for agents that run long, hibernate and resume, touch multiple providers, or gate sensitive actions on human approval. If that's not you yet, file this away for when it is. You'll know exactly when you need it.
Works with what you already use
The core package has zero runtime dependencies. There's no account to create, no service to configure — just:
npm i nominee
For framework integration:
npm i nominee-ai # Vercel AI SDK
npm i nominee-auth0 # Auth0 Token Vault + CIBA (optional)
npm i nominee-supabase # Supabase token strategy
If you're on Cloudflare Agents or Durable Objects, the core package works directly — no adapter needed. If you're using Vercel AI SDK, nominee-ai gives you a nomineeTool wrapper that auto-injects tokens and gates on approvals.
What's next
v1.1.0 is stable and in production. What I'm working on next:
- More built-in strategies. Clerk, WorkOS, Firebase are the most-requested. If you want to contribute one, CONTRIBUTING.md has the pattern — it's about 50 lines.
- Per-framework recipes. Step-by-step integration guides for Cloudflare Agents, Vercel AI SDK, LangGraph, and Inngest.
- The durable-agent pattern post. A deeper dive into what it actually means to not serialize a token into your Durable Object — with code.
If you're building an agent that touches third-party APIs across a long session, I'd love to hear what you're running into. Find me on GitHub or open a Discussion.