Catalog npm
PACKAGE EXPLORERView in registry

@0gfoundation/0g-agenticid-sdk

npm

TypeScript SDK for the 0G AgenticID protocol — register, manage, and interact with on-chain AI agent identities

0gagentagenticidblockchainethereumidentityteeviem
Versions collected8
Direct dependencies6From release 0.1.7
Last observed20 Oct, 22:11 UTC

README

Current package documentation

@0gfoundation/0g-agenticid-sdk

npm license: MIT

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?

This README is the interface reference — signatures and shapes. The Guide explains the reasoning behind each call; the repo has protocol docs + source.

The API at a glance

One entry point, two namespaces + top-level ops.

SurfaceWhat
ag.agentagent lifecycle (deploy / clone / transfer), reads (owner, agentSeal, iData…), agent-seal gas top-up
ag.reputationcapture 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.

Quickstart

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

iData is the encrypted content minted for an agent (persona, framework binding). deploy assembles it from name/description/inference via defaultIData(); pass your own iData for 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:

IdentifierWhat it isUsed by
agentId (bigint)ERC-7857 tokenId (ERC-721-compatible)reads, transfer, clone, runtimeCosts
sealId (bytes32)seal-binding hash; the attestor's deployment keystop/start/reset/retry, waitForMint
agentSeal (address)the agent's own wallettopUpAgentSeal, serve-proof signer checks

Convert freely: getSealId / getAgentIdBySealId / getAgentSeal.

ag.reputation — serve-proof + feedback

The 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…"

Top-level ops

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).

iData & framework

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

Cost

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.

Interacting with a running agent

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.

More

  • Agents as owners (nested agents) — an in-container agent runs this SDK as itself over the sign socket (sealAccount()), never holding a raw key.
  • Addresses — a deployment artifact, not baked into the SDK; fromAttestor fills them. Exported constants: ZERO_G_TESTNET / ZERO_G_MAINNET / RPC_URL / CHAIN_ID / RECEIPT_WAIT.
  • Advanced — raw ABIs + serve-proof primitives (buildServeProofMessageHash, signServeProof, verifyServeProofSignature) and the canonical digest spec.
  • CLI npx 0g-agenticid — interactive shell (deploy/use agents, chat with Esc-interruptible turns); doctor / status <agent> / list subcommands with --json for scripts.

Notes

  • Serve-proof binding — attribution is msg.sender at submission; each proof also names a submitter (the redeemer). Treat proofs as sensitive regardless of transport.
  • 0G receipt timing — waitForTransaction is tuned for 0G (120s + retries); if it times out the tx likely still landed — confirm by reading state.
  • On-chain types — value/summaryValue are int128, feedbackIndex is uint64 (all bigint).

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 8

Collected releases
VersionPublishedDirect dependenciesOpen version
0.1.7Registry latest13 Oct 20266
0.1.616 Sept 20263
0.1.514 Sept 20263
0.1.410 Sept 20263
0.1.37 Sept 20263
0.1.214 Aug 20263
0.1.111 Aug 20263
0.1.011 Aug 20263

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
0gfoundation/0g-agentic-id

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 22:11 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:23fa3e5fe796164fe9897b6bdd429db961522743d327450912f5b47d1f7eca71