Connections and secrets
What the agent needs, who supplies it, and what happens when it is missing.
Declare what the agent needs
An agent says what it cannot work without, and says who supplies it. Declaring is part of the configuration; supplying is not.
await ws.agents.create({
name: 'Outreach assistant',
instructions: '...',
connections: [{ provider: 'gmail', scope: 'conversation', required: true }],
secrets: [{ name: 'APOLLO_API_KEY', scope: 'fixed', required: true }],
});scope | Who supplies it | Reaches |
|---|---|---|
fixed | you, once, on the agent | every conversation, including ones already open |
conversation | each conversation, on itself | that conversation only |
The two do not substitute for each other. Connecting an account on the agent
does not satisfy a declaration that said conversation, and a conversation cannot supply a
fixed one on its own.
Connecting an account
A connection is an account somebody signs in to, so it takes a round trip through their browser. You never see a token.
One account for everyone
const { url } = await agent.authorize('gmail');
// send them to `url`; the account attaches when they finish
await agent.connections();
// [{ provider: 'gmail', declared: true, connected: true, accountLabel: 'ops@…' }]Each of your users connects their own
const conversation = await agent.start();
if (!conversation.ready) {
const { url } = await conversation.authorize('gmail');
return redirect(url); // they sign in to their own Google
}Not "was set up once". A revoked token flips connected to false
and puts the reason in error. That is the field to show an operator, because a
connection can go bad while nobody is looking.
Supplying a secret
A secret is a value you already hold, so there is no round trip and no browser. You write it and it is gone: nothing reads it back.
await agent.bind({ secrets: { APOLLO_API_KEY: '...' } }); // fixed
await conversation.bind({ secrets: { APOLLO_API_KEY: theirKey } }); // conversationBoth merge, so supplying one key leaves the others in place.
What a conversation still needs
conversation.ready; // true when nothing REQUIRED is outstanding
conversation.requires; // [{ kind: 'connection', name: 'gmail', required: true }]ready is the answer most callers want. requires is the detail behind
it, and the two are not the same question: a conversation can be ready and still
list a requirement, because one declared required: false is something the agent
works without.
It starts even with nothing supplied, reports what is outstanding, and the agent's first reply can be the thing that asks for it. If a credential breaks mid-conversation you find out on the next turn, which is the only moment it matters.