Connect to a vault
Deploying makes a version available. Nothing runs on a vault’s data until the vault owner connects the agent. A connection is a signed, scoped, revocable agreement between the vault and the agent: the owner signs it with their wallet, and the vault records it with the settings they chose and the code they consented to.
What a connection holds
| Part | Meaning |
|---|---|
| Scopes | The vault scopes the agent may read and publish in. Each must be in the manifest’s requiredScopes (default default). |
| Settings | Values for the agent’s declared settings, validated against its schema. |
| Bundle pin | The bundle of the version the owner consented to. Dispatch runs exactly that code. |
| Ceiling | Optional spend limits. See Spend limits. |
The connection also provisions the agent’s cubbies in the vault.
Connect from ROC
| Where | How |
|---|---|
| Workflow Builder | Deploy connects the workflow, and any agent it calls, to your vault. Your wallet may ask for your passkey. |
| Sandbox | Pick the agent and a vault; the pill reads agent not connected with a Connect button. When connected, it reads agent connected, with a control to disconnect. |
| A widget | WidgetRuntime.connectAgent() connects the widget’s agent for the signed-in reader. |
Reconnect to pick up new code
A connection pins the bundle the owner consented to. Pushing and deploying a new version does not change the code a connected vault runs; the vault keeps running the pinned bundle until the owner reconnects. Routing, params, and the engagement list do follow the deployment; only the code is pinned.
When the code has changed, ROC shows a prompt: “This agent’s code changed since you connected it (was …, now …). Reconnect to the new version?” with Reconnect and Not now. Reconnect signs the consent for the new bundle.
LLM agents have no bundle and pin nothing.
Connect with the vault SDK
import { VaultSDK, CereWallet } from "@cef-ai/vault-sdk";
const sdk = new VaultSDK({ endpoint: vaultApiUrl, garEndpoint: garUrl, // required for agents.connect signer: await CereWallet.fromMnemonic(mnemonic), // or KeypairWallet.fromSeed(seed)});
const vault = await sdk.vault.ensure();const connection = await vault.agents.connect({ agentId: "<agentServicePubkey>:<alias>", scope: "default", // or scopes: ["default", "research"] settings: { apiKey: "…" }, ceiling: { gpuUnits: 500, a2aTokens: 200000 }, bundleCid: reviewedBundleCid, // the code the owner reviewed});| Input | Required | Meaning |
|---|---|---|
agentId |
yes | <agentServicePubkey>:<alias>. The pubkey is taken from the prefix unless you pass agentServicePubkey. |
scope or scopes |
one of them | The scopes to connect into. |
settings |
no | Values for the agent’s settings schema. |
ceiling |
no | Spend limits, written together with the connection. |
bundleCid |
no | The bundle the owner consented to. Required when reconnecting to a different bundle. |
connect signs one consent agreement covering every requested scope, submits it to the agreement registry, and creates the connection. The vault reads the manifest from the registry itself; you never send it.
Reconnect after a new version
import { BundleChangedError, ReconsentRequiredError } from "@cef-ai/vault-sdk";
try { await vault.agents.connect({ agentId, scope: "default" });} catch (e) { if (e instanceof ReconsentRequiredError) { // Show the owner the new code (e.currentCid), then: await vault.agents.connect({ agentId, scope: "default", bundleCid: e.currentCid }); } else if (e instanceof BundleChangedError) { // A new version landed after the owner reviewed; show e.currentCid and ask again. } else throw e;}| Error | Code | Meaning |
|---|---|---|
ReconsentRequiredError |
RECONSENT_REQUIRED |
The reconnect would run different code than the owner consented to, and no bundleCid was given. Nothing changed. |
BundleChangedError |
BUNDLE_CHANGED |
The bundleCid you named is no longer the agent’s current bundle. Nothing was stored. |
Manage a connection
const all = await vault.agents.list();const one = await vault.agents.get(agentId); // status, scopes, bundle, ceilingawait one.update({ apiKey: "…" }); // new settingsawait one.setCeiling({ gpuUnits: 1000 }); // new limitsawait one.disconnect();Disconnecting keeps the cubby data.
Spend limits
A connection can carry two limits. Blank or 0 means no limit.
| ROC label | SDK field | Counts |
|---|---|---|
| Compute limit — metered GPU units | gpuUnits |
GPU units the platform measured. Decimals allowed. |
| Token limit — agent-reported | a2aTokens |
Tokens the agent reports about itself. Whole numbers. A stop signal, not a measured cost. |
Once the connection’s total reaches a limit, new tasks for that agent in that vault are refused. Spend is counted when a task finishes, so a limit stops the next run after it is crossed, not a run in progress.
In ROC, set limits in the deployment dialog’s Execution rules section: pick the vault, enter the limits, and click Save limits. Limits belong to the connection, not to the deployment revision, so the agent must already be connected to that vault.
Errors
| Code | Cause | Fix |
|---|---|---|
MANIFEST_INVALID |
A requested scope is not in requiredScopes, or the agent id does not match the manifest. |
Connect into a declared scope, or push a version that declares it. |
MANIFEST_NOT_FOUND |
No manifest for that agent id in the registry. | Check the id and that the agent was pushed. |
SETTINGS_SCHEMA_MISMATCH |
Settings do not satisfy the schema. | Fix the values. |
AGENT_ALREADY_CONNECTED |
A connection already exists. | Use the existing connection. |
GAR_MISSING, GAR_EXPIRED |
No valid consent agreement. | Connect again to sign a new one. |
CUBBY_PROVISION_FAILED |
A cubby migration failed. | Fix the migration in a new version. |
All codes: Errors.