Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Yellow Network Protocol app sessions for AI agents - a shared room where several agents pool funds, reallocate off-chain at machine speed, and settle one final split. Use for multiparty settlement among agents. Covers connecting, the funded-account prerequisite, creating a session with participants + weights + quorum, deposit, operate, withdraw, close, and the trust boundary. Grounded in the official @yellow-org/sdk lifecycle example.
.claude/skills/internet-court-yellow-settlement-room/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 10% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 27% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 68% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 51% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 127% | 0% |
Use this skill when several AI agents need to hold funds together and settle one outcome: open a shared session, each agent's balance is tracked inside it, they reallocate between themselves off-chain with no gas per step, and they co-sign the final split.
The session holds N agents, not two. A payment rail moves value from one payer to one payee; a swarm of agents settling over a rail needs a separate escrow per pair. One session settles all of them at once: one object, one deposit per agent, one final allocation. That is the shape to reach for when more than two agents have a stake in the same outcome.
This skill covers the app-session (virtual) layer only. It operates on funds that are already in an account balance at Yellow. A session opens with zero allocations, so not every participant needs funds - only a participant that makes a deposit does. Getting funds into an account is a one-time on-chain step, out of scope here - see ## Prerequisite. This skill never funds accounts; before a participant deposits, check its balance with client.getBalances(wallet), and if it is short, stop and report it.
Package: @yellow-org/sdk (v1). A complete runnable reference is the official example at github.com/layer-3/docs, examples/nitrolite-v1-lifecycle; the flow below matches it exactly, generalised from two participants to N. For method lookups, the docs MCP: npx -y @yellow-org/sdk-mcp@^1.
textEach agent runs its own client with its own key (backend: private-key based) -> agents open one app session: N participants, signature weights, quorum (opens with ZERO allocations; nobody needs funds yet) -> a depositing agent commits its OWN funds into the session (only depositors need a funded account; others can hold zero) -> agents reallocate between themselves (operate), each update co-signed to quorum -> withdraw / close: the final split releases back to channels, withdrawable on-chain
The session is an off-chain ledger hosted by the Yellow node. Its guarantee is signature-based: no agent's allocation changes without signatures meeting the quorum. It is not a trustless escrow; read ## Trust Boundary before sizing exposure.
## Roles and key separation.client.getBalances(wallet) and if it is short, stop and report the shortfall. Do not attempt deposit, approveToken, or transfer to self-fund.## Weights and Quorum.A session is created with zero allocations, so not every participant needs funds. Only a participant that makes a deposit needs a funded account balance at Yellow for the asset. (An account is backed by an on-chain state channel, but you can treat it as the participant's balance.) That balance is the ceiling on what a depositor can commit and the most it can lose. Check a depositor's balance before it deposits:
tsconst balances = await client.getBalances(wallet); // account balances at Yellow, per asset
Funding is a one-time on-chain operation, done once before any session and out of scope here. The funding calls, for reference, are:
tsawait client.setHomeBlockchain(asset, chainId); await client.approveToken(chainId, asset, amount); await client.deposit(chainId, asset, amount); await client.checkpoint(asset); // finalizes the deposit on-chain
For the exact funding sequence and any Node-specific transaction setup, see the quickstart at docs.yellow.org/nitrolite/build/getting-started/quickstart and the official nitrolite-v1-lifecycle example.
tsimport { Client, createSigners, withBlockchainRPC } from '@yellow-org/sdk'; const signers = createSigners(privateKey); // 0x-prefixed 32-byte hex const client = await Client.create( wsURL, // sandbox: wss://nitronode-sandbox.yellow.org/v1/ws signers.stateSigner, signers.txSigner, withBlockchainRPC(chainId, rpcURL), );
There is no login handshake; authorization is per-call, from the signatures inside each payload. Each agent runs its own client with its own key, in its own process. The examples below show several signers together for readability; in a real agent-to-agent deployment each agent constructs only its own signer and signs the shared state hash with its own key.
Agent-to-agent means the keys are distributed. You cannot take several agents' private keys into one client. Model these roles:
createAppSession / submitAppSessionDeposit / submitAppState).The cross-process pattern uses the same methods as the single-process code below:
ts// Proposer (any agent or a coordinating server): build the state, pack the hash. const hash = packAppStateUpdateV1(update); // portable 0x string, safe to send over the wire // Each signer, in its OWN process with its OWN key: const mySig = await new AppSessionWalletSignerV1(new EthereumMsgSigner(myKey)).signMessage(hash); // ...return mySig to the proposer over your own transport (HTTP, queue, etc.) // Submitter: gather signatures until summed weight meets quorum, then submit once. await client.submitAppState(update, [sigFromA, sigFromB, sigFromC]);
The protocol carries no transport for moving the hash out and the signatures back; that is the integrator's to build. A common topology: a Nitronode, agents connecting to a coordinating server (an app), and agents transacting agent-to-agent through shared sessions. The single-process code below co-locates keys only for readability and local testing; do not ship it that way.
tsimport { AppSessionWalletSignerV1, EthereumMsgSigner, packCreateAppSessionRequestV1, type AppDefinitionV1, } from '@yellow-org/sdk'; // One session signer per participant, a plain wallet signer (type 0xa1). // LOCAL TEST ONLY: holding pkA, pkB, pkC in one process is a shortcut for a // smoke test or a custodial server. In a real deployment each agent builds ONLY // its own signer, in its own process, from its own key (see Roles and key // separation above). Session keys are an optional friction-reducer, omitted here. const signerA = new AppSessionWalletSignerV1(new EthereumMsgSigner(pkA)); const signerB = new AppSessionWalletSignerV1(new EthereumMsgSigner(pkB)); const signerC = new AppSessionWalletSignerV1(new EthereumMsgSigner(pkC)); const definition: AppDefinitionV1 = { applicationId: appId, // ^[a-z0-9_-]{1,66}$ participants: [ { walletAddress: addrA, signatureWeight: 1 }, { walletAddress: addrB, signatureWeight: 1 }, { walletAddress: addrC, signatureWeight: 1 }, ], quorum: 3, // = sum of weights -> unanimous (the safe default) nonce: BigInt(Date.now()) * 1_000_000n + BigInt(Math.floor(Math.random() * 1_000_000)), }; const createPayload = packCreateAppSessionRequestV1(definition, sessionData); const created = await client.createAppSession(definition, sessionData, [ await signerA.signMessage(createPayload), await signerB.signMessage(createPayload), await signerC.signMessage(createPayload), // creation must itself meet quorum ]); // created.appSessionId, created.version, created.status
The participant set is immutable after creation; no agent can be added later. Creation must meet quorum, so every participant that makes up the quorum co-signs the create request.
tsconst { sessions } = await client.getAppSessions({ appSessionId }); const session = sessions[0]; // session.version, session.isClosed, session.allocations
Read this immediately before signing any update: version must be exactly session.version + 1n.
All updates share one shape; intent is a number, not a string.
tsimport { AppStateUpdateIntent, packAppStateUpdateV1, type AppStateUpdateV1 } from '@yellow-org/sdk'; import { Decimal } from 'decimal.js'; // named import: default import is not constructable under NodeNext // DEPOSIT (own endpoint): a depositor commits its OWN funds into the session. // List ONLY the depositing participant in allocations; do not add zero-value // entries for participants who are not depositing here. const deposit: AppStateUpdateV1 = { appSessionId, intent: AppStateUpdateIntent.Deposit, version: session.version + 1n, allocations: [ { participant: addrA, asset, amount: new Decimal('10') }, ], sessionData: JSON.stringify({ intent: 'fund' }), }; const dp = packAppStateUpdateV1(deposit); // The deposit state still needs signatures meeting quorum. Each agent signs the // hash with its OWN key in its OWN process; here they are shown together only for // readability. The submitter gathers the signatures and calls the node. await client.submitAppSessionDeposit( deposit, [await signerA.signMessage(dp), await signerB.signMessage(dp), await signerC.signMessage(dp)], asset, new Decimal('10'), // amount must equal the deposit allocation total );
ts// OPERATE: reallocate between participants. Per-asset totals must stay CONSTANT, // and every non-zero allocation must be restated (not a delta). const operate: AppStateUpdateV1 = { appSessionId, intent: AppStateUpdateIntent.Operate, version: /* live */ session.version + 1n, allocations: [ { participant: addrA, asset, amount: new Decimal('4') }, { participant: addrB, asset, amount: new Decimal('4') }, { participant: addrC, asset, amount: new Decimal('2') }, ], sessionData: JSON.stringify({ round: 'payout' }), }; const op = packAppStateUpdateV1(operate); await client.submitAppState(operate, [ /* signatures summing to quorum */ await signerA.signMessage(op), await signerB.signMessage(op), await signerC.signMessage(op), ]);
Withdraw (intent 2) may only decrease allocations and releases to channels. Close (intent 3) must restate the current allocation exactly, releases everything, and is terminal - never close while work or a review is outstanding. Both use submitAppState the same way.
Signatures are collected across agents, off the wire. The proposer builds the state and hash; each agent signs that hash with its own key in its own process; the submitter gathers the signatures until summed weight meets quorum and calls the node. The protocol provides no transport for this exchange - it is the caller's responsibility. Duplicate signers count once.
signatureWeight per participant, quorum = the weight threshold a state needs to be valid.
State which regime a session is in when you design it.
State this before any agent puts value at risk. Do not soften it.
vendored/arkhai/alkahest-user) for a bilateral, one-shot deal that needs trustless on-chain escrow with a reclaim timeout. Reach for a settlement room when the deal is multiparty and many-update, which a single bilateral escrow cannot express.version must be exactly session.version + 1n. Concurrent signers collide and one update fails cleanly (the node serializes). Re-read the live version, re-collect signatures, retry. Most common friction in a busy room.submitAppSessionDeposit fails if the account is not funded (on the sandbox the node error reads no channel state to advance). This is the prerequisite, not a bug - check getBalances(wallet) first and report a shortfall.Operate that drops a non-zero allocation or whose per-asset total drifts: rejected.Close while work or a review is outstanding: terminal and unrecoverable.getBalances(wallet), and its deposit as the max it can lose. Non-depositing participants need no funds.## Trust Boundary, in plain language, before any value moves.references/agent-lifecycle.md - the full multiparty flow as runnable code, matching the official nitrolite-v1-lifecycle example.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 54,820 | 29,579 | -46% | 1 | 1 | 0% | 8,612 | 9,461 | +10% | 0 | 0 | — |
case-02 | fail→pass | 39,813 | 28,823 | -28% | 1 | 1 | 0% | 7,320 | 9,305 | +27% | 0 | 0 | — |
case-03 | fail→fail | 132,167 | 34,325 | -74% | 1 | 1 | 0% | 8,314 | 10,654 | +28% | 0 | 0 | — |
case-04 | fail→pass | 17,115 | 9,173 | -46% | 1 | 1 | 0% | 3,260 | 5,481 | +68% | 0 | 0 | — |
case-05 | fail→pass | 24,755 | 12,631 | -49% | 1 | 1 | 0% | 3,838 | 5,787 | +51% | 0 | 0 | — |
case-06 | fail→pass | 23,087 | 26,306 | +14% | 1 | 1 | 0% | 3,551 | 8,051 | +127% | 0 | 0 | — |
case-07 | fail→pass | 18,897 | 9,929 | -47% | 1 | 1 | 0% | 2,566 | 5,669 | +121% | 0 | 0 | — |
case-08 | pass→pass | 16,572 | 13,467 | -19% | 1 | 1 | 0% | 2,360 | 6,213 | +163% | 0 | 0 | — |
case-09 | fail→pass | 39,392 | 9,581 | -76% | 1 | 1 | 0% | 2,426 | 5,472 | +126% | 0 | 0 | — |
case-10 | fail→pass | 24,721 | 11,738 | -53% | 1 | 1 | 0% | 2,314 | 5,843 | +153% | 0 | 0 | — |
case-11 | fail→pass | 23,948 | 5,588 | -77% | 1 | 1 | 0% | 2,366 | 4,833 | +104% | 0 | 0 | — |
case-12 | fail→pass | 28,206 | 4,959 | -82% | 1 | 1 | 0% | 5,209 | 4,541 | -13% | 0 | 0 | — |
case-13 | fail→fail | 9,399 | 4,722 | -50% | 1 | 1 | 0% | 1,285 | 4,566 | +255% | 0 | 0 | — |
case-14 | fail→pass | 11,037 | 5,235 | -53% | 1 | 1 | 0% | 1,768 | 4,621 | +161% | 0 | 0 | — |
case-15 | pass→pass | 20,698 | 6,865 | -67% | 1 | 1 | 0% | 1,643 | 5,008 | +205% | 0 | 0 | — |
case-16 | fail→fail | 26,543 | 10,376 | -61% | 1 | 1 | 0% | 2,513 | 5,477 | +118% | 0 | 0 | — |
case-17 | fail→pass | 14,302 | 8,666 | -39% | 1 | 1 | 0% | 2,275 | 5,260 | +131% | 0 | 0 | — |
case-18 | fail→pass | 14,511 | 9,473 | -35% | 1 | 1 | 0% | 2,365 | 5,353 | +126% | 0 | 0 | — |
case-19 | pass→pass | 6,527 | 6,237 | -4% | 1 | 1 | 0% | 691 | 4,670 | +576% | 0 | 0 | — |
case-20 | pass→pass | 11,470 | 5,824 | -49% | 1 | 1 | 0% | 1,700 | 4,804 | +183% | 0 | 0 | — |
case-21 | fail→pass | 12,503 | 5,219 | -58% | 1 | 1 | 0% | 2,102 | 4,760 | +126% | 0 | 0 | — |
case-22 | fail→pass | 22,478 | 19,049 | -15% | 1 | 1 | 0% | 3,431 | 6,779 | +98% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +68 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.