Observed since 20 Oct, 22:11 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
TypeScript SDK for the 0G AgenticID protocol — register, manage, and interact with on-chain AI agent identities
TypeScript SDK for 0G AgenticID — deploy, call, and build on-chain reputation for autonomous AI agents. Built on viem.
npm install @0gfoundation/0g-agenticid-sdk viem
What do you want to do?
ag.reputationThis README is the interface reference — signatures and shapes. The Guide explains the reasoning behind each call; the repo has protocol docs + source.
One entry point, two namespaces + top-level ops.
| Surface | What |
|---|---|
ag.agent | agent lifecycle (deploy / clone / transfer), reads (owner, agentSeal, iData…), agent-seal gas top-up |
ag.reputation | capture a TEE-signed serve-proof, verify it, submit/read on-chain feedback |
ag.ack() / ag.ackStatus() | acknowledge the TEE trust-root component set (attestor + kms + sandbox-provider) |
ag.deposit() / ag.getBalance() | fund / read the prepaid sandbox balance (pay-as-you-go runtime) |
ag.getBalanceDetail() / ag.requestRefund() / ag.withdrawRefund() | full 3-value account view; withdraw prepaid funds (time-locked two-step) |
Backends (AgenticID / the canonical ERC-8004 ReputationRegistry + VerifiedFeedbackRegistry / TappRegistry / SandboxServing contracts + the attestor's HTTP endpoints) are hidden behind the facade — you don't decide which call is on-chain and which is HTTP.
import { AgenticID } from '@0gfoundation/0g-agenticid-sdk';
const ag = await AgenticID.fromAttestor('https://agenticid.0g.ai', {
account: process.env.PRIVATE_KEY as `0x${string}`, // omit for read-only
});
fromAttestor reads the attestor's GET /config and fills all contract addresses, RPC, and appIds — the URL alone pins the environment (0G AgenticID is ERC-8004 identity + reputation, plus ERC-7857 sealed intelligent data). Manual construction (new AgenticID({ addresses, … })), overrides, custom walletClient, and the full config shape are covered in the setup guide.
What you pass grows with what you do:
| You want to… | Pass |
|---|---|
| read the chain (owner, balance, agent data) | addresses only (RPC has a built-in default) |
| write to the chain (deploy, transfer, feedback) | + account |
| manage containers / see deploy status | + attestorUrl |
Bindings used in the snippets below:
const agentId = 33n; // ERC-7857 tokenId (ERC-721-compatible)
const owner = '0xAaAa...'; // the address that owns the agent
const buyer = '0xBbBb...'; // the address leaving feedback (attribution is msg.sender)
ag.agent — lifecycle + reads
iDatais the encrypted content minted for an agent (persona, framework binding).deployassembles it fromname/description/inferenceviadefaultIData(); pass your owniDatafor full control (details in the guide).
import { parseEther } from 'viem';
// deploy — signs the deploy + sandbox-create envelopes, POSTs them to the attestor
const params = {
name: 'Sage',
description: 'a helpful agent',
framework: 'openclaw', // a name from GET /config's frameworks[]; SDK resolves its sealed image
inference: { provider: '0g-compute', model: 'claude-sonnet-5' }, // optional (see listModels())
sandbox: { apiKey: process.env.AGENT_API_KEY }, // sealedImage optional — pass only to pin a specific image
};
// deploy/clone are ASYNC (submit → mint → provision). `wait` picks how far to block:
// omit → accepted · 'minted' → +agentId · 'running' → +url
const { sealId, agentSealAddr, agentId } = await ag.agent.deploy(params, { wait: 'minted' });
const { url } = await ag.agent.deploy(params, { wait: 'running' });
await ag.agent.deploy({ ...params, sandbox: undefined }, { wait: 'minted' }); // mint-only, no container
// or fire-and-forget:
const dep = await ag.agent.deploy(params); // → { sealId, agentSealAddr }
const id = await ag.agent.waitForMint(dep.sealId); // → 34n; throws on phase=failed or timeout
// clone — source owner mints a copy for another owner (attestor re-keys the sealed data)
const cl = await ag.agent.clone({ sourceAgentId: agentId, targetOwner: newOwner }, { wait: 'minted' });
// → { sealId, agentSealAddr, agentId }
// clone, contract mode (issue #133 marketplace fork) — the BUYER (connected
// wallet == targetOwner) signs an intent binding the policy context, and the
// source owner's on-chain ICloneAuthorizer decides:
const fork = await ag.agent.clone(
{ sourceAgentId, targetOwner: buyer, authorization: { authData: purchaseReceipt } },
{ wait: 'minted' },
);
// authorization.authorizer is optional — read live via cloneAuthorizerOf when
// omitted. See the GUIDE's marketplace-fork walkthrough.
// transfer — ERC-7857 (old container reaped async by the indexer; don't gate on phase right after)
await ag.agent.transferFrom(owner, newOwner, agentId); // → "0x…"
await ag.agent.safeTransferFrom(owner, newOwner, agentId); // → "0x…"
// reads
await ag.agent.ownerOf(agentId); // → "0x…"
await ag.agent.getAgentSeal(agentId); // → "0x…" the agent's on-chain signing key (address)
await ag.agent.getSealId(agentId); // → "0x…" bytes32 seal id (== deploy's sealId)
await ag.agent.getAgentIdBySealId(sealId); // → 33n reverse lookup
await ag.agent.isSealIdBound(sealId); // → true
await ag.agent.intelligentDatasOf(agentId); // → [{ dataDescription /* JSON string */, dataHash }, …]
await ag.agent.sealedKeysOf(agentId); // → ["0x04…", …] one sealed key per iData entry
await ag.agent.balanceOf(owner); // → 5n agents owned by `owner`
// agent-seal gas — the agent's own on-chain-write budget
await ag.agent.topUpAgentSeal(await ag.agent.getAgentSeal(agentId), parseEther('0.01')); // → "0x…"
// runtime lifecycle (owner-signed; on-chain identity preserved)
await ag.agent.stop(sealId, sandboxId);
await ag.agent.start(sealId, sandboxId); // resume a STOPPED container
await ag.agent.start(sealId, { apiKey }); // FIRST provision of a mint-only agent
await ag.agent.reset(sealId, { framework, apiKey }); // recreate an EXISTING container
await ag.agent.retry(sealId, { apiKey }); // resume a 'failed' deploy (do NOT redeploy — that orphans the mint)
await ag.agent.listDeployments(); // public listing; owner/sandboxId/lastProvisionError come back null
await ag.agent.listMyDeployments(); // owner-signed; full detail
// row → { agentId, sealId, phase, sandboxId, url, owner, name, createdAt, lastProvisionError }
// phase: 'deploying' | 'running' | 'stopped' | 'offline' | 'failed'
// settings — the owner's configuration document (which model the agent thinks
// with, and how hard). Stored by the attestor as an OPAQUE blob and applied by
// the container, so widening the vocabulary never needs an attestor change.
// Owner-signed BOTH ways — it is on no listing and on no public row.
const { settings, version } = await ag.agent.getSettings(sealId);
// settings → { provider, model, thinking, framework } | null (null = never configured)
// version → the number to write back against (0 when there is no document)
await ag.agent.setSettings(sealId, { ...(settings ?? {}), model: '0gm-1.0-35b-a3b', thinking: 'high' }, {
baseVersion: version, // compare-and-swap: a stale one is refused, not applied
onWarn: console.warn, // advisory 0G-router catalog check, run before signing
}); // → { version }
setSettings writes the WHOLE document (spread the current one to change a field) and is owner-signed: the attestor verifies the signer against the live on-chain owner, so a seller cannot keep configuring an agent they sold — and the same signature gates the READ, because the document is the owner's, not public. It is deliberately not on chain — configuration is re-suppliable, where the agent's memory is not. The container picks it up on its next boot (reset to apply immediately) and reports success, which promotes it to last-known-good; a container that keeps coming back WITHOUT ever confirming is handed that last-known-good document instead, so a document that prevents boot costs a boot rather than the agent. framework is an opaque JSON section for that framework's own knobs — the platform neither parses nor validates it. onWarn is advisory only: a model absent from the 0G router catalog is reported, never blocked (a framework built-in is legitimately absent, and the container is the real gate).
Every write is a compare-and-swap on baseVersion — the version you read before editing (0 = "no document yet"). If the stored version has moved on, the write is refused with SettingsConflictError, which carries the document that is actually there. Re-read, re-apply, write again; do not loop on it, or the other writer's change is the one that disappears.
The lifecycle guide covers async phases, transfer teardown, mint-only vs first-provision, apiKey handling, and failure reasons.
start / reset / retry seal apiKey to the agent (SEAL_SECRET_ENV) when the attestor advertises secret_env_scheme, so the owner's wallet prompt never shows it. A one-shot deploy with sandbox still signs it in clear; to avoid that, deploy without sandbox, then start(sealId, { apiKey }). When your server supplies the key and a user signs, seal it on the server with agent.sealApiKey(sealId, { owner, apiKey }) and pass the result to the browser's start / reset / retry as sealedSecretEnv, so the key never reaches the browser. See the guide for secretEnv: 'sealed' | 'plaintext' and SecretEnvRefusedError.
Three IDs, same agent:
| Identifier | What it is | Used by |
|---|---|---|
agentId (bigint) | ERC-7857 tokenId (ERC-721-compatible) | reads, transfer, clone, runtimeCosts |
sealId (bytes32) | seal-binding hash; the attestor's deployment key | stop/start/reset/retry, waitForMint |
agentSeal (address) | the agent's own wallet | topUpAgentSeal, serve-proof signer checks |
Convert freely: getSealId / getAgentIdBySealId / getAgentSeal.
ag.reputation — serve-proof + feedbackThe sealed proxy stamps X-Agent-Proof on the agent's /api/* services (its outward, attributable surface) — not on the owner↔agent chat/UI routes (signing an owner-authenticated channel would be self-dealt reputation). capture reads that header; on-chain attribution stays msg.sender, and each proof is bound to a single submitter (the only address allowed to redeem it). Feedback is stored in the official canonical ERC-8004 Reputation Registry (every 8004 reader sees it natively); the local VerifiedFeedbackRegistry records which entries were proof-backed — giveFeedback bundles the two calls. The reputation guide explains why.
import { keccak256, toBytes } from 'viem';
// 1. call one of the agent's signed /api/* services and capture the proof (public handle needs no wallet)
const agent = await ag.agent.connect(agentId);
const { response, proof } = await agent.fetchWithProof('/api/summarize', {
method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ q: 'hi' }),
});
// proof → { agentId, submitter, timestamp, deadline, taskHash, dataHashes, frameworkHash, signature } | null
// low-level escape hatch: ag.reputation.capture(() => fetch(…))
// 2. verify before spending gas
await ag.reputation.verifyProof(proof);
// → { ok, signerMatches, notExpired, dataOnChain, reasons }
// 3. submit feedback — canonical 8004 write (attribution = msg.sender) + TEE
// verification mark, bundled. On 7702-enabled environments (a feedbackBatcher
// is advertised) both land in ONE atomic type-4 tx; otherwise two sequential
// txs. An owner CANNOT attest feedback on their own agent (the verified
// registry rejects it).
const fb = await ag.reputation.giveFeedback({ agentId, value: 5n, serveProof: proof });
// → { feedbackTx (mined), attestTx (pending — wait it), feedbackIndex }
await ag.reputation.waitForTransaction(fb.attestTx);
// optional params: valueDecimals / tag1 / tag2 / endpoint / feedbackURI / feedbackHash
// escape hatch: attestFeedback(agentId, idx, proof) marks an entry submitted separately
// canonical reads (raw 8004 feedback — permissionless, verified or not)
await ag.reputation.getLastIndex(agentId, buyer); // → 2n
await ag.reputation.readFeedback(agentId, buyer, idx); // → { value, valueDecimals, tag1, tag2, isRevoked }
await ag.reputation.getSummary({ agentId }); // → { count, summaryValue, summaryValueDecimals }
await ag.reputation.readAllFeedback({ agentId }); // filters: clientAddresses / tags / includeRevoked
await ag.reputation.getClients(agentId); // → ["0x…", …]
await ag.reputation.getResponseCount(agentId, buyer, idx, [owner]); // → 1n
// verified reads (TEE marks — intersect these with the canonical entries)
await ag.reputation.isVerified(agentId, buyer, idx); // → true
await ag.reputation.getVerifiedSummary({ agentId }); // proof-backed entries only
await ag.reputation.getVerifiedClients(agentId); // clients with ≥1 verified entry
await ag.reputation.getVerifiedIndexes(agentId, buyer); // → [1n, …]
await ag.reputation.getServeData(agentId, buyer, idx); // → { dataHashes, frameworkHash }
// task-receipt opening: pass `task` (method/uri/body hashes/status of the rated
// interaction) to giveFeedback and the contract verifies it against the proof's
// taskHash, recording the URI as the entry's TEE-verified endpoint:
await ag.reputation.getVerifiedEndpoint(agentId, buyer, idx); // → "/hello" ("" if unrevealed)
await ag.reputation.getVerifiedSummaryForEndpoint(agentId, '/hello'); // per-interface verified aggregate
// owner responds to an entry; the client who left it can revoke
await ag.reputation.appendResponse({ agentId, clientAddress: buyer, feedbackIndex: idx, responseURI: 'ipfs://…', responseHash: keccak256(toBytes('thanks')) });
await ag.reputation.revokeFeedback(agentId, idx); // → "0x…"
Two distinct balances: ag.deposit() funds the prepaid sandbox balance (compute runtime, pay-as-you-go), ag.agent.topUpAgentSeal() funds the agentSeal's own gas (the agent's on-chain activity). The top-level-ops guide explains both in full.
import { parseEther } from 'viem';
// trust-root acknowledgment (TappRegistry — spans attestor + kms + sandbox-provider)
await ag.ackStatus(owner); // → { allAcked, missing }
await ag.components(owner); // → per-component detail
const ackTx = await ag.ack(); // → "0x…", or null if nothing was missing
if (ackTx) await ag.waitForTransaction(ackTx);
// prepaid sandbox balance (SandboxServing)
await ag.getBalance(); // → wei
const depositTx = await ag.deposit({ amountWei: parseEther('0.5') });
await ag.waitForTransaction(depositTx);
deploy()/clone() preflight both prerequisites (all components acked + balance ≥ 0.1 OG). Bare writes (ack/deposit/topUpAgentSeal) return before mining — await ag.waitForTransaction(tx) before reading state back; giveFeedback mines the canonical write internally and returns a pending attestTx to wait on (more in the guide).
You don't pass a sealed image — the SDK resolves it from your framework via GET /config's frameworks[] (each { name, image? }). iData is an array of { role, plaintext, extra }; deploy builds a framework + persona entry by default. The iData guide has the shapes and rules (WYSIWYS, persona is a one-shot seed, what the attestor validates).
await ag.agent.listModels(); // → ['claude-opus-4-8', …] the 0G router's live catalog
await ag.agent.estimateCosts(); // pricing + cost/min (+ your balance/runway if account set)
await ag.agent.runtimeCosts(agentId); // + that agent's evolution-gas balance
// → { prepaidBalanceWei, sealGasWei,
// pricing: { pricePerCPUPerMin, pricePerMemGBPerMin, createFee },
// costPerMinWei, estimatedRunwayMinutes }
// Pass { cpu, memGb } if the container spec differs.
ag.agent.client(agentId) resolves the URL on chain — one handle for every caller; owner ops (chat/chatStream/logs) are present only when ag holds the owner key. Multi-window apps: pass { instanceId } — the container seats ONE driving client at a time (a displaced window gets 409; claimAgent takes the seat over; callers without an id bypass the gate). See the guide.
const { hello, verification } = await ag.agent.sayHi(agentUrl);
// hello → { agent, owner, public_url, message, services, routes }
// services: { path, method, description?, input_example? } — proof-signed /api/* endpoints
// routes: { prefix, kind?, auth?, signed, description? } — framework prefixes (e.g. chat at /v1/)
const agent = await ag.agent.client(agentId); // agentId OR agentUrl — the SDK fills in the other half
agent.routes; agent.services;
if (agent.chat) { // present only with an owner key + a declared chat route
const { choices } = await agent.chat([{ role: 'user', content: 'hi' }], { model: 'openclaw' });
}
if (agent.chatStream) { // streaming variant — yields content deltas
for await (const delta of agent.chatStream(msgs, { model: 'openclaw' })) process.stdout.write(delta);
}
const r = await agent.fetch('/v1/models'); // attaches the bearer if the route asks
const { response, proof } = await agent.fetchWithProof('/api/summarize', { method: 'POST', body });
if (agent.logs) await agent.logs({ tail: 200 }); // owner-only: the agent's own process log
// Long tasks: when /hello declares a kind "responses" route, chat/chatStream
// already ride it — the turn is owned by a server-side task id, survives
// dropped connections (auto-resume by sequence number), and can be
// re-attached or stopped by id:
let taskId = '';
for await (const d of agent.chatStream!(msgs, { onTask: (id) => (taskId = id) })) process.stdout.write(d);
await agent.task!(taskId); // poll {status, output_text}
for await (const d of agent.followTask!(taskId)) process.stdout.write(d); // re-attach: replay + live
await agent.cancelTask!(taskId); // stop the turn
// opinionated shortcuts over client():
const auth = await ag.agent.authenticate(agentId); // mints the owner token up front (needs a wallet)
const pub = await ag.agent.connect(agentId); // explicit PUBLIC handle (never attaches a token)
model is the framework's own selector, not an LLM name (the LLM is fixed at deploy) — openclaw wants "openclaw". The interaction guide covers capability signalling, openclaw's token lifecycle, and the failure/retry flow.
sealAccount()), never holding a raw key.fromAttestor fills them. Exported constants: ZERO_G_TESTNET / ZERO_G_MAINNET / RPC_URL / CHAIN_ID / RECEIPT_WAIT.buildServeProofMessageHash, signServeProof, verifyServeProofSignature) and the canonical digest spec.npx 0g-agenticid — interactive shell (deploy/use agents, chat with Esc-interruptible turns); doctor / status <agent> / list subcommands with --json for scripts.msg.sender at submission; each proof also names a submitter (the redeemer). Treat proofs as sensitive regardless of transport.waitForTransaction is tuned for 0G (120s + retries); if it times out the tx likely still landed — confirm by reading state.value/summaryValue are int128, feedbackIndex is uint64 (all bigint).Download counts and popularity will appear here as the collector receives them.
This describes our collected data; it is not a security assessment.
Shared repository data · GitHub
Observed since 20 Oct, 22:11 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:23fa3e5fe796164fe9897b6bdd429db961522743d327450912f5b47d1f7eca71