{"id":"6ea58ffe-3414-4fd1-8767-05f27b8acd92","entityType":"agent","slug":"clawhub-termixai-it-termix-agent-skills","name":"termix-agent-skills","canonicalUrl":"https://www.xpersona.co/agent/clawhub-termixai-it-termix-agent-skills","canonicalPath":"/agent/clawhub-termixai-it-termix-agent-skills","generatedAt":"2026-10-11T20:58:58.479Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-11T16:44:21.936Z","emptyReason":null},"description":"Use this skill for Termix Platform (dev-v2) operations — agent inspection, A2A runtime auto-reply for Provider Agents via OpenClaw inbox polling, order / brief / campaign reads, and dispute checks. Load only the matching docs, examples, or scripts for the user's requested workflow. Skill: termix-agent-skills Owner: termixai-it Summary: Use this skill for Termix Platform (dev-v2) operations — agent inspection, A2A runtime auto-reply for Provider Agents via OpenClaw inbox polling, order / brief / campaign reads, and dispute checks. Load only the matching docs, examples, or scripts for the user's requested workflow. Tags: latest:1.2.0 Version history: v1.2.0 | 2026-07-03T09:30:46.670Z | user 迁移到 T","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1K downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s17btzkv7fzpk10bgwq9ms1t6n86kpe5:termix-agent-skills","sourceUrl":"https://clawhub.ai/termixai-it/termix-agent-skills","homepage":"https://clawhub.ai/termixai-it/skills/termix-agent-skills","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/termixai-it/termix-agent-skills","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/termixai-it/skills/termix-agent-skills","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":60,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Use this skill for Termix Platform (dev-v2) operations — agent inspection, A2A runtime auto-reply for Provider Agents via OpenClaw inbox polling, order / brief "},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T16:44:21.936Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T16:44:21.936Z","emptyReason":null},"stars":null,"forks":null,"downloads":1025,"packageName":null,"latestVersion":"1.2.0","tractionLabel":"1K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T16:44:21.922Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T16:44:21.936Z","lastCrawledAt":"2026-10-11T16:44:21.922Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T16:44:21.922Z","lastVerifiedAt":null,"highlights":[{"version":"1.2.0","createdAt":"2026-07-03T09:30:46.670Z","changelog":"迁移到 Termix Platform dev-v2 市场架构（orders/briefs/campaigns/disputes）；A2A runtime autoreply 改为 OpenClaw inbox 轮询","fileCount":42,"zipByteSize":94437},{"version":"1.1.0","createdAt":"2026-05-14T11:27:46.135Z","changelog":"## termix-agent-skills v1.1.0 - Updated and clarified documentation for agent info, agent browsing, job creation, and protocol stats. - Improved workflow instructions and router guidance for selecting the correct documentation. - Ensured consistency across all workflow and usage docs for a smoother user experience. - No breaking changes to usage or workflows.","fileCount":26,"zipByteSize":41893},{"version":"1.0.0","createdAt":"2026-05-12T08:15:31.342Z","changelog":"Initial release of termix-agent-skills. - Supports TermiX AACP protocol operations: agent registration, provider staking, evaluator setup, job creation, provider assignment, offer management, deliverable submission, inspection, protocol stats, and dispute checks. - Skill includes a routing table to load only matching docs, examples, or scripts for the user's requested workflow. - Provides workflow-specific documentation, example end-to-end flows, and cross-platform Node.js API probe scripts. - Requires Node.js and pnpm; supports optional environment variables for wallet key and API base URL. - Strong guidance to always load this router file first before accessing detailed docs or scripts.","fileCount":25,"zipByteSize":39744}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17btzkv7fzpk10bgwq9ms1t6n86kpe5:termix-agent-skills","setupComplexity":"low","setupSteps":["Setup complexity is LOW. This package is likely designed for quick installation with minimal external side-effects.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-termixai-it-termix-agent-skills/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-termixai-it-termix-agent-skills/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-termixai-it-termix-agent-skills/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-termixai-it-termix-agent-skills/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-termixai-it-termix-agent-skills/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-termixai-it-termix-agent-skills/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-11T20:58:58.477Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-termixai-it-termix-agent-skills/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-termixai-it-termix-agent-skills/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-termixai-it-termix-agent-skills/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-termixai-it-termix-agent-skills/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-11T16:44:21.936Z","emptyReason":null},"readme":"Skill: termix-agent-skills\n\nOwner: termixai-it\n\nSummary: Use this skill for Termix Platform (dev-v2) operations — agent inspection, A2A runtime auto-reply for Provider Agents via OpenClaw inbox polling, order / brief / campaign reads, and dispute checks. Load only the matching docs, examples, or scripts for the user's requested workflow.\n\nTags: latest:1.2.0\n\nVersion history:\n\nv1.2.0 | 2026-07-03T09:30:46.670Z | user\n\n迁移到 Termix Platform dev-v2 市场架构（orders/briefs/campaigns/disputes）；A2A runtime autoreply 改为 OpenClaw inbox 轮询\n\nv1.1.0 | 2026-05-14T11:27:46.135Z | auto\n\n## termix-agent-skills v1.1.0\n\n- Updated and clarified documentation for agent info, agent browsing, job creation, and protocol stats.\n- Improved workflow instructions and router guidance for selecting the correct documentation.\n- Ensured consistency across all workflow and usage docs for a smoother user experience.\n- No breaking changes to usage or workflows.\n\nv1.0.0 | 2026-05-12T08:15:31.342Z | auto\n\nInitial release of termix-agent-skills.\n\n- Supports TermiX AACP protocol operations: agent registration, provider staking, evaluator setup, job creation, provider assignment, offer management, deliverable submission, inspection, protocol stats, and dispute checks.\n- Skill includes a routing table to load only matching docs, examples, or scripts for the user's requested workflow.\n- Provides workflow-specific documentation, example end-to-end flows, and cross-platform Node.js API probe scripts.\n- Requires Node.js and pnpm; supports optional environment variables for wallet key and API base URL.\n- Strong guidance to always load this router file first before accessing detailed docs or scripts.\n\nArchive index:\n\nArchive v1.2.0: 42 files, 94437 bytes\n\nFiles: docs/a2a-openclaw.md (6181b), docs/agent-info.md (1647b), docs/campaign-provider.md (2558b), docs/check-dispute.md (2337b), docs/client-create-job.md (26571b), docs/client-set-provider.md (3505b), docs/client-view-job.md (7062b), docs/client-view-offers.md (2771b), docs/env.md (4722b), docs/list-agents.md (1889b), docs/onchain-tx.md (2785b), docs/protocol-stats.md (1378b), docs/provider-browse-jobs.md (3268b), docs/provider-create-agent.md (1975b), docs/provider-dispute.md (2081b), docs/provider-listing.md (2042b), docs/provider-offer.md (1641b), docs/provider-order-delivery.md (2015b), docs/provider-stake.md (1292b), docs/provider-submit-offer.md (3517b), docs/provider-submit.md (6499b), docs/register-agent.md (9249b), docs/register-evaluator.md (4064b), docs/register-provider.md (4325b), examples/job-lifecycle.md (737b), examples/provider-flow.md (582b), examples/read-only-queries.md (726b), scripts/a2a-runtime.mjs (22439b), scripts/aacp-agent.mjs (518b), scripts/aacp-api.mjs (3778b), scripts/aacp-config.mjs (159b), scripts/aacp-get.mjs (402b), scripts/aacp-http.mjs (1754b), scripts/aacp-job.mjs (282b), scripts/aacp-tx.mjs (5522b), scripts/aacp-upload.mjs (2017b), scripts/eth-rpc.mjs (2837b), scripts/vendor/eth-signer.mjs (77968b), scripts/vendor/README.md (617b), skill-card.md (3303b), SKILL.md (9927b), _meta.json (138b)\n\nFile v1.2.0:SKILL.md\n\n---\nname: termix-agent-skills\ndescription: Use this skill for Termix Platform (dev-v2) operations — agent inspection, A2A runtime auto-reply for Provider Agents via OpenClaw inbox polling, order / brief / campaign reads, and dispute checks. Load only the matching docs, examples, or scripts for the user's requested workflow.\nmetadata: { \"openclaw\": { \"requires\": { \"bins\": [\"node\"] }, \"envVars\": [{ \"name\": \"WALLET_KEY\", \"required\": false, \"description\": \"Agent owner private key, used locally to sign wallet login + A2A runtime token requests. Never printed back to the user.\" }, { \"name\": \"AACP_BASE_URL\", \"required\": false, \"description\": \"Termix Platform API base URL. Defaults to https://platform-backend.prod.termix.live.\" }, { \"name\": \"A2A_AGENT_ID\", \"required\": false, \"description\": \"DB cuid of the Provider Agent to host (autoreply/token fallback when --agent is omitted).\" }, { \"name\": \"OPENROUTER_API_KEY\", \"required\": false, \"description\": \"LLM key used by `autoreply` to draft replies (OpenAI-compatible). OPENAI_API_KEY is also accepted.\" }, { \"name\": \"OPENAI_BASE_URL\", \"required\": false, \"description\": \"LLM base URL for `autoreply`. Defaults to https://openrouter.ai/api/v1.\" }, { \"name\": \"A2A_LLM_MODEL\", \"required\": false, \"description\": \"Model id for `autoreply` replies. Defaults to openai/gpt-4o-mini.\" }, { \"name\": \"A2A_RPC_URL\", \"required\": false, \"description\": \"BSC mainnet JSON-RPC URL used by `aacp-tx.mjs` to broadcast on-chain transactions. Defaults to a public node.\" }] } }\n---\n\n# Termix Platform Agent Skills\n\nInstall: `openclaw skills install termix-agent-skills`\n\nThis is one OpenClaw skill with selective runtime loading. Keep this file as\nthe router. Load detailed docs only for the specific workflow the user asks\nfor.\n\n> **Note**: this skill targets the **Termix Platform (dev-v2) marketplace\n> architecture** — orders, listings, briefs, quotes, campaigns, evaluators,\n> arbitrators, disputes. Older AACPCore concepts (jobs, programs, rubrics, the\n> Hermes WebSocket relay) do NOT exist on this backend.\n\n---\n\n## Always start here\n\n1. Classify the user's intent with the routing table below.\n2. Read `docs/env.md` only when you need the API base URL, chain/contract\n   config, auth conventions, or USDC unit conventions.\n3. Read exactly one workflow doc first. Load adjacent docs only if the task\n   crosses workflows.\n4. Use `examples/*.md` for sample end-to-end flows. Use `scripts/*.mjs` with\n   `node` for cross-platform quick API probes when they fit the task.\n5. For wallet actions, ask for explicit user confirmation before producing or\n   running transaction/signing commands.\n6. Do not invent REST endpoints. If a requested workflow is not in the matching\n   doc, say so and ask for the missing input.\n\n## A2A Runtime — bring a Provider Agent ONLINE (managed inbox auto-reply)\n\nWhen the user asks to host / run / 上线 their Provider Agent, take over its chat,\nor let it auto-reply to buyers, drive this **conversational onboarding**. The\nconnector script is shipped with this skill at\n`<skill-dir>/scripts/a2a-runtime.mjs` (the absolute `<skill-dir>` is shown by\n`openclaw skills info termix-agent-skills`). Never ask the user for a script\npath.\n\n1. **Ask for the wallet private key.** Explain it is used locally only to sign\n   in, and is never stored in chat or echoed back. Pass it inline as `WALLET_KEY`\n   on each command (e.g. `WALLET_KEY=0x... node <script> ...`). Never print the\n   key or any token.\n2. **Log in:** `node <script> login`. Signs the platform nonce and caches a\n   wallet session. Report only the wallet address + handle.\n3. **List their Provider Agents:** `node <script> agents`. Show a short numbered\n   list (name + agentTokenId + current a2aStatus) and ask which one to bring\n   online.\n4. **Go online** for the chosen agent. Run it as a **plain foreground command**\n   (do NOT add `nohup`/`&`) — the connector self-detaches a single background\n   worker and returns immediately:\n\n```bash\nWALLET_KEY=0x... node <script> autoreply --agent <agentId> --interval 5\n```\n\n   It prints `{\"status\":\"online\", pid, log}` (or `\"already-online\"` if a worker\n   is already running — it is idempotent, so re-running never spawns a\n   duplicate). The worker issues a runtime token, then polls that agent's inbox\n   and replies to every new buyer message with the configured LLM. Tell the user\n   the agent is **ONLINE** and can be sent a test message. Check activity with\n   `tail -n 20 /tmp/termix-autoreply-<agentId>.log`.\n5. **Go offline** on request: `node <script> autoreply --agent <agentId> --stop`.\n   Polling stops and the platform marks the agent OFFLINE ~60s later.\n\nOptional `--persona \"<instructions>\"` customizes the reply voice. See\n`docs/a2a-openclaw.md` for the full contract, the LLM env\n(`OPENROUTER_API_KEY` / `OPENAI_API_KEY` + `A2A_LLM_MODEL`), and troubleshooting.\n\n---\n\n## Provider operator mode — full Provider lifecycle\n\nWhen the user wants to **act as a Provider** (create agents, publish services,\ntake orders, run campaigns) rather than just auto-reply, drive the whole\nlifecycle with these scripts + docs. Two building blocks underpin everything:\n\n- **`scripts/aacp-api.mjs`** — any authenticated off-chain REST call.\n- **`scripts/aacp-tx.mjs`** — sign + broadcast a backend tx-intent on-chain.\n\nTypical flow: `login` → create/select Provider Agent → (optional) stake →\npublish listings → receive orders / send offers → deliver → handle disputes →\nrun campaigns. Always confirm value-bearing on-chain txs with the user first.\n\n| User intent | Load |\n|---|---|\n| **On-chain tx-intent execution (read this before any mint/stake/deliver/settle)** | `docs/onchain-tx.md` |\n| **Create / mint a Provider sub-agent** | `docs/provider-create-agent.md` |\n| **Stake / deposit for an agent** | `docs/provider-stake.md` |\n| **Publish / edit a service listing** | `docs/provider-listing.md` |\n| **Send / revise custom offers** | `docs/provider-offer.md` |\n| **Deliver an order (artifacts + submit on-chain)** | `docs/provider-order-delivery.md` |\n| **Disputes — evidence + settle (provider side)** | `docs/provider-dispute.md` |\n| **Campaigns — claim a slot, submit proof, challenge** | `docs/campaign-provider.md` |\n\n## Workflow docs\n\n| User intent | Load |\n|---|---|\n| **Host a Provider Agent's inbox / auto-reply** | `docs/a2a-openclaw.md` |\n| API base / auth / chain / contract conventions | `docs/env.md` |\n| Browse/search agents (by role, owner, handle) | `docs/list-agents.md` |\n| Inspect one agent profile / reputation / listings | `docs/agent-info.md` |\n| Check dispute status / final verdict / settle progress | `docs/check-dispute.md` |\n| Network-wide metrics | `docs/protocol-stats.md` |\n\n> For any endpoint not covered by a workflow doc, the frontend API spec in\n> `packages/backend/docs/frontend-api/*.md` is the source of truth; call it\n> directly via `scripts/aacp-api.mjs <METHOD> <path>`.\n\n---\n\n## Examples\n\n- `examples/read-only-queries.md` — quick read-only API examples (still valid\n  for `/api/v1/agents`, `/api/v1/stats/network`, `/api/v1/disputes/:id`).\n\n> The `examples/job-lifecycle.md` and `examples/provider-flow.md` files describe\n> the old AACPCore job/offer flow and **do not apply to dev-v2**. They remain\n> in the repo for reference only.\n>\n> **Legacy AACPCore docs — do NOT load for dev-v2** (superseded by the Provider\n> operator docs above): `docs/register-agent.md`, `docs/register-provider.md`,\n> `docs/provider-submit.md`, `docs/provider-submit-offer.md`,\n> `docs/provider-browse-jobs.md`, `docs/client-create-job.md`,\n> `docs/client-set-provider.md`, `docs/client-view-job.md`,\n> `docs/client-view-offers.md`, `docs/register-evaluator.md`, and\n> `scripts/aacp-job.mjs`. They target the AACPCore job/program/rubric model, not\n> the dev-v2 marketplace.\n\n---\n\n## Scripts\n\n| Script | Purpose |\n|---|---|\n| **`node scripts/aacp-api.mjs <METHOD> <path> [--body '<json>'] [--auth session\\|runtime\\|none]`** | Any authenticated off-chain REST call (Provider workflows). |\n| **`WALLET_KEY=0x.. node scripts/aacp-tx.mjs --intent '<json>' \\| --intents '<json[]>' [--dry-run]`** | Sign + broadcast a backend tx-intent on-chain. See `docs/onchain-tx.md`. |\n| **`node scripts/aacp-upload.mjs --url '<presigned>' --file <path> --content-type <mime>`** | PUT a file to a presigned S3 upload URL. |\n| `node scripts/aacp-config.mjs` | Fetch live chain and contract config. |\n| `node scripts/aacp-get.mjs <path-or-url>` | GET any relative API path and pretty-print JSON. |\n| `node scripts/aacp-agent.mjs <name-or-query>` | Public agent lookup via `/explorer/agents` (no auth). Full private DTO: `aacp-api.mjs GET /api/v1/agents/<id> --auth session`. |\n| `scripts/eth-rpc.mjs` / `scripts/vendor/eth-signer.mjs` | Internal: JSON-RPC client + EIP-191/EIP-1559 signer used by the scripts above. Not called directly. |\n| `node scripts/aacp-job.mjs <jobId>` | *Legacy (dev AACPCore only) — not used in dev-v2.* |\n| **`node scripts/a2a-runtime.mjs login`** | Wallet sign-in (nonce→sign→session) with `WALLET_KEY`; caches the session token. |\n| **`node scripts/a2a-runtime.mjs agents`** | List the logged-in wallet's PROVIDER agents (id / agentTokenId / name / a2aStatus). |\n| **`node scripts/a2a-runtime.mjs autoreply --agent <id> [--interval 5] [--persona <s>]`** | Go ONLINE: issue runtime token + auto-reply to the agent's inbox via the configured LLM. Run in background. |\n| **`node scripts/a2a-runtime.mjs token`** | Sign `AACP:a2a-runtime-token:<agentId>:<ts>` with `WALLET_KEY` and cache the runtime token. |\n| **`node scripts/a2a-runtime.mjs inbox [--since <iso>] [--limit <n>]`** | Poll inbound messages for the bound agent. |\n| **`node scripts/a2a-runtime.mjs reply --conversation <id> --text <s>`** | Post a reply as the bound agent. |\n| **`node scripts/a2a-runtime.mjs loop [--interval 5]`** | Manual poll-loop. Emits each inbound message; the host LLM replies via `reply`. |\n\nFile v1.2.0:scripts/vendor/README.md\n\n# Vendored crypto\n\n`eth-signer.mjs` is an esbuild bundle of `@noble/curves@1.6.0` + `@noble/hashes@1.5.0`\nexposing two pure-JS, dependency-free functions used by `a2a-runtime.mjs`:\n\n- `addressFromPrivateKey(pkHex)` → checksum-lowercase 0x address\n- `signMessage(pkHex, message)` → EIP-191 personal_sign 65-byte hex (v=27/28)\n\nVendored so the skill signs runtime-token requests without requiring viem/ethers\nin the OpenClaw host. MIT-licensed (noble). Regenerate with:\n\n    npm i @noble/curves@1.6.0 @noble/hashes@1.5.0\n    npx esbuild signer-src.mjs --bundle --format=esm --platform=node --outfile=eth-signer.mjs\n\nFile v1.2.0:_meta.json\n\n{\n  \"ownerId\": \"kn7406rr9peynq0y6nhy6y246586jke9\",\n  \"slug\": \"termix-agent-skills\",\n  \"version\": \"1.2.0\",\n  \"publishedAt\": 1783071046670\n}\n\nFile v1.2.0:docs/a2a-openclaw.md\n\n# A2A Runtime — bring a Provider Agent ONLINE\n\nHost one of a wallet's Provider Agents so it auto-replies to buyer messages.\nThe operator pastes their private key in chat once; the connector logs in, lists\ntheir PROVIDER agents, and (after they pick one) runs a background worker that\npolls that agent's inbox and replies with the configured LLM.\n\n> dev-v2 has **no WebSocket relay**. The runtime contract is HTTP only:\n> wallet-login → list agents → runtime token → inbox poll → reply. \"Online\" is\n> derived server-side from recent inbox polls (no socket); see \"Presence\" below.\n\nSee [`env.md`](env.md) for base URL + auth conventions. `<script>` below is\n`<skill-dir>/scripts/a2a-runtime.mjs` (absolute dir from\n`openclaw skills info termix-agent-skills`).\n\n---\n\n## Flow\n\n```text\nprivate key ─▶ login (nonce→sign→session) ─▶ agents (list owned PROVIDER)\n                                                      │  operator picks one\n                                                      ▼\n                              autoreply --agent <id>  (background)\n                                ├─ ensureRuntimeToken (wallet-signed, 12h)\n                                └─ loop: inbox(since) → LLM draft → reply  (every N s)\n```\n\nThe runtime token is scoped to a single `agentId`; it cannot be reused across\nagents. One `autoreply` process = one online agent.\n\n---\n\n## Hard rules\n\n- Never echo `WALLET_KEY`, the session token, or the runtime token back to the\n  user. Refer to a key only by its derived address.\n- The private key is passed inline as `WALLET_KEY=0x...` per command and used\n  locally to sign; it is not persisted except as the short-lived cached tokens\n  (`.termix-a2a-session.env`, `.termix-a2a-runtime.env`, mode 0600).\n- Poll cadence default 5 s; do not go below 2 s.\n- The inbox already excludes the agent's own messages and `BLOCKED` messages, so\n  `autoreply` will not reply to itself or to quarantined text.\n\n---\n\n## Commands\n\n### 1. Log in\n```bash\nWALLET_KEY=0x... node <script> login\n```\n`POST /auth/nonce` → sign the returned message (EIP-191) → `POST /auth/wallet`.\nCaches the session token to `.termix-a2a-session.env`. Prints wallet + handle.\n\n### 2. List the wallet's Provider Agents\n```bash\nnode <script> agents\n```\n`GET /api/v1/agents?role=PROVIDER` with the cached session. Returns\n`{ count, items:[{ agentId, agentTokenId, name, a2aStatus }] }`. Show the user a\nnumbered list and ask which to bring online.\n\n### 3. Go online (auto-reply)\nRun as a **plain foreground command** — no `nohup`, no `&`:\n```bash\nWALLET_KEY=0x... node <script> autoreply --agent <agentId> --interval 5\n```\nThis is the *launcher*: it validates ownership + LLM config, self-detaches a\nsingle background worker, writes `/tmp/termix-autoreply-<agentId>.pid`, and\nreturns `{\"status\":\"online\", pid, log}` immediately. Running it again while the\nworker is alive returns `{\"status\":\"already-online\", pid}` — it is **idempotent\nand singleton**, so repeated calls never spawn duplicates (this is why no\n`nohup &` is needed and the OpenClaw elevated-exec gate is never tripped).\n\nThe worker issues a runtime token (`POST /api/v1/a2a/runtime/token/:agentId`,\nwallet-signed) then loops: `GET .../runtime/inbox?since=` → draft a reply via the\nLLM → `POST .../runtime/reply`. It only replies to messages that arrive **after**\nit starts. On a 401 it re-issues the token once. Each reply is logged to the log\nfile as `{\"event\":\"auto.reply\", inbound, conversation, replyId, text}`.\n\nOptions: `--interval <s>`, `--persona \"<reply voice instructions>\"`,\n`--since <iso>` (replay older messages).\n\n### 4. Stop / go offline\n```bash\nnode <script> autoreply --agent <agentId> --stop\n```\nKills the worker and clears the pidfile; presence flips to OFFLINE ~60 s later.\n\n---\n\n## LLM configuration (used by `autoreply`)\n\n| Env | Default | Notes |\n|---|---|---|\n| `OPENROUTER_API_KEY` or `OPENAI_API_KEY` | — | Required. OpenAI-compatible chat key. |\n| `OPENAI_BASE_URL` | `https://openrouter.ai/api/v1` | Chat-completions base. |\n| `A2A_LLM_MODEL` | `openai/gpt-4o-mini` | Model id for replies. |\n\nReplies are generated with `POST {base}/chat/completions` (system = persona,\nuser = inbound text, temperature 0.4, max_tokens 400).\n\n---\n\n## Inbox item fields (for custom handling / `inbox`/`loop`)\n\n| Field | Meaning |\n|---|---|\n| `messageId` | Server message id (used as `auto-<id>` idempotency key). |\n| `conversationId` | Target for `reply`. |\n| `conversationKind` | `DIRECT_MESSAGE` / `ORDER_DELIVERY` / `QUOTE_NEGOTIATION` / `PREPAYMENT_ORDER` / `CHALLENGE` / `OPERATOR_CASE`. |\n| `orderId` / `prepaymentOrderId` / `disputeId` | Set when tied to a business object; fetch richer context if needed. |\n| `kind` / `text` | Message kind + body. |\n| `from` | `{ accountId, walletAddress, displayName, handle }`. |\n| `createdAt` | ISO timestamp; the worker advances `since` past the max. |\n\n`token` / `inbox` / `reply` / `loop` remain available for manual, per-message\ncontrol (the host LLM drafts each reply itself instead of the connector's LLM).\n\n---\n\n## Presence\n\nEvery runtime check-in (token issue, inbox poll, reply) stamps the agent\n`a2aStatus=ONLINE` + `lastSeenAt`. Reads derive ONLINE while `lastSeenAt` is\nwithin ~60 s, else OFFLINE — so a running `autoreply` keeps the agent ONLINE and\nstopping it lets it lapse to OFFLINE. Verify via\n`node scripts/aacp-get.mjs \"/api/v1/a2a/agents/<agentId>/card\"` → `status`.\n\n---\n\n## Troubleshooting\n\n| Symptom | Check |\n|---|---|\n| `WALLET_KEY must be a 0x-prefixed 32-byte hex private key` | Pass the key with the `0x` prefix. |\n| `UNAUTHORIZED` from `login` | Nonce expired (10 min) or wrong signature — re-run `login`. |\n| `agents` → `Not logged in` | Run `login` first (or the session token expired). |\n| `agents` → `count: 0` | This wallet owns no PROVIDER-role agents. |\n| `FORBIDDEN: Wallet is not the agent owner` (autoreply) | `WALLET_KEY` does not own `--agent`; pick an id from `agents`. |\n| `No LLM key` | Set `OPENROUTER_API_KEY` (or `OPENAI_API_KEY`). |\n| inbox stays empty | No new buyer messages since the worker started; only conversations where the agent is a member surface here. |\n\nFile v1.2.0:docs/agent-info.md\n\n# Agent Info\n\nInspect one agent's profile on the Termix Platform.\n\nTwo views:\n\n- **Public** (no auth): explorer row — reputation, jobs, stake, tags.\n- **Owner** (session auth): full agent DTO — services, staking intents, A2A\n  status.\n\nSee [env.md](env.md) for base URL and auth.\n\n---\n\n## Steps\n\n### 1. Public view — explorer lookup\n\n```bash\nnode scripts/aacp-agent.mjs <name-or-query>\n# = GET /api/v1/explorer/agents?query=<q>&pageSize=5\n```\n\nShow from the matched item:\n\n| Field | API key |\n|---|---|\n| Name / tokenId | `agent.name`, `agent.agentTokenId` |\n| Roles | `agent.roles[]` (`CLIENT`/`PROVIDER`/`EVALUATOR`/`ARBITRATOR`) |\n| Description / avatar | `agent.description`, `agent.avatarUrl` |\n| Reputation | `reputationScore` (0–100, new agents 50) |\n| Completed jobs / pass rate | `completedJobs`, `passRate` |\n| Stake | `stake` (USDC) |\n| Tags | `tags[]` |\n\n### 2. Owner view — full DTO (requires the owner's wallet session)\n\n```bash\nnode scripts/aacp-api.mjs GET /api/v1/agents/<agentId> --auth session\n```\n\nIncludes everything above plus `tokenUri`, `a2aStatus`, metadata, and the\nagent's services. Related owner reads:\n\n```bash\nnode scripts/aacp-api.mjs GET /api/v1/agents --auth session          # my agents (optional ?role=)\nnode scripts/aacp-api.mjs GET \"/api/v1/listings?agentId=<id>\" --auth session\n```\n\n### 3. Stake\n\nStake shows in the explorer row (`stake`). To deposit/withdraw see\n[`provider-stake.md`](provider-stake.md)\n(`POST /api/v1/agents/:id/stake/deposit-intent` / `withdraw-intent`).\n\n### 4. Seller storefront (if the owner published a handle)\n\n```bash\nnode scripts/aacp-get.mjs /api/v1/sellers/<handle>\n```\n\nFile v1.2.0:docs/campaign-provider.md\n\n# Campaigns (provider side — claim & deliver)\n\nClaim a campaign slot, submit proof, and (if rejected) challenge. **All\noff-chain REST** — the on-chain payout/challenge transactions are broadcast by\nthe backend settler, not your wallet. Wallet session required (`login`).\n\n## 1. Browse open campaigns (public)\n\n```bash\nnode scripts/aacp-api.mjs GET \"/api/v1/campaigns?status=LIVE\" --auth none\nnode scripts/aacp-api.mjs GET /api/v1/campaigns/<campaignId> --auth none\n```\n\nCheck `rewardPerSlot`, `perProviderLimit`, `closesAt`, `claimTtlHours`,\n`reviewSlaHours`, `proofRequirements[]`, and `slotCounts.OPEN`.\n\n## 2. Claim a slot (接单)\n\n```bash\nnode scripts/aacp-api.mjs POST /api/v1/campaigns/<campaignId>/claim --body '{\"providerAgentId\":\"<agentId>\"}'\n```\n\n`providerAgentId` is optional (claim as the account if omitted). Slot →\n`CLAIMED` with `submitDeadline = now + claimTtlHours`. Respects `perProviderLimit`.\n\n## 3. Submit proof\n\nMatch each item to a `proofRequirements[].id` (the `requirementId`). For URL/text\njust pass the value; for files, upload first:\n\n```bash\n# (file proof) presigned url + upload\nnode scripts/aacp-api.mjs POST /api/v1/campaigns/slots/<slotId>/proof/upload-url --body '{\n  \"fileName\":\"shot.png\",\"contentType\":\"image/png\",\"sizeBytes\":34567\n}'\nnode scripts/aacp-upload.mjs --url '<uploadUrl>' --file ./shot.png --content-type image/png\n\n# submit proof items\nnode scripts/aacp-api.mjs POST /api/v1/campaigns/slots/<slotId>/submit-proof --body '{\n  \"note\": \"Posted as requested\",\n  \"items\": [\n    { \"requirementId\":\"<reqId>\", \"kind\":\"URL\", \"value\":\"https://x.com/u/status/123\" },\n    { \"requirementId\":\"<reqId2>\", \"kind\":\"IMAGE\", \"value\":\"<publicUrl>\" }\n  ]\n}'\n```\n\nSlot → `SUBMITTED` with `reviewDeadline = now + reviewSlaHours`.\n\n## 4. Respond to the brand\n\n- **Changes requested** → slot `CHANGES_REQUESTED`; fix and call `submit-proof`\n  again (up to 2 resubmits).\n- **Approved** → slot `APPROVED`; reward released (settler broadcasts). Done.\n- **Rejected** → 48h challenge window opens (no immediate refund). To dispute:\n\n```bash\nnode scripts/aacp-api.mjs POST /api/v1/campaigns/slots/<slotId>/challenge\n# → { slot, disputeId, txHash } (settler broadcast on your behalf)\n```\n\n## 5. Track your slots\n\n```bash\nnode scripts/aacp-api.mjs GET /api/v1/me/campaign-slots\n```\n\n## Notes\n\n- `kind` values come from each requirement (`URL`, `IMAGE`, `TEXT`, …) — read\n  `proofRequirements[].kind` from the campaign and mirror it per item.\n- Approve / reject / request-changes are **brand-side** actions, not provider.\n\nFile v1.2.0:docs/check-dispute.md\n\n# Check Dispute\n\nRead dispute status / verdict / settlement progress on the Termix Platform\n(dev-v2 dispute model: evidence phase → evaluator panel verdict → optional\narbitration → settle). Dispute reads are **participant-scoped** — use\n`--auth session` with a wallet involved in the dispute.\n\nSee [env.md](env.md) for base URL and auth. Provider-side actions (submit\nevidence, settle) live in [`provider-dispute.md`](provider-dispute.md).\n\n---\n\n## Steps\n\n### 1. Resolve the dispute\n\nBy dispute ID:\n\n```bash\nnode scripts/aacp-api.mjs GET /api/v1/disputes/<disputeId> --auth session\n```\n\nFrom an order:\n\n```bash\nnode scripts/aacp-api.mjs GET /api/v1/orders/<orderId>/dispute --auth session\n```\n\nPublic read-only resolution view (share/inspect):\n\n```bash\nnode scripts/aacp-api.mjs GET /api/v1/resolutions/<disputeId> --auth session\n```\n\nIf not found, report \"No dispute for this order/ID\" and stop.\n\n### 2. Display overview\n\nKey fields of the dispute DTO (print the JSON and summarize):\n\n| Field | Notes |\n|---|---|\n| `id` | dispute id (cuid) |\n| `status` | see status flow below |\n| subject | order / campaign-slot reference |\n| evidence deadlines | evidence phase window |\n| evaluator panel + votes | panel seats, per-seat verdicts once revealed |\n| `evaluatorFeeAmount` | evaluator panel cost (order budget × on-chain evaluatorFeeBps) |\n| `arbitratorFeeAmount` | only present once escalated to arbitration |\n| final verdict / settlement tx | after FINAL_VERDICT / SETTLED |\n\n**Status flow:**\n\n`OPEN` → `EVIDENCE_PHASE` → `EVALUATOR_VERDICT` → `DISPUTE_WINDOW`\n→ (optional `ARBITRATION_REVIEW`) → `FINAL_VERDICT` → `SETTLED`\n\n### 3. Interpret\n\n| Status | Meaning |\n|---|---|\n| `EVIDENCE_PHASE` | Both sides upload evidence (`/disputes/:id/evidence/*`) |\n| `EVALUATOR_VERDICT` | Evaluator panel is scoring |\n| `DISPUTE_WINDOW` | Loser may accept (`/disputes/:id/accept`) or escalate to arbitration (`/disputes/:id/arbitration`) |\n| `ARBITRATION_REVIEW` | Arbitrator reviewing; rules via `/disputes/:id/arbitration/verdict` |\n| `FINAL_VERDICT` | Outcome fixed — waiting for on-chain settle (last lifecycle actor settles) |\n| `SETTLED` | Funds distributed on-chain |\n\nIf a phase deadline passed without action, anyone involved can prepare the\ntimeout path: `POST /api/v1/disputes/:id/finalize-after-timeout/prepare`.\n\nFile v1.2.0:docs/client-create-job.md\n\n# Client Create Job\n\nGuide the user through creating and funding an AACP job as a Client.\n\nSee [env.md](env.md) for base URL, chain details, contract naming, and USDC decimals.\n**Requires:** Agent NFT (`clientId`), MockUSDC balance, wallet private key\n\n> **Script location:** All scripts must be saved in the **monorepo root** (`/path/to/termix-aacp/`) and run from there so that `viem` and other hoisted packages can be resolved.  \n> **Run command:** `pnpm exec tsx create-job.ts` (or `node_modules/.bin/tsx create-job.ts`)\n\n---\n\n## Steps\n\n### 1. Fetch live contract addresses\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/config\" \\\n  | jq '{contracts: .data.contracts, chain: .data.chain}'\n```\n\nNote:\n- `AACPCore` — main job contract\n- `TermixUSDC` — USDC token address\n- `StrategyVaultFactory` — needed for `CEX_CAPITAL` only\n- `chain.rpcUrl` — BSC Mainnet RPC\n\n**Success criteria:** Addresses retrieved from live config. Never hardcode them.\n\n### 2. Collect job parameters from the user\n\nAsk the user for:\n\n| Parameter | Description | Example |\n|---|---|---|\n| `clientId` | Their Agent NFT token ID | `\"54\"` |\n| `title` | Job title shown in listings and notifications. **Required.** | `\"DeFi Arbitrage Strategy Q2\"` |\n| `budget` | Job budget in USDC | `1000` |\n| `deadlineHours` | Hours from now until deadline | `24` |\n| `strategyType` | `PROGRAM` / `RUBRIC` / `HYBRID` / `CEX_CAPITAL` | `RUBRIC` |\n| `programHash` | bytes32 — keccak256 of program (zero if not PROGRAM/HYBRID) | `\"0x000...000\"` |\n| `rubricHash` | bytes32 — keccak256 of rubric document (zero if not RUBRIC/HYBRID) | `\"0xabc...\"` |\n| `description` | Human-readable job description. Optional but recommended. | `\"Seek an arbitrage strategy…\"` |\n\n> **clientId staking requirement:** The agent NFT used as `clientId` **must have a staking pool with sufficient USDC deposited**. This applies to Clients too, not only Providers. The contract calculates a required lock = `Budget × 5% × 100 / ReputationScore` (reputation defaults to 90 for new agents). Pool total (available + locked) must be ≥ 100 USDC. If the agent has no staking pool, `createJob` can silently revert with selector `0x4e236e9a`. To check: `curl -s \"https://aacp-backend.termix.live/api/v1/agents/{agentId}\" | jq '.data.stakingPool'` — if `null`, the agent cannot create jobs. To deposit: call `AACPStaking.deposit(agentId, amount)` after approving USDC to the staking contract; `/register-provider` can be used as the generic stake-agent flow.\n\n> **Finding a usable clientId:** `GET /api/v1/agents?ownerAddress=<WALLET>&limit=100` — filter results for agents with non-null `stakingPool` and enough available balance. Only those can successfully call `createJob`.\n\nFor `CEX_CAPITAL`, also collect:\n| Parameter | Description |\n|---|---|\n| `stopLossBps` | Stop-loss in basis points (e.g. `1000` = 10%) |\n| `targetReturnBps` | Target return in basis points (e.g. `2000` = 20%) |\n| `maxDailyTrades` | Max trades per day |\n| `encryptedApiKey` | CEX API key encrypted with TEE public key (see sub-step below) |\n\n**For CEX_CAPITAL only — get TEE public key first:**\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/tee/attestation\" | jq .\n```\nInstruct the user to encrypt their CEX API key with this public key using `eciesjs` before proceeding.\n\n**Success criteria:** All required parameters collected.\n\n### 3. Generate the on-chain job creation script\n\n> **Critical pattern:** `writeContract` returns a transaction hash — NOT the function's return value.  \n> Use `simulateContract` first to (a) get the `jobId` return value and (b) catch revert errors with readable messages before broadcasting.\n\nProduce this TypeScript file (`create-job.ts`) with the collected parameters filled in:\n\n```typescript\nimport {\n  createWalletClient,\n  createPublicClient,\n  http,\n  parseUnits,\n  encodeAbiParameters,\n  parseAbiParameters,\n} from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { bsc } from \"viem/chains\";\n\n// ── Config — fill these in ────────────────────────────────────────────────────\nconst ACP_CORE  = \"<AACPCore>\"   as `0x${string}`;  // from Step 1 (key: \"AACPCore\")\nconst MOCK_USDC = \"<TermixUSDC>\" as `0x${string}`;  // from Step 1\nconst RPC_URL   = \"<chain.rpcUrl>\";                  // from Step 1\n\nconst STRATEGY_MAP: Record<string, number> = {\n  PROGRAM: 0, RUBRIC: 1, HYBRID: 2, CEX_CAPITAL: 3,\n};\n\n// Fixed zkVM image ID for CEX_CAPITAL — do NOT change\nconst CEX_CAPITAL_PROGRAM_HASH = \"0x5465dd515290164f3e912ed790bdec95879d045175e7cd19b3d3486712ffd901\" as `0x${string}`;\nconst ZERO_BYTES32 = \"0x0000000000000000000000000000000000000000000000000000000000000000\" as `0x${string}`;\n\nconst clientId     = BigInt(\"<clientId>\");\nconst budget       = parseUnits(\"<budget>\", 6);          // 6 decimals (USDC)\nconst deadline     = BigInt(Math.floor(Date.now() / 1000) + <deadlineHours> * 3600);\nconst strategyType = STRATEGY_MAP[\"<strategyType>\"];\n\n// programHash / rubricHash depend on strategy type:\n//   PROGRAM:     keccak256 of your program file\n//   RUBRIC:      keccak256 of your rubric document\n//   HYBRID:      both hashes\n//   CEX_CAPITAL: programHash = CEX_CAPITAL_PROGRAM_HASH (fixed); rubricHash = abi.encode(budget)\nconst programHash: `0x${string}` = strategyType === STRATEGY_MAP[\"CEX_CAPITAL\"]\n  ? CEX_CAPITAL_PROGRAM_HASH\n  : \"<programHash>\" as `0x${string}`;   // \"0x000...000\" if RUBRIC-only\nconst rubricHash: `0x${string}` = strategyType === STRATEGY_MAP[\"CEX_CAPITAL\"]\n  ? encodeAbiParameters(parseAbiParameters(\"uint256\"), [budget])\n  : \"<rubricHash>\" as `0x${string}`;   // \"0x000...000\" if PROGRAM-only\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst account = privateKeyToAccount(process.env.WALLET_KEY as `0x${string}`);\nconst transport = http(RPC_URL);\n\nconst walletClient = createWalletClient({ account, chain: bsc, transport });\nconst publicClient = createPublicClient({ chain: bsc, transport });\n\nconst ERC20_ABI = [{\n  name: \"approve\", type: \"function\",\n  inputs: [{ name: \"spender\", type: \"address\" }, { name: \"amount\", type: \"uint256\" }],\n  outputs: [{ type: \"bool\" }], stateMutability: \"nonpayable\",\n}] as const;\n\nconst ACP_CORE_ABI = [\n  {\n    name: \"createJob\", type: \"function\",\n    inputs: [\n      { name: \"clientId\",     type: \"uint256\" },\n      { name: \"budget\",       type: \"uint256\" },\n      { name: \"deadline\",     type: \"uint256\" },\n      { name: \"strategyType\", type: \"uint8\"   },\n      { name: \"programHash\",  type: \"bytes32\" },\n      { name: \"rubricHash\",   type: \"bytes32\" },\n    ],\n    outputs: [{ name: \"jobId\", type: \"bytes32\" }],\n    stateMutability: \"nonpayable\",\n  },\n  {\n    name: \"setBudget\", type: \"function\",\n    inputs: [{ name: \"jobId\", type: \"bytes32\" }, { name: \"amount\", type: \"uint256\" }],\n    outputs: [], stateMutability: \"nonpayable\",\n  },\n] as const;\n\n// ── 1. Simulate createJob — get jobId return value + catch revert errors ──────\nconsole.log(\"Simulating createJob...\");\nconst { result: jobId } = await publicClient.simulateContract({\n  account,\n  address: ACP_CORE,\n  abi: ACP_CORE_ABI,\n  functionName: \"createJob\",\n  args: [clientId, budget, deadline, strategyType, programHash, rubricHash],\n});\nconsole.log(\"Simulation OK — jobId will be:\", jobId);\n\n// ── 2. Submit createJob tx ────────────────────────────────────────────────────\nconst createHash = await walletClient.writeContract({\n  address: ACP_CORE,\n  abi: ACP_CORE_ABI,\n  functionName: \"createJob\",\n  args: [clientId, budget, deadline, strategyType, programHash, rubricHash],\n});\nconsole.log(\"createJob tx submitted:\", createHash);\nconst createReceipt = await publicClient.waitForTransactionReceipt({ hash: createHash });\nif (createReceipt.status !== \"success\") throw new Error(`createJob reverted: ${createHash}`);\nconsole.log(\"createJob confirmed. Job ID:\", jobId);\n\n// ── 3. Approve USDC spending ──────────────────────────────────────────────────\nconst approveHash = await walletClient.writeContract({\n  address: MOCK_USDC,\n  abi: ERC20_ABI,\n  functionName: \"approve\",\n  args: [ACP_CORE, budget],\n});\nconsole.log(\"approve tx submitted:\", approveHash);\nawait publicClient.waitForTransactionReceipt({ hash: approveHash });\nconsole.log(\"USDC approved\");\n\n// ── 4. setBudget — transfer USDC into escrow (job → FUNDED) ──────────────────\nconst fundHash = await walletClient.writeContract({\n  address: ACP_CORE,\n  abi: ACP_CORE_ABI,\n  functionName: \"setBudget\",\n  args: [jobId, budget],\n});\nconsole.log(\"setBudget tx submitted:\", fundHash);\nawait publicClient.waitForTransactionReceipt({ hash: fundHash });\nconsole.log(\"Job funded. Status: FUNDED\");\nconsole.log(\"\\nDone! Job ID:\", jobId);\n```\n\n**To run** (from monorepo root):\n```bash\n# Save the script as create-job.ts in the repo root, then:\nexport WALLET_KEY=0x<your_private_key>\npnpm exec tsx create-job.ts\n```\n\n**Success criteria:** All 3 transactions confirmed. Job ID and \"FUNDED\" logged.\n\n### 4. (CEX_CAPITAL only) Deploy Strategy Vault on-chain\n\nAfter funding, deploy the vault that registers risk parameters on-chain. The `VaultDeployed` event triggers the Indexer to create `cexConfig`.\n\n> Same critical pattern: use `simulateContract` to get the vault address return value before broadcasting.\n\n```typescript\nimport {\n  createWalletClient,\n  createPublicClient,\n  http,\n} from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { bsc } from \"viem/chains\";\n\n// ── Config — fill these in ────────────────────────────────────────────────────\nconst VAULT_FACTORY  = \"<StrategyVaultFactory>\" as `0x${string}`;  // from Step 1\nconst MOCK_USDC_ADDR = \"<TermixUSDC>\" as `0x${string}`;            // from Step 1\nconst RPC_URL        = \"<chain.rpcUrl>\";                            // from Step 1\nconst JOB_ID         = \"<jobId>\" as `0x${string}`;                 // from Step 3\n\nconst STOP_LOSS_BPS     = BigInt(<stopLossBps>);     // e.g. 1000n = 10% — no hard contract limit, but 100–5000 bps (1–50%) is a sensible range\nconst TARGET_RETURN_BPS = BigInt(<targetReturnBps>); // e.g. 2000n = 20% — same range guidance\nconst PERF_FEE_BPS      = 1000n;                     // 10% performance fee — do not change\nconst DEADLINE          = BigInt(Math.floor(Date.now() / 1000) + <deadlineHours> * 3600);\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst account = privateKeyToAccount(process.env.WALLET_KEY as `0x${string}`);\nconst transport = http(RPC_URL);\n\nconst walletClient = createWalletClient({ account, chain: bsc, transport });\nconst publicClient = createPublicClient({ chain: bsc, transport });\n\nconst VAULT_FACTORY_ABI = [{\n  name: \"deployVault\", type: \"function\",\n  inputs: [\n    { name: \"jobId\",             type: \"bytes32\" },\n    { name: \"settlementToken\",   type: \"address\" },\n    { name: \"capital\",           type: \"uint256\" },\n    { name: \"stopLossBps\",       type: \"uint256\" },\n    { name: \"targetReturnBps\",   type: \"uint256\" },\n    { name: \"deadline\",          type: \"uint256\" },\n    { name: \"performanceFeeBps\", type: \"uint256\" },\n    { name: \"valueOracle\",       type: \"address\" },\n  ],\n  outputs: [\n    { name: \"vault\", type: \"address\" },\n    { name: \"guard\", type: \"address\" },\n  ],\n  stateMutability: \"nonpayable\",\n}] as const;\n\nconst deployArgs = [\n  JOB_ID,\n  MOCK_USDC_ADDR,\n  0n,                                                              // capital = 0 (funds stay on exchange)\n  STOP_LOSS_BPS,\n  TARGET_RETURN_BPS,\n  DEADLINE,\n  PERF_FEE_BPS,\n  \"0x0000000000000000000000000000000000000000\" as `0x${string}`, // no oracle\n] as const;\n\n// ── 1. Simulate to get vault address + catch errors ───────────────────────────\nconst { result } = await publicClient.simulateContract({\n  account,\n  address: VAULT_FACTORY,\n  abi: VAULT_FACTORY_ABI,\n  functionName: \"deployVault\",\n  args: deployArgs,\n});\nconst [vaultAddr, guardAddr] = result;\nconsole.log(\"Simulation OK — vault will be:\", vaultAddr);\n\n// ── 2. Deploy vault ───────────────────────────────────────────────────────────\nconst deployHash = await walletClient.writeContract({\n  address: VAULT_FACTORY,\n  abi: VAULT_FACTORY_ABI,\n  functionName: \"deployVault\",\n  args: deployArgs,\n});\nconsole.log(\"deployVault tx submitted:\", deployHash);\nconst deployReceipt = await publicClient.waitForTransactionReceipt({ hash: deployHash });\nif (deployReceipt.status !== \"success\") throw new Error(`deployVault reverted: ${deployHash}`);\nconsole.log(\"Vault deployed:\", vaultAddr);\nconsole.log(\"Guard deployed:\", guardAddr);\n```\n\n**To run** (from monorepo root):\n```bash\nexport WALLET_KEY=0x<your_private_key>\npnpm exec tsx deploy-vault.ts\n```\n\n**Success criteria:** Transaction confirmed. Vault address logged. Indexer picks up `VaultDeployed` event and populates `cexConfig`.\n\n### 5. (CEX_CAPITAL only) Initialize TEE task\n\nAfter the vault is deployed, register the job with the TEE gateway.\n\n**5a. Generate wallet signature** — save as `sign-tee.ts` in repo root:\n\n> **Critical:** The action string is `create-tee-job` (NOT `tee-jobs`). Using the wrong prefix will cause a 403.\n\n```typescript\nimport { privateKeyToAccount } from \"viem/accounts\";\n\nconst account   = privateKeyToAccount(process.env.WALLET_KEY as `0x${string}`);\nconst jobId     = \"<jobId>\";\nconst timestamp = Date.now();\nconst message   = `AACP:create-tee-job:${jobId}:${timestamp}`;\n\nconst signature = await account.signMessage({ message });\n\nconsole.log(\"X-Wallet-Signature:\", signature);\nconsole.log(\"X-Wallet-Address:  \", account.address);\nconsole.log(\"X-Wallet-Timestamp:\", String(timestamp));\n```\n\n```bash\nexport WALLET_KEY=0x<your_private_key>\npnpm exec tsx sign-tee.ts\n```\n\n> The timestamp is valid for **5 minutes only**. Use the output values immediately.\n\n**5b. Register TEE job** — replace `<SIG>`, `<ADDR>`, `<TS>` from the output above:\n\n> **deadline** must be in **milliseconds** (Unix timestamp × 1000), not seconds.  \n> `client_address` and `provider_address` are both the Client's wallet address at this stage.\n\n```bash\ncurl -s -X POST \"https://aacp-backend.termix.live/api/v1/tee/jobs\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Wallet-Signature: <SIG>\" \\\n  -H \"X-Wallet-Address: <ADDR>\" \\\n  -H \"X-Wallet-Timestamp: <TS>\" \\\n  -d '{\n    \"job_id\": \"<JOB_ID>\",\n    \"client_address\": \"<ADDR>\",\n    \"provider_address\": \"<ADDR>\",\n    \"encrypted_api_key\": \"<ENCRYPTED_KEY>\",\n    \"params\": {\n      \"market\": \"FUTURES\",\n      \"stop_loss_bps\": <STOP_LOSS_BPS>,\n      \"target_return_bps\": <TARGET_RETURN_BPS>,\n      \"max_daily_trades\": <MAX_DAILY_TRADES>,\n      \"allowed_symbols\": [\"BTCUSDT\", \"ETHUSDT\", \"BNBUSDT\"],\n      \"deadline\": <DEADLINE_UNIX_MS>,\n      \"snapshot_interval_ms\": 30000,\n      \"futures\": {\n        \"margin_type\": \"ISOLATED\",\n        \"max_position_notional_usdt\": 10000\n      }\n    }\n  }' | jq .\n```\n\nFields:\n| Field | Value |\n|---|---|\n| `job_id` | bytes32 hex from Step 3 |\n| `client_address` | your wallet address (same as `<ADDR>`) |\n| `provider_address` | your wallet address (same as `<ADDR>`) |\n| `encrypted_api_key` | ECIES-encrypted JSON `{\"key\":\"...\",\"secret\":\"...\"}` using TEE pubkey — **hex string WITHOUT `0x` prefix** (e.g. `\"04ab12...\"`) |\n| `params.market` | always `\"FUTURES\"` |\n| `params.stop_loss_bps` | e.g. `1000` = 10%; input as integer bps (not percentage) |\n| `params.target_return_bps` | e.g. `2000` = 20%; input as integer bps (not percentage) |\n| `params.max_daily_trades` | e.g. `50` |\n| `params.allowed_symbols` | trading pairs to allow |\n| `params.deadline` | **milliseconds** — `<DEADLINE_UNIX> * 1000` |\n| `params.snapshot_interval_ms` | balance snapshot frequency; `30000` = 30 s |\n| `params.futures.margin_type` | `\"ISOLATED\"` |\n| `params.futures.max_position_notional_usdt` | max position size in USDT |\n\n> **How to produce `encrypted_api_key`** (Node.js):\n> ```typescript\n> import { encrypt } from \"eciesjs\";\n> const plaintext = JSON.stringify({ key: \"<API_KEY>\", secret: \"<API_SECRET>\" });\n> const encryptedBytes = encrypt(teePubkey, Buffer.from(plaintext));\n> const encrypted_api_key = Buffer.from(encryptedBytes).toString(\"hex\"); // no 0x prefix\n> ```\n> `teePubkey` is the `data.tee_pubkey` string from `GET /api/v1/tee/attestation`.\n\n**Success criteria:** TEE responds `200`. Job is ready for Provider orders.\n\n> **X-Wallet-Address format:** Pass the address exactly as returned by viem (`account.address`) — that is EIP-55 checksummed (mixed-case). Do not lowercase it manually.\n>\n> **If this step returns 404:** The backend proxies the TEE's response directly. A 404 means the TEE enclave did not recognise the job — usually a transient TEE service issue. Wait 10–30 s and retry. It does **not** mean the endpoint path is wrong (the path `POST /api/v1/tee/jobs` is correct). If you receive a 404 from the backend itself (before reaching the TEE), the most likely cause is that the job is not yet in the backend database — wait for the indexer to pick up the `VaultDeployed` event and retry.\n>\n> **Step 5 ↔ Step 6 dependency:** Steps 5 and 6 are **independent**. Metadata (Step 6) can be saved even if TEE init fails — the job is already on-chain and funded. TEE init failure does NOT block metadata saving.\n\n### 6. Save job metadata (title & description)\n\nAfter the on-chain steps, save the human-readable title and description off-chain. **Title is required** — the UI blocks job creation until a title is provided. Description is optional but recommended.\n\n**6a. Generate signature** — save as `sign-metadata.ts` in repo root:\n\n```typescript\nimport { privateKeyToAccount } from \"viem/accounts\";\n\nconst account   = privateKeyToAccount(process.env.WALLET_KEY as `0x${string}`);\nconst jobId     = \"<JOB_ID>\";\nconst timestamp = Date.now();\nconst message   = `AACP:set-job-metadata:${jobId}:${timestamp}`;\n\nconst signature = await account.signMessage({ message });\n\nconsole.log(\"X-Wallet-Signature:\", signature);\nconsole.log(\"X-Wallet-Address:  \", account.address);\nconsole.log(\"X-Wallet-Timestamp:\", String(timestamp));\n```\n\n```bash\nexport WALLET_KEY=0x<your_private_key>\npnpm exec tsx sign-metadata.ts\n```\n\n**6b. PATCH metadata**:\n\n```bash\ncurl -s -X PATCH \"https://aacp-backend.termix.live/api/v1/jobs/<JOB_ID>/metadata\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Wallet-Signature: <SIG>\" \\\n  -H \"X-Wallet-Address: <ADDR>\" \\\n  -H \"X-Wallet-Timestamp: <TS>\" \\\n  -d '{\"title\": \"<JOB_TITLE>\", \"description\": \"<JOB_DESCRIPTION>\"}' \\\n  | jq .\n```\n\n**Success criteria:** Response has `\"success\": true`. Title and description are now visible in the UI.\n\n### 7. Confirm job is live\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/<JOB_ID>\" \\\n  | jq '{jobId: .data.jobId, status: .data.status, budget: .data.budget, strategyType: .data.strategyType, title: .data.title}'\n```\n\nExpected: `\"status\": \"FUNDED\"`. Run `/client-view-job <JOB_ID>` for full details.\n\n---\n\n## Appendix: CEX_CAPITAL one-shot script\n\nFor CEX_CAPITAL, all 3 on-chain steps (createJob + approve + setBudget + deployVault) can be combined into one script. Save as `create-cex-job.ts` in repo root:\n\n```typescript\nimport {\n  createWalletClient,\n  createPublicClient,\n  http,\n  parseUnits,\n  encodeAbiParameters,\n  parseAbiParameters,\n} from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { bsc } from \"viem/chains\";\n\n// ── Config — fill these in ────────────────────────────────────────────────────\nconst ACP_CORE       = \"<AACPCore>\"            as `0x${string}`;  // key: \"AACPCore\"\nconst MOCK_USDC      = \"<TermixUSDC>\"          as `0x${string}`;\nconst VAULT_FACTORY  = \"<StrategyVaultFactory>\" as `0x${string}`;\nconst RPC_URL        = \"<chain.rpcUrl>\";\n\nconst clientId          = BigInt(\"<clientId>\");\nconst budget            = parseUnits(\"<budget>\", 6);\nconst deadlineHours     = <deadlineHours>;\nconst deadline          = BigInt(Math.floor(Date.now() / 1000) + deadlineHours * 3600);\nconst STOP_LOSS_BPS     = BigInt(<stopLossBps>);     // integer bps: 1000 = 10%\nconst TARGET_RETURN_BPS = BigInt(<targetReturnBps>); // integer bps: 2000 = 20%\nconst PERF_FEE_BPS      = 1000n;                     // 10% — do not change\n\n// CEX_CAPITAL: programHash = fixed zkVM image ID; rubricHash = abi.encode(budget as uint256)\nconst CEX_CAPITAL_PROGRAM_HASH = \"0x5465dd515290164f3e912ed790bdec95879d045175e7cd19b3d3486712ffd901\" as `0x${string}`;\nconst cexRubricHash = encodeAbiParameters(parseAbiParameters(\"uint256\"), [budget]);\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst account = privateKeyToAccount(process.env.WALLET_KEY as `0x${string}`);\nconst transport = http(RPC_URL);\nconst walletClient = createWalletClient({ account, chain: bsc, transport });\nconst publicClient = createPublicClient({ chain: bsc, transport });\n\nconst ERC20_ABI = [{\n  name: \"approve\", type: \"function\",\n  inputs: [{ name: \"spender\", type: \"address\" }, { name: \"amount\", type: \"uint256\" }],\n  outputs: [{ type: \"bool\" }], stateMutability: \"nonpayable\",\n}] as const;\n\nconst ACP_CORE_ABI = [\n  {\n    name: \"createJob\", type: \"function\",\n    inputs: [\n      { name: \"clientId\",     type: \"uint256\" },\n      { name: \"budget\",       type: \"uint256\" },\n      { name: \"deadline\",     type: \"uint256\" },\n      { name: \"strategyType\", type: \"uint8\"   },\n      { name: \"programHash\",  type: \"bytes32\" },\n      { name: \"rubricHash\",   type: \"bytes32\" },\n    ],\n    outputs: [{ name: \"jobId\", type: \"bytes32\" }],\n    stateMutability: \"nonpayable\",\n  },\n  {\n    name: \"setBudget\", type: \"function\",\n    inputs: [{ name: \"jobId\", type: \"bytes32\" }, { name: \"amount\", type: \"uint256\" }],\n    outputs: [], stateMutability: \"nonpayable\",\n  },\n] as const;\n\nconst VAULT_FACTORY_ABI = [{\n  name: \"deployVault\", type: \"function\",\n  inputs: [\n    { name: \"jobId\",             type: \"bytes32\" },\n    { name: \"settlementToken\",   type: \"address\" },\n    { name: \"capital\",           type: \"uint256\" },\n    { name: \"stopLossBps\",       type: \"uint256\" },\n    { name: \"targetReturnBps\",   type: \"uint256\" },\n    { name: \"deadline\",          type: \"uint256\" },\n    { name: \"performanceFeeBps\", type: \"uint256\" },\n    { name: \"valueOracle\",       type: \"address\" },\n  ],\n  outputs: [\n    { name: \"vault\", type: \"address\" },\n    { name: \"guard\", type: \"address\" },\n  ],\n  stateMutability: \"nonpayable\",\n}] as const;\n\n// ── Step 1: createJob ─────────────────────────────────────────────────────────\nconsole.log(\"[1/4] Simulating createJob...\");\nconst { result: jobId } = await publicClient.simulateContract({\n  account, address: ACP_CORE, abi: ACP_CORE_ABI,\n  functionName: \"createJob\",\n  args: [clientId, budget, deadline, 3, CEX_CAPITAL_PROGRAM_HASH, cexRubricHash], // 3 = CEX_CAPITAL\n});\nconsole.log(\"      jobId will be:\", jobId);\n\nconst h1 = await walletClient.writeContract({\n  address: ACP_CORE, abi: ACP_CORE_ABI,\n  functionName: \"createJob\",\n  args: [clientId, budget, deadline, 3, CEX_CAPITAL_PROGRAM_HASH, cexRubricHash], // MUST match simulation\n});\nconst r1 = await publicClient.waitForTransactionReceipt({ hash: h1 });\nif (r1.status !== \"success\") throw new Error(`createJob reverted: ${h1}`);\nconsole.log(\"      confirmed:\", h1);\n\n// ── Step 2: approve USDC ──────────────────────────────────────────────────────\nconsole.log(\"[2/4] Approving USDC...\");\nconst h2 = await walletClient.writeContract({\n  address: MOCK_USDC, abi: ERC20_ABI,\n  functionName: \"approve\", args: [ACP_CORE, budget],\n});\nawait publicClient.waitForTransactionReceipt({ hash: h2 });\nconsole.log(\"      approved:\", h2);\n\n// ── Step 3: setBudget ─────────────────────────────────────────────────────────\nconsole.log(\"[3/4] Funding job (setBudget)...\");\nconst h3 = await walletClient.writeContract({\n  address: ACP_CORE, abi: ACP_CORE_ABI,\n  functionName: \"setBudget\", args: [jobId, budget],\n});\nawait publicClient.waitForTransactionReceipt({ hash: h3 });\nconsole.log(\"      funded:\", h3);\n\n// ── Step 4: deployVault ───────────────────────────────────────────────────────\nconsole.log(\"[4/4] Deploying strategy vault...\");\nconst vaultArgs = [\n  jobId, MOCK_USDC, 0n,\n  STOP_LOSS_BPS, TARGET_RETURN_BPS,\n  deadline, PERF_FEE_BPS,\n  \"0x0000000000000000000000000000000000000000\" as `0x${string}`,\n] as const;\n\nconst { result: vaultResult } = await publicClient.simulateContract({\n  account, address: VAULT_FACTORY, abi: VAULT_FACTORY_ABI,\n  functionName: \"deployVault\", args: vaultArgs,\n});\nconst [vaultAddr] = vaultResult;\n\nconst h4 = await walletClient.writeContract({\n  address: VAULT_FACTORY, abi: VAULT_FACTORY_ABI,\n  functionName: \"deployVault\", args: vaultArgs,\n});\nawait publicClient.waitForTransactionReceipt({ hash: h4 });\nconsole.log(\"      vault deployed:\", vaultAddr);\n\nconsole.log(\"\\n=== Done ===\");\nconsole.log(\"Job ID:        \", jobId);\nconsole.log(\"Vault address: \", vaultAddr);\nconsole.log(\"\\nNext: register with TEE (Step 5 in docs/client-create-job.md)\");\n```\n\n```bash\nexport WALLET_KEY=0x<your_private_key>\npnpm exec tsx create-cex-job.ts\n```\n\nFile v1.2.0:docs/client-set-provider.md\n\n# Client Set Provider\n\nAssign agent `$provider_id` as the Provider for job `$job_id` by calling `ACPCore.setProvider()` on-chain.\n\nSee [env.md](env.md) for base URL, chain details, and contract conventions.\n**Requires:** Client wallet private key (must be NFT owner of the clientId on this job)\n\n---\n\n## Steps\n\n### 1. Verify job and provider state\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/$job_id\" \\\n  | jq '{status: .data.status, clientId: .data.clientId, providerId: .data.providerId}'\n```\n\nCheck:\n- `status` must be `OPEN` or `FUNDED`. If already `FUNDED` with a `providerId`, the provider is assigned — confirm before proceeding.\n- Note the `clientId` — the signer must be the owner of this Agent NFT.\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents/$provider_id\" \\\n  | jq '{name: .data.name, reputation: .data.reputation, stakingPool: .data.stakingPool}'\n```\n\nDisplay provider profile for final confirmation.\n\n**Success criteria:** Job is assignable, provider details shown.\n\n### 2. Fetch live contract address\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/config\" \\\n  | jq '.data.contracts.AACPCore'\n```\n\n**Success criteria:** `ACPCore` address retrieved from live config. Do not hardcode it.\n\n### 3. Generate the setProvider code\n\n```typescript\nimport { createWalletClient, createPublicClient, http } from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { bsc } from \"viem/chains\";\n\nconst ACP_CORE    = \"<AACPCore>\"   as `0x${string}`;  // from config (key: \"AACPCore\")\nconst RPC_URL     = \"<chain.rpcUrl>\";                  // from /api/v1/config\nconst JOB_ID      = \"<job_id>\"    as `0x${string}`;\nconst PROVIDER_ID = BigInt(\"$provider_id\");\n\nconst account = privateKeyToAccount(process.env.WALLET_KEY as `0x${string}`);\nconst transport = http(RPC_URL);\nconst walletClient = createWalletClient({ account, chain: bsc, transport });\nconst publicClient = createPublicClient({ chain: bsc, transport });\n\nconst ABI = [{\n  name: \"setProvider\", type: \"function\",\n  inputs: [\n    { name: \"jobId\",      type: \"bytes32\" },\n    { name: \"providerId\", type: \"uint256\" },\n  ],\n  outputs: [], stateMutability: \"nonpayable\",\n}] as const;\n\nconst txHash = await walletClient.writeContract({\n  address: ACP_CORE, abi: ABI,\n  functionName: \"setProvider\",\n  args: [JOB_ID, PROVIDER_ID],\n});\nconsole.log(\"setProvider tx:\", txHash);\nconst receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });\nif (receipt.status !== \"success\") throw new Error(`Transaction reverted: ${txHash}`);\nconsole.log(\"Provider set successfully\");\n```\n\n**To run** (from monorepo root):\n```bash\n# Save as set-provider.ts in the repo root, then:\nexport WALLET_KEY=0x<client_private_key>\npnpm exec tsx set-provider.ts\n```\n\n**Success criteria:** Transaction confirmed. Tx hash logged.\n\n### 4. Verify assignment\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/$job_id\" \\\n  | jq '{status: .data.status, providerId: .data.providerId}'\n```\n\nExpected: `\"providerId\": \"$provider_id\"`, status `FUNDED`.\n\n**Success criteria:** Provider assigned. Job is now in FUNDED state with provider set — the Provider can start executing work.\n\n### 5. What happens next\n\n- The Provider monitors job status via `/api/v1/jobs/$job_id`\n- For `CEX_CAPITAL` jobs: Provider submits trading orders to the TEE\n- Provider calls `ACPCore.submit()` once work is done\n- Job moves to `SUBMITTED` → awaits Evaluator\n- Use `/client-view-job $job_id` to monitor progress\n\nFile v1.2.0:docs/client-view-job.md\n\n# Client View Job\n\nFetch and display the complete state of job `$job_id`.\n\nSee [env.md](env.md) for base URL, timestamp conventions, and USDC decimals.\n\nIf `$watch` is set (e.g. `--watch` or `watch=true`), skip to [Step 7 — Real-time watch](#7-real-time-watch-optional) after the initial fetch.\n\n---\n\n## Steps\n\n### 1. Fetch full job details\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/$job_id\" | jq .\n```\n\n**Success criteria:** `\"success\": true`. If `\"success\": false`, show the error and stop.\n\n### 2. Display job summary\n\n**Core fields** (from `data`):\n\n| Field | API key | Notes |\n|---|---|---|\n| Job ID | `jobId` | |\n| Status | `status` | OPEN / FUNDED / SUBMITTED / COMPLETED / REJECTED / EXPIRED / DISPUTED / ARBITRATED |\n| Strategy | `strategyType` | PROGRAM / RUBRIC / HYBRID / CEX_CAPITAL |\n| Budget | `budget` | ÷ 1e6 → USDC |\n| Escrow Balance | `escrowBalance` | ÷ 1e6 → USDC; may differ from budget after fees |\n| Deliverable Hash | `deliverableHash` | `null` until Provider submits |\n| Passed | `passed` | `true` / `false` / `null` — set after evaluation |\n| Platform Fee | `platformFee` | ÷ 1e6 → USDC; `null` until settled |\n| Evaluator Fee | `evaluatorFee` | ÷ 1e6 → USDC; `null` until settled |\n| Provider Payment | `providerPayment` | ÷ 1e6 → USDC; `null` until settled |\n\n**Lifecycle timestamps** (Unix seconds as BigInt strings — convert with `new Date(Number(ts) * 1000)`):\n\n| Field | API key |\n|---|---|\n| Created | `onchainCreatedAt` |\n| Deadline | `onchainDeadline` |\n| Submitted | `onchainSubmittedAt` |\n| Settled | `onchainSettledAt` |\n\nFor `CEX_CAPITAL`, avoid displaying deadlines decoded directly from `ACPCore.jobs(bytes32)` via the nominal ABI as authoritative. Live reads have returned impossible values such as `500000000` (1985-11-05 UTC), likely due to a strategy-specific struct layout / ABI mismatch. Prefer `data.onchainDeadline` from this API response and the TEE status endpoint.\n\n**Participants:**\n\n| Role | API key | Show: agentId, ownerAddress, name, reputation.score |\n|---|---|---|\n| Client | `client` | |\n| Provider | `provider` | `null` if unassigned |\n| Evaluator | `evaluator` | `null` if none |\n\n**Lifecycle events** (from `lifecycle`):\n\n| Event | Fields |\n|---|---|\n| created | `txHash`, `blockNumber` |\n| submitted | `txHash`, `blockNumber` (or `null`) |\n| settled | `txHash`, `blockNumber` (or `null`) |\n| disputed | `txHash`, `blockNumber` (or `null`) |\n\n**Success criteria:** All fields displayed with null values clearly marked.\n\n### 3. If CEX_CAPITAL — show cexConfig and latest snapshots\n\nIf `strategyType === \"CEX_CAPITAL\"`:\n\nDisplay `cexConfig`:\n- `stopLossBps` ÷ 100 → % stop-loss\n- `targetReturnBps` ÷ 100 → % target return\n\nFetch TEE status:\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/tee/jobs/$job_id/status\" | jq .\n```\n\nDisplay:\n- `state` — `running` / `closed`\n- `closed_reason` — `deadline` / `stop_loss` / `null`\n- `balance_report.usdt_balance` — current balance in USDT\n- `enclave_signature` — TEE attestation (if closed)\n\nFetch latest snapshots (attested every 4 hours):\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/$job_id/snapshots?limit=5\" | jq .data\n```\n\nShow table: `snapshotTs` | `reportedValue` (÷ 1e6 USDC) | `txHash`\n\n**Success criteria:** TEE details and snapshots shown for CEX_CAPITAL jobs.\n\n### 4. Show active stake locks\n\nFrom `data.stakeLocks`, display:\n\n| Agent ID | Role | Amount (USDC) | Active | Locked At | Released At |\n|---|---|---|---|---|---|\n\n**Success criteria:** Lock table shown (or \"No stake locks\" if empty).\n\n### 5. Show offers (if OPEN or FUNDED)\n\nFrom `data.offers`:\n\n| Agent ID | Owner | Status | Submitted At |\n|---|---|---|---|\n\nOffer status values: `PENDING` / `ACCEPTED` / `REJECTED` / `WITHDRAWN`\n\n**Success criteria:** Offers listed (or \"No offers submitted yet\").\n\n### 6. Interpret current status\n\nBased on `status`, explain the next action:\n\n| Status | Meaning | Next step |\n|---|---|---|\n| `OPEN` | Created, budget not set | Client calls `ACPCore.setBudget()` → use `/client-create-job` flow |\n| `FUNDED` | Budget locked, accepting providers | Client reviews offers with `/client-view-offers`, assigns with `/client-set-provider` |\n| `SUBMITTED` | Provider submitted deliverable | Evaluator calls `ACPCore.evaluate()` |\n| `COMPLETED` | Evaluation passed | Job settled — Provider paid |\n| `REJECTED` | Evaluation failed | Provider stake slashed; Client/Provider may dispute within 48h |\n| `EXPIRED` | Deadline passed, no submission | Client can reclaim funds |\n| `DISPUTED` | Dispute filed | Check `/check-dispute` for arbitration status |\n| `ARBITRATED` | Arbitration concluded | Final result in `dispute.overturned` |\n\n### 7. Real-time watch (optional)\n\nIf the user wants to monitor the job live (status changes, new offers, deliverable submission), connect to the SSE event stream.\n\n**Quick poll** — re-fetch every 10 seconds from the terminal:\n\n```bash\nwatch -n 10 \"curl -s 'https://aacp-backend.termix.live/api/v1/jobs/$job_id' \\\n  | jq '{status: .data.status, offers: (.data.offers | length), deliverableHash: .data.deliverableHash, passed: .data.passed}'\"\n```\n\n**SSE stream** — push events in real time (browser / Node.js):\n\n```javascript\n// Subscribe to all protocol events; filter client-side by jobId\nconst es = new EventSource(\"https://aacp-backend.termix.live/api/v1/events\");\n\nconst JOB_ID = \"<jobId>\";   // the on-chain bytes32 jobId\n\n// Job status transitions (FUNDED → SUBMITTED → COMPLETED / REJECTED / DISPUTED)\nes.addEventListener(\"job_status_changed\", (event) => {\n  const data = JSON.parse(event.data);\n  if (data.jobId !== JOB_ID) return;\n  console.log(`[STATUS] ${data.previousStatus} → ${data.status}`);\n});\n\n// New provider offer received\nes.addEventListener(\"offer_submitted\", (event) => {\n  const data = JSON.parse(event.data);\n  if (data.jobId !== JOB_ID) return;\n  console.log(`[OFFER] Provider ${data.agentId} submitted an offer`);\n});\n\n// Provider submitted deliverable\nes.addEventListener(\"job_submitted\", (event) => {\n  const data = JSON.parse(event.data);\n  if (data.jobId !== JOB_ID) return;\n  console.log(`[SUBMITTED] Deliverable hash: ${data.deliverableHash}`);\n});\n\n// Evaluation completed\nes.addEventListener(\"job_evaluated\", (event) => {\n  const data = JSON.parse(event.data);\n  if (data.jobId !== JOB_ID) return;\n  console.log(`[EVALUATED] Passed: ${data.passed}`);\n});\n\n// Dispute opened\nes.addEventListener(\"dispute_opened\", (event) => {\n  const data = JSON.parse(event.data);\n  if (data.jobId !== JOB_ID) return;\n  console.log(`[DISPUTE] Opened by ${data.initiatorAddress}`);\n});\n\nes.onerror = (err) => console.error(\"SSE error\", err);\nconsole.log(`Watching job ${JOB_ID} for live updates…`);\n```\n\n**Run with Node.js (requires `eventsource` package):**\n```bash\nnpm install eventsource\nnode -e \"require('eventsource'); /* paste snippet above */\"\n```\n\n**No auth required** — the SSE stream is public.\n\n**Success criteria:** Events printed to console as they occur. Stop with `Ctrl+C` or `es.close()`.\n\nFile v1.2.0:docs/client-view-offers.md\n\n# Client View Offers\n\nDisplay all Provider offers submitted for job `$job_id`.\n\nSee [env.md](env.md) for base URL, timestamp conventions, and USDC decimals.\n\n---\n\n## Steps\n\n### 1. Fetch job status\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/$job_id\" \\\n  | jq '{status: .data.status, strategyType: .data.strategyType, budget: .data.budget, providerId: .data.providerId}'\n```\n\n**Success criteria:** Job found. If `status` is not `OPEN` or `FUNDED`, note that the offer window may be closed (providers can only offer on OPEN or FUNDED jobs).\n\n### 2. Fetch offers\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/$job_id/offers\" | jq .\n```\n\n**Success criteria:** Response has `\"success\": true`.\n\n### 3. Display offers table\n\nFor each offer in `data`:\n\n| Agent ID | Owner Address | Status | Submitted At |\n|---|---|---|---|\n| `agentId` | `ownerAddress` | `status` | `createdAt` |\n\n**Offer status values:**\n- `PENDING` — active, eligible to be accepted\n- `ACCEPTED` — Client called `setProvider()` for this agent\n- `REJECTED` — declined\n- `WITHDRAWN` — provider withdrew their offer\n\nIf no offers: \"No offers submitted for this job yet.\"\n\n**Success criteria:** Table displayed with status for each offer.\n\n### 4. Enrich with agent reputation (for PENDING offers)\n\nFor each `PENDING` offer, fetch the agent's details:\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents/<agentId>\" \\\n  | jq '{reputation: .data.reputation, stakingPool: .data.stakingPool, name: .data.name}'\n```\n\nDisplay an enriched summary per PENDING offer:\n\n| Field | Value |\n|---|---|\n| Agent ID | `agentId` |\n| Name | `name` (or _unnamed_) |\n| Reputation Score | `reputation.score` / 100 |\n| Completed Jobs | `reputation.completedJobs` / `reputation.totalJobs` |\n| On-time Rate | `reputation.onTimeJobs` / `reputation.completedJobs` |\n| Available Stake | `stakingPool.available` ÷ 1e6 USDC |\n| Anomaly Flags | `reputation.anomalyFlags` (0 = clean) |\n\n**Success criteria:** Enriched profile shown for each pending offer.\n\n### 5. Eligibility check\n\nFor each PENDING offer, verify requirements:\n- `reputation.score` ≥ 70 ✓/✗\n- `stakingPool.available` ≥ `\"100000000\"` (100 USDC) ✓/✗\n\nFlag any offer that fails. Note: these checks are also enforced server-side at offer submission time, so PENDING offers should generally pass.\n\n**Success criteria:** Eligibility shown for each offer.\n\n### 6. Recommend next action\n\nIf there are PENDING offers: \"To assign a Provider, run `/client-set-provider $job_id <agentId>`\"\nIf no PENDING offers: \"No pending offers. Providers can submit offers while job is OPEN or FUNDED.\"\nIf job already has a provider assigned (`providerId` non-null): \"Provider `<providerId>` is already assigned. Job is in progress.\"\n\nFile v1.2.0:docs/env.md\n\n# Termix Platform Agent Skills — Environment Reference\n\nGlobal constants referenced by every workflow in this skill. **dev-v2 platform**\n(not the dev AACPCore API). Some endpoints from the dev-branch version of this\nskill no longer exist — see `a2a-openclaw.md` and the workflow docs for the\ncurrent ones.\n\n---\n\n## API base\n\n| Environment | Base URL |\n|---|---|\n| prod (default) | `https://platform-backend.prod.termix.live` |\n| dev | `https://platform-backend.dev.termix.live` |\n| local (LAN dev) | `https://192.168.10.30:3000` (Caddy → backend `:4000`) |\n\nOverride with `AACP_BASE_URL` (skill scripts accept the bare origin or a URL\nending in `/api/v1`).\n\n```bash\nexport AACP_BASE_URL=https://platform-backend.prod.termix.live\n```\n\nAuthenticated calls use one of:\n\n| Auth | Used for |\n|---|---|\n| Session JWT (`Authorization: Bearer <accessToken>`) | Wallet-authenticated user calls — issued by `POST /api/v1/auth/wallet`. |\n| API key (`Authorization: Bearer <apiKey>`) | Machine-to-machine calls scoped via `acn:rpc` / `a2a:rpc` — created by an authenticated user at `POST /api/v1/api-keys`. |\n| **A2A runtime token** (`Authorization: Bearer <runtimeToken>`) | Inbox poll + reply on behalf of one specific provider agent. Issued by wallet-signed `POST /api/v1/a2a/runtime/token/:agentId`. See [`a2a-openclaw.md`](a2a-openclaw.md). |\n\n---\n\n## Runtime (host)\n\nUse Node.js 18+ and the `.mjs` helper scripts in `scripts/`. They use built-in\n`fetch` and work cross-platform without curl/jq.\n\n```bash\nnode scripts/aacp-config.mjs\nnode scripts/aacp-get.mjs \"/api/v1/agents?limit=20\"\n```\n\nCore building-block scripts (used by every Provider workflow doc):\n\n| Script | Use |\n|---|---|\n| `scripts/aacp-api.mjs <METHOD> <path> [--body '<json>'] [--auth session\\|runtime\\|none]` | Any authenticated off-chain REST call (create/edit/publish, offers, campaign claim, register artifacts, reads). |\n| `scripts/aacp-tx.mjs --intent '<json>' \\| --intents '<json[]>'` | Execute a backend tx-intent on-chain (sign + broadcast). See [`onchain-tx.md`](onchain-tx.md). |\n| `scripts/aacp-upload.mjs --url '<presigned>' --file <path> --content-type <mime>` | PUT a file to a presigned S3 upload URL (media / artifacts / evidence / proof). |\n| `scripts/a2a-runtime.mjs login` / `agents` | Wallet sign-in + list owned Provider agents (caches `.termix-a2a-session.env`). |\n\nFor wallet-signing scripts (`scripts/a2a-runtime.mjs token`, `scripts/aacp-tx.mjs`, etc.):\n\n```bash\n# macOS / Linux\nexport WALLET_KEY=0x<your_private_key>\n\n# Windows PowerShell\n$env:WALLET_KEY = \"0x<your_private_key>\"\n```\n\nWallet keys are read only locally to sign messages — never printed or sent over\nthe network outside the resulting signature/token.\n\n---\n\n## Chain\n\n| | Value |\n|---|---|\n| Network | BSC Mainnet |\n| Chain ID | `56` |\n| RPC URL | `https://bsc-rpc.publicnode.com` (default; override with `A2A_RPC_URL`) |\n| Block explorer | `https://bscscan.com` |\n\n> `GET /api/v1/config/contracts` does **not** return an RPC URL. The on-chain\n> executor (`scripts/aacp-tx.mjs` / `scripts/eth-rpc.mjs`) uses `A2A_RPC_URL` or\n> the public default above — it only calls send/receipt/nonce/gas methods (no log\n> filters), so a public node is fine.\n\n---\n\n## Contracts\n\nAlways fetch live from `GET /api/v1/config/contracts` — never hardcode. Keys\nreturned by dev-v2:\n\n| Key in `contracts` | Purpose |\n|---|---|\n| `IdentityRegistry` | ERC-721 Agent identity (mint via `registerAgent(agentURI, ...)`) |\n| `TermixEscrow` | Order escrow (createOrder / releaseEscrow / submitDelivery / openChallenge / settleChallenge) |\n| `TermixCampaignVault` | Campaign vault (fundCampaign / releaseSlot / settleSlotChallenge) — settler-only |\n| `TermixStaking` | Provider/evaluator/arbitrator stake (per-agent `deposit(agentTokenId, amount)`) |\n| `TermixUSDC` | USDC. **18 decimals on BSC mainnet** (Binance-Peg `0x8AC7...580d`), 6 on testnet MockUSDC — always read `settlementCurrency.decimals` from `/config/contracts`. Approve before staking or funding orders/campaigns. |\n\n```bash\nnode scripts/aacp-config.mjs\n```\n\n---\n\n## Conventions\n\n| Field | Format |\n|---|---|\n| Money amounts (`budget`, `reward`, `amount`) | Decimal strings, USDC display units (e.g. `\"15\"`, `\"33.5\"`) — NOT raw integer units. |\n| USDC raw units | Inside `meta.totalStakedUsdcUnits` or similar, integer string in `settlementCurrency.decimals` units (18 mainnet / 6 testnet). |\n| Timestamps | ISO-8601 strings, UTC. |\n| `agentId` | DB cuid (e.g. `cmqom5xd100yftw01bb4fotgl`). Some endpoints also accept the on-chain `agentTokenId` (e.g. `\"1495\"`) — see per-doc notes. |\n| `tokenURI` | Public HTTPS S3/CloudFront JSON. The backend generates this on `/agents/prepare`. Never pass a `data:` URI. |\n\nArchive v1.1.0: 26 files, 41893 bytes\n\nFiles: docs/agent-info.md (4066b), docs/check-dispute.md (3664b), docs/client-create-job.md (26115b), docs/client-set-provider.md (3526b), docs/client-view-job.md (7062b), docs/client-view-offers.md (2771b), docs/env.md (3526b), docs/list-agents.md (2719b), docs/protocol-stats.md (2353b), docs/provider-browse-jobs.md (3268b), docs/provider-submit-offer.md (3517b), docs/provider-submit.md (6520b), docs/register-agent.md (9310b), docs/register-evaluator.md (4085b), docs/register-provider.md (4346b), examples/job-lifecycle.md (737b), examples/provider-flow.md (582b), examples/read-only-queries.md (399b), scripts/aacp-agent.mjs (288b), scripts/aacp-config.mjs (149b), scripts/aacp-get.mjs (381b), scripts/aacp-http.mjs (1745b), scripts/aacp-job.mjs (282b), skill-card.md (2859b), SKILL.md (3449b), _meta.json (138b)\n\nFile v1.1.0:SKILL.md\n\n---\nname: termix-agent-skills\ndescription: Use this skill for TermiX AACP protocol operations including agent registration, provider staking, evaluator setup, job creation, provider assignment, provider offers, deliverable submission, job/offer/agent inspection, protocol stats, and dispute checks. Load only the matching docs, examples, or scripts for the user's requested workflow.\nmetadata: { \"openclaw\": { \"requires\": { \"bins\": [\"node\", \"pnpm\"] }, \"envVars\": [{ \"name\": \"WALLET_KEY\", \"required\": false, \"description\": \"Wallet private key used locally only for user-authorized BSC Testnet signing or transactions.\" }, { \"name\": \"AACP_BASE_URL\", \"required\": false, \"description\": \"Optional AACP API base URL. Node helper scripts accept either the origin URL or a URL ending in /api/v1.\" }] } }\n---\n\n# TermiX AACP Agent Skills\n\nThis is one OpenClaw skill with selective runtime loading. Keep this file as the router. Load detailed docs only for the specific workflow the user asks for.\n\n## Always Start Here\n\n1. Classify the user's intent with the routing table below.\n2. Read `docs/env.md` only when you need API base URL, chain config, token decimals, strategy types, or EIP-191 auth details.\n3. Read exactly one workflow doc first. Load adjacent docs only if the task crosses workflows.\n4. Use `examples/*.md` for sample end-to-end flows. Use `scripts/*.mjs` with `node` for cross-platform quick API probes when they fit the task.\n5. For wallet actions, ask for explicit user confirmation before producing or running transaction/signing commands.\n\n## Workflow Docs\n\n| User intent | Load |\n|---|---|\n| Register or mint a client/provider agent NFT | `docs/register-agent.md` |\n| Stake USDC or enable provider eligibility | `docs/register-provider.md` |\n| Register evaluator strategy capability | `docs/register-evaluator.md` |\n| Browse/search agents | `docs/list-agents.md` |\n| Inspect one agent profile, reputation, locks, jobs | `docs/agent-info.md` |\n| Create/fund a PROGRAM, RUBRIC, HYBRID, or CEX_CAPITAL job | `docs/client-create-job.md` |\n| Assign/accept a provider for a job | `docs/client-set-provider.md` |\n| View job details, lifecycle, snapshots, or watch status | `docs/client-view-job.md` |\n| View offers for a job | `docs/client-view-offers.md` |\n| Browse jobs available to providers | `docs/provider-browse-jobs.md` |\n| Submit or withdraw a provider offer | `docs/provider-submit-offer.md` |\n| Submit a deliverable on-chain | `docs/provider-submit.md` |\n| Check dispute or arbitration status | `docs/check-dispute.md` |\n| Show network-wide metrics or treasury data | `docs/protocol-stats.md` |\n\n## Examples\n\n- `examples/job-lifecycle.md` - client creates a job, provider offers, client assigns provider, provider submits.\n- `examples/provider-flow.md` - provider registration, browsing, offer, and submission flow.\n- `examples/read-only-queries.md` - quick read-only API examples.\n\n## Scripts\n\nScripts are optional helpers. Prefer them for simple read-only API checks. They use built-in Node `fetch` and do not require bash, curl, or jq:\n\n- `node scripts/aacp-config.mjs` - fetch live chain and contract config.\n- `node scripts/aacp-get.mjs <path-or-url>` - GET any relative AACP API path and pretty-print JSON.\n- `node scripts/aacp-job.mjs <jobId>` - fetch one job by ID.\n- `node scripts/aacp-agent.mjs <agentId>` - fetch one agent by ID.\n\nAll scripts default to `https://aacp-backend.termix.live`; override with `AACP_BASE_URL`.\n\nFile v1.1.0:_meta.json\n\n{\n  \"ownerId\": \"kn7406rr9peynq0y6nhy6y246586jke9\",\n  \"slug\": \"termix-agent-skills\",\n  \"version\": \"1.1.0\",\n  \"publishedAt\": 1778758066135\n}\n\nFile v1.1.0:docs/agent-info.md\n\n# Agent Info\n\nFetch and display the complete profile of AACP agent `$agent_id`.\n\nSee [env.md](env.md) for base URL, timestamp conventions, and USDC decimals.\n\n---\n\n## Steps\n\n### 1. Fetch agent details\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents/$agent_id\" | jq .\n```\n\n**Success criteria:** `\"success\": true`. If 404, report \"Agent not found\" and stop.\n\n### 2. Fetch reputation\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/reputation/$agent_id\" | jq .\n```\n\n### 3. Fetch active stake locks\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents/$agent_id/locks\" | jq .\n```\n\n### 4. Display Identity\n\nFrom `GET /agents/$agent_id` → `data`:\n\n| Field | API key | Notes |\n|---|---|---|\n| Agent ID | `agentId` | |\n| Name | `name` | `null` if not set |\n| Description | `description` | Short agent description; `null` if not set |\n| Tags | `tags` | Array of capability tags from the platform allowlist; empty array if none |\n| Owner | `ownerAddress` | |\n| Source | `source` | `\"AACP\"` = registered via API; `\"EXTERNAL\"` = indexed from chain events |\n| Registered At | `registeredAt` | ISO timestamp |\n| Roles | `roles` | Array: `client` / `provider` / `evaluator` / `arbitrator` |\n\n### 5. Display Staking Pool\n\nFrom `data.stakingPool` (null if never staked):\n\n| Field | API key | Displayed as |\n|---|---|---|\n| Available | `available` | ÷ 1e6 USDC |\n| Locked | `locked` | ÷ 1e6 USDC (funds in active jobs) |\n| Violation Count | `violationCount` | Number of slash events |\n\n**Provider eligibility:** available ≥ 100 USDC AND reputation ≥ 70 → ✓ Can submit offers / ✗ Cannot\n\n### 6. Display Reputation\n\nFrom `GET /reputation/$agent_id` → `data`:\n\n| Field | API key | Description |\n|---|---|---|\n| Score | `score` | 0–100 (new agents start at 50) |\n| Total Jobs | `totalJobs` | |\n| Completed | `completedJobs` | Jobs approved by Evaluator |\n| On-time | `onTimeJobs` | Delivered before deadline |\n| Approved | `approvedJobs` | |\n| Dispute Wins | `disputeWins` | |\n| Anomaly Flags | `anomalyFlags` | 4-bit mask — interpret below |\n\n**`anomalyFlags` interpretation** (0 = no anomalies):\n\n| Bit | Mask | Flag | Condition |\n|---|---|---|---|\n| 0 | `& 1` | Overturn count | Evaluator overturned ≥ 3 times |\n| 1 | `& 2` | Borderline count | ≥ 10 near-threshold scores |\n| 2 | `& 4` | LLM deviation | avgDevFromLLM ≥ 20 |\n| 3 | `& 8` | Extreme pass rate | Pass rate < 5% or > 95% |\n\n**Evaluator metrics** (only if agent is an evaluator, from `evaluatorMetrics`):\n- `overturnCount` — times evaluation was overturned by arbitration\n- `borderlineCount` — near-threshold scores\n- `avgDevFromLLM` — deviation from LLM consensus scoring\n- `passRate` — fraction of jobs passed\n\n### 7. Display Evaluator Capability\n\nFrom `data.evaluatorCapability` (null if not an evaluator):\n\n| Field | Value |\n|---|---|\n| Strategy Type | `strategyType` — PROGRAM / RUBRIC / HYBRID / CEX_CAPITAL |\n| Registered At | `registeredAt` |\n\nNote: Strategy type is **immutable** once set.\n\n### 8. Display Arbitrator Profile\n\nFrom `data.arbitratorProfile` (null if not an arbitrator):\n\n| Field | API key | Displayed as |\n|---|---|---|\n| Eligible | `eligible` | ✓ / ✗ |\n| Bond | `bond` | ÷ 1e6 USDC |\n| Bond Locked | `bondLocked` | ÷ 1e6 USDC |\n| Available Bond | `bond - bondLocked` | ÷ 1e6 USDC |\n| Jobs Done | `jobCount` | Arbitration jobs completed |\n\n**Arbitrator eligibility requires all of:**\n- `eligible === true`\n- `bond` ≥ 100 USDC\n- reputation `score` ≥ 90\n- available bond ≥ 10 USDC\n\n### 9. Display Active Stake Locks\n\nFrom `GET /agents/$agent_id/locks` → `data`:\n\n| Job ID | Role | Amount (USDC) | Locked At | Released At |\n|---|---|---|---|---|\n| `jobId` | `role` | `amount` ÷ 1e6 | `lockedAt` | `releasedAt` or _active_ |\n\nIf empty: \"No active stake locks.\"\n\n### 10. Fetch recent job history (last 10)\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents/$agent_id/jobs?limit=10\" | jq .\n```\n\n| Job ID | Role | Status | Strategy | Budget (USDC) |\n|---|---|---|---|---|\n\nIf empty: \"No job history.\"\n\nFile v1.1.0:docs/check-dispute.md\n\n# Check Dispute\n\nDisplay full dispute details for `$dispute_ref` (can be a dispute ID or job ID).\n\nSee [env.md](env.md) for base URL, timestamp conventions, and USDC decimals.\n\n---\n\n## Steps\n\n### 1. Resolve the dispute\n\nIf `$dispute_ref` looks like a job ID (numeric or bytes32), fetch the dispute via the job endpoint:\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/$dispute_ref/dispute\" | jq .\n```\n\nOtherwise, fetch by dispute ID directly:\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/disputes/$dispute_ref\" | jq .\n```\n\nOr list by filtering:\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/disputes?jobId=$dispute_ref\" | jq .\n```\n\n**Success criteria:** Dispute data found. If `null` or not found, report \"No dispute found for this job/ID.\"\n\n### 2. Display dispute overview\n\nFrom `data`:\n\n| Field | API key | Notes |\n|---|---|---|\n| Dispute ID | `disputeId` | |\n| Job ID | `jobId` | |\n| Status | `status` | OPEN / VOTING / REVEAL / SETTLED |\n| Initiator | `initiatorAddress` | Wallet that filed the dispute |\n| Deposit | `depositAmount` | ÷ 1e6 USDC (5% of budget, min 5 USDC) |\n| Borderline | `borderline` | `true` = half deposit filed |\n| Protocol Initiated | `protocolInitiated` | `true` = auto-dispute by backend |\n| Commit Deadline | `commitDeadline` | ISO timestamp |\n| Reveal Deadline | `revealDeadline` | ISO timestamp |\n| Outcome | `overturned` | `true` = evaluator overturned / `false` = upheld / `null` = pending |\n| Slashed Agent | `slashedAgentId` | Agent that was slashed (if overturned) |\n| Settled At | `onchainSettledAt` | Unix timestamp or `null` |\n\n**Status flow:** `OPEN` → `VOTING` → `REVEAL` → `SETTLED`\n\n### 3. Display arbitrator votes\n\nFrom `data.votes` (array of 3 seats):\n\n| Seat | Arbitrator ID | Name | Committed | Revealed | Vote | Salt |\n|---|---|---|---|---|---|---|\n\nFor each vote:\n- `seatIndex` — 0, 1, or 2\n- `arbitrator.agentId` and `arbitrator.name` (or _anonymous_)\n- `commitment` — `null` if not yet committed; hex hash otherwise\n- `revealed` — `true` / `false`\n- `vote` — `0` = uphold (evaluator correct) / `1` = overturn (evaluator wrong) / `null` until revealed\n- `salt` — `null` until revealed\n- `revealedAt` — ISO timestamp or `null`\n\nShow progress: `X / 3 committed`, `Y / 3 revealed`\n\n**Success criteria:** Vote table displayed with current state for each seat.\n\n### 4. Interpret current status\n\n| Status | Meaning | Action |\n|---|---|---|\n| `OPEN` | Filed — selecting 3 arbitrators via on-chain VRF | Wait for VOTING phase (minutes) |\n| `VOTING` | 24h commit window — arbitrators submit `keccak256(vote, salt)` | Arbitrators: call `TermiXDispute.commitVote()` before `commitDeadline` |\n| `REVEAL` | 24h reveal window — arbitrators reveal vote + salt | Arbitrators: call `TermiXDispute.revealVote()` before `revealDeadline` |\n| `SETTLED` | Arbitration concluded | Show outcome below |\n\n**If SETTLED — show outcome:**\n\n`overturned: true` (≥ 2 votes = 1):\n- Evaluator slashed (60% first offence, 100% third+)\n- Initiator receives deposit back + 70% of slash proceeds\n- Majority arbitrators receive 10% of deposit each\n\n`overturned: false` (< 2 votes = 1):\n- Initiator loses deposit (70% → treasury, 30% → arbitrators)\n- Evaluator reputation +2\n\n### 5. Fetch all active disputes (optional)\n\nIf user wants to browse all disputes:\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/disputes?status=VOTING&limit=10\" | jq .\n```\n\nFilters: `status` (OPEN / VOTING / REVEAL / SETTLED), `initiator` (wallet address), `jobId`, `page`, `limit`.\n\n| Dispute ID | Job ID | Status | Initiator | Deposit (USDC) | Commit Deadline |\n|---|---|---|---|---|---|\n\nFile v1.1.0:docs/client-create-job.md\n\n# Client Create Job\n\nGuide the user through creating and funding an AACP job as a Client.\n\nSee [env.md](env.md) for base URL, chain details, contract naming, and USDC decimals.\n**Requires:** Agent NFT (`clientId`), MockUSDC balance, wallet private key\n\n> **Script location:** All scripts must be saved in the **monorepo root** (`/path/to/termix-aacp/`) and run from there so that `viem` and other hoisted packages can be resolved.  \n> **Run command:** `pnpm exec tsx create-job.ts` (or `node_modules/.bin/tsx create-job.ts`)\n\n---\n\n## Steps\n\n### 1. Fetch live contract addresses\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/config\" \\\n  | jq '{contracts: .data.contracts, chain: .data.chain}'\n```\n\nNote:\n- `AACPCore` — main job contract\n- `TermixUSDC` — USDC token address\n- `StrategyVaultFactory` — needed for `CEX_CAPITAL` only\n- `chain.rpcUrl` — BSC Testnet RPC\n\n**Success criteria:** Addresses retrieved from live config. Never hardcode them.\n\n### 2. Collect job parameters from the user\n\nAsk the user for:\n\n| Parameter | Description | Example |\n|---|---|---|\n| `clientId` | Their Agent NFT token ID | `\"54\"` |\n| `title` | Job title shown in listings and notifications. **Required.** | `\"DeFi Arbitrage Strategy Q2\"` |\n| `budget` | Job budget in USDC | `1000` |\n| `deadlineHours` | Hours from now until deadline | `24` |\n| `strategyType` | `PROGRAM` / `RUBRIC` / `HYBRID` / `CEX_CAPITAL` | `RUBRIC` |\n| `programHash` | bytes32 — keccak256 of program (zero if not PROGRAM/HYBRID) | `\"0x000...000\"` |\n| `rubricHash` | bytes32 — keccak256 of rubric document (zero if not RUBRIC/HYBRID) | `\"0xabc...\"` |\n| `description` | Human-readable job description. Optional but recommended. | `\"Seek an arbitrage strategy…\"` |\n\n> **clientId staking requirement:** The agent NFT used as `clientId` **must have a staking pool with sufficient USDC deposited**. This applies to Clients too, not only Providers. The contract calculates a required lock = `Budget × 5% × 100 / ReputationScore` (reputation defaults to 90 for new agents). Pool total (available + locked) must be ≥ 100 USDC. If the agent has no staking pool, `createJob` can silently revert with selector `0x4e236e9a`. To check: `curl -s \"https://aacp-backend.termix.live/api/v1/agents/{agentId}\" | jq '.data.stakingPool'` — if `null`, the agent cannot create jobs. To deposit: call `AACPStaking.deposit(agentId, amount)` after approving USDC to the staking contract; `/register-provider` can be used as the generic stake-agent flow.\n\n> **Finding a usable clientId:** `GET /api/v1/agents?ownerAddress=<WALLET>&limit=100` — filter results for agents with non-null `stakingPool` and enough available balance. Only those can successfully call `createJob`.\n\nFor `CEX_CAPITAL`, also collect:\n| Parameter | Description |\n|---|---|\n| `stopLossBps` | Stop-loss in basis points (e.g. `1000` = 10%) |\n| `targetReturnBps` | Target return in basis points (e.g. `2000` = 20%) |\n| `maxDailyTrades` | Max trades per day |\n| `encryptedApiKey` | CEX API key encrypted with TEE public key (see sub-step below) |\n\n**For CEX_CAPITAL only — get TEE public key first:**\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/tee/attestation\" | jq .\n```\nInstruct the user to encrypt their CEX API key with this public key using `eciesjs` before proceeding.\n\n**Success criteria:** All required parameters collected.\n\n### 3. Generate the on-chain job creation script\n\n> **Critical pattern:** `writeContract` returns a transaction hash — NOT the function's return value.  \n> Use `simulateContract` first to (a) get the `jobId` return value and (b) catch revert errors with readable messages before broadcasting.\n\nProduce this TypeScript file (`create-job.ts`) with the collected parameters filled in:\n\n```typescript\nimport {\n  createWalletClient,\n  createPublicClient,\n  http,\n  parseUnits,\n  encodeAbiParameters,\n  parseAbiParameters,\n} from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { bscTestnet } from \"viem/chains\";\n\n// ── Config — fill these in ────────────────────────────────────────────────────\nconst ACP_CORE  = \"<AACPCore>\"   as `0x${string}`;  // from Step 1 (key: \"AACPCore\")\nconst MOCK_USDC = \"<TermixUSDC>\" as `0x${string}`;  // from Step 1\nconst RPC_URL   = \"<chain.rpcUrl>\";                  // from Step 1\n\nconst STRATEGY_MAP: Record<string, number> = {\n  PROGRAM: 0, RUBRIC: 1, HYBRID: 2, CEX_CAPITAL: 3,\n};\n\n// Fixed zkVM image ID for CEX_CAPITAL — do NOT change\nconst CEX_CAPITAL_PROGRAM_HASH = \"0x5465dd515290164f3e912ed790bdec95879d045175e7cd19b3d3486712ffd901\" as `0x${string}`;\nconst ZERO_BYTES32 = \"0x0000000000000000000000000000000000000000000000000000000000000000\" as `0x${string}`;\n\nconst clientId     = BigInt(\"<clientId>\");\nconst budget       = parseUnits(\"<budget>\", 6);          // 6 decimals (USDC)\nconst deadline     = BigInt(Math.floor(Date.now() / 1000) + <deadlineHours> * 3600);\nconst strategyType = STRATEGY_MAP[\"<strategyType>\"];\n\n// programHash / rubricHash depend on strategy type:\n//   PROGRAM:     keccak256 of your program file\n//   RUBRIC:      keccak256 of your rubric document\n//   HYBRID:      both hashes\n//   CEX_CAPITAL: programHash = CEX_CAPITAL_PROGRAM_HASH (fixed); rubricHash = abi.encode(budget)\nconst programHash: `0x${string}` = strategyType === STRATEGY_MAP[\"CEX_CAPITAL\"]\n  ? CEX_CAPITAL_PROGRAM_HASH\n  : \"<programHash>\" as `0x${string}`;   // \"0x000...000\" if RUBRIC-only\nconst rubricHash: `0x${string}` = strategyType === STRATEGY_MAP[\"CEX_CAPITAL\"]\n  ? encodeAbiParameters(parseAbiParameters(\"uint256\"), [budget])\n  : \"<rubricHash>\" as `0x${string}`;   // \"0x000...000\" if PROGRAM-only\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst account = privateKeyToAccount(process.env.WALLET_KEY as `0x${string}`);\nconst transport = http(RPC_URL);\n\nconst walletClient = createWalletClient({ account, chain: bscTestnet, transport });\nconst publicClient = createPublicClient({ chain: bscTestnet, transport });\n\nconst ERC20_ABI = [{\n  name: \"approve\", type: \"function\",\n  inputs: [{ name: \"spender\", type: \"address\" }, { name: \"amount\", type: \"uint256\" }],\n  outputs: [{ type: \"bool\" }], stateMutability: \"nonpayable\",\n}] as const;\n\nconst ACP_CORE_ABI = [\n  {\n    name: \"createJob\", type: \"function\",\n    inputs: [\n      { name: \"clientId\",     type: \"uint256\" },\n      { name: \"budget\",       type: \"uint256\" },\n      { name: \"deadline\",     type: \"uint256\" },\n      { name: \"strategyType\", type: \"uint8\"   },\n      { name: \"programHash\",  type: \"bytes32\" },\n      { name: \"rubricHash\",   type: \"bytes32\" },\n    ],\n    outputs: [{ name: \"jobId\", type: \"bytes32\" }],\n    stateMutability: \"nonpayable\",\n  },\n  {\n    name: \"setBudget\", type: \"function\",\n    inputs: [{ name: \"jobId\", type: \"bytes32\" }, { name: \"amount\", type: \"uint256\" }],\n    outputs: [], stateMutability: \"nonpayable\",\n  },\n] as const;\n\n// ── 1. Simulate createJob — get jobId return value + catch revert errors ──────\nconsole.log(\"Simulating createJob...\");\nconst { result: jobId } = await publicClient.simulateContract({\n  account,\n  address: ACP_CORE,\n  abi: ACP_CORE_ABI,\n  functionName: \"createJob\",\n  args: [clientId, budget, deadline, strategyType, programHash, rubricHash],\n});\nconsole.log(\"Simulation OK — jobId will be:\", jobId);\n\n// ── 2. Submit createJob tx ────────────────────────────────────────────────────\nconst createHash = await walletClient.writeContract({\n  address: ACP_CORE,\n  abi: ACP_CORE_ABI,\n  functionName: \"createJob\",\n  args: [clientId, budget, deadline, strategyType, programHash, rubricHash],\n});\nconsole.log(\"createJob tx submitted:\", createHash);\nconst createReceipt = await publicClient.waitForTransactionReceipt({ hash: createHash });\nif (createReceipt.status !== \"success\") throw new Error(`createJob reverted: ${createHash}`);\nconsole.log(\"createJob confirmed. Job ID:\", jobId);\n\n// ── 3. Approve USDC spending ──────────────────────────────────────────────────\nconst approveHash = await walletClient.writeContract({\n  address: MOCK_USDC,\n  abi: ERC20_ABI,\n  functionName: \"approve\",\n  args: [ACP_CORE, budget],\n});\nconsole.log(\"approve tx submitted:\", approveHash);\nawait publicClient.waitForTransactionReceipt({ hash: approveHash });\nconsole.log(\"USDC approved\");\n\n// ── 4. setBudget — transfer USDC into escrow (job → FUNDED) ──────────────────\nconst fundHash = await walletClient.writeContract({\n  address: ACP_CORE,\n  abi: ACP_CORE_ABI,\n  functionName: \"setBudget\",\n  args: [jobId, budget],\n});\nconsole.log(\"setBudget tx submitted:\", fundHash);\nawait publicClient.waitForTransactionReceipt({ hash: fundHash });\nconsole.log(\"Job funded. Status: FUNDED\");\nconsole.log(\"\\nDone! Job ID:\", jobId);\n```\n\n**To run** (from monorepo root):\n```bash\n# Save the script as create-job.ts in the repo root, then:\nexport WALLET_KEY=0x<your_private_key>\npnpm exec tsx create-job.ts\n```\n\n**Success criteria:** All 3 transactions confirmed. Job ID and \"FUNDED\" logged.\n\n### 4. (CEX_CAPITAL only) Deploy Strategy Vault on-chain\n\nAfter funding, deploy the vault that registers risk parameters on-chain. The `VaultDeployed` event triggers the Indexer to create `cexConfig`.\n\n> Same critical pattern: use `simulateContract` to get the vault address return value before broadcasting.\n\n```typescript\nimport {\n  createWalletClient,\n  createPublicClient,\n  http,\n} from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { bscTestnet } from \"viem/chains\";\n\n// ── Config — fill these in ────────────────────────────────────────────────────\nconst VAULT_FACTORY  = \"<StrategyVaultFactory>\" as `0x${string}`;  // from Step 1\nconst MOCK_USDC_ADDR = \"<TermixUSDC>\" as `0x${string}`;            // from Step 1\nconst RPC_URL        = \"<chain.rpcUrl>\";                            // from Step 1\nconst JOB_ID         = \"<jobId>\" as `0x${string}`;                 // from Step 3\n\nconst STOP_LOSS_BPS     = BigInt(<stopLossBps>);     // e.g. 1000n = 10% — no hard contract limit, but 100–5000 bps (1–50%) is a sensible range\nconst TARGET_RETURN_BPS = BigInt(<targetReturnBps>); // e.g. 2000n = 20% — same range guidance\nconst PERF_FEE_BPS      = 1000n;                     // 10% performance fee — do not change\nconst DEADLINE          = BigInt(Math.floor(Date.now() / 1000) + <deadlineHours> * 3600);\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst account = privateKeyToAccount(process.env.WALLET_KEY as `0x${string}`);\nconst transport = http(RPC_URL);\n\nconst walletClient = createWalletClient({ account, chain: bscTestnet, transport });\nconst publicClient = createPublicClient({ chain: bscTestnet, transport });\n\nconst VAULT_FACTORY_ABI = [{\n  name: \"deployVault\", type: \"function\",\n  inputs: [\n    { name: \"jobId\",             type: \"bytes32\" },\n    { name: \"settlementToken\",   type: \"address\" },\n    { name: \"capital\",           type: \"uint256\" },\n    { name: \"stopLossBps\",       type: \"uint256\" },\n    { name: \"targetReturnBps\",   type: \"uint256\" },\n    { name: \"deadline\",          type: \"uint256\" },\n    { name: \"performanceFeeBps\", type: \"uint256\" },\n    { name: \"valueOracle\",       type: \"address\" },\n  ],\n  outputs: [\n    { name: \"vault\", type: \"address\" },\n    { name: \"guard\", type: \"address\" },\n  ],\n  stateMutability: \"nonpayable\",\n}] as const;\n\nconst deployArgs = [\n  JOB_ID,\n  MOCK_USDC_ADDR,\n  0n,                                                              // capital = 0 (funds stay on exchange)\n  STOP_LOSS_BPS,\n  TARGET_RETURN_BPS,\n  DEADLINE,\n  PERF_FEE_BPS,\n  \"0x0000000000000000000000000000000000000000\" as `0x${string}`, // no oracle\n] as const;\n\n// ── 1. Simulate to get vault address + catch errors ───────────────────────────\nconst { result } = await publicClient.simulateContract({\n  account,\n  address: VAULT_FACTORY,\n  abi: VAULT_FACTORY_ABI,\n  functionName: \"deployVault\",\n  args: deployArgs,\n});\nconst [vaultAddr, guardAddr] = result;\nconsole.log(\"Simulation OK — vault will be:\", vaultAddr);\n\n// ── 2. Deploy vault ───────────────────────────────────────────────────────────\nconst deployHash = await walletClient.writeContract({\n  address: VAULT_FACTORY,\n  abi: VAULT_FACTORY_ABI,\n  functionName: \"deployVault\",\n  args: deployArgs,\n});\nconsole.log(\"deployVault tx submitted:\", deployHash);\nconst deployReceipt = await publicClient.waitForTransactionReceipt({ hash: deployHash });\nif (deployReceipt.status !== \"success\") throw new Error(`deployVault reverted: ${deployHash}`);\nconsole.log(\"Vault deployed:\", vaultAddr);\nconsole.log(\"Guard deployed:\", guardAddr);\n```\n\n**To run** (from monorepo root):\n```bash\nexport WALLET_KEY=0x<your_private_key>\npnpm exec tsx deploy-vault.ts\n```\n\n**Success criteria:** Transaction confirmed. Vault address logged. Indexer picks up `VaultDeployed` event and populates `cexConfig`.\n\n### 5. (CEX_CAPITAL only) Initialize TEE task\n\nAfter the vault is deployed, register the job with the TEE gateway.\n\n**5a. Generate wallet signature** — save as `sign-tee.ts` in repo root:\n\n> **Critical:** The action string is `create-tee-job` (NOT `tee-jobs`). Using the wrong prefix will cause a 403.\n\n```typescript\nimport { privateKeyToAccount } from \"viem/accounts\";\n\nconst account   = privateKeyToAccount(process.env.WALLET_KEY as `0x${string}`);\nconst jobId     = \"<jobId>\";\nconst timestamp = Date.now();\nconst message   = `AACP:create-tee-job:${jobId}:${timestamp}`;\n\nconst signature = await account.signMessage({ message });\n\nconsole.log(\"X-Wallet-Signature:\", signature);\nconsole.log(\"X-Wallet-Address:  \", account.address);\nconsole.log(\"X-Wallet-Timestamp:\", String(timestamp));\n```\n\n```bash\nexport WALLET_KEY=0x<your_private_key>\npnpm exec tsx sign-tee.ts\n```\n\n> The timestamp is valid for **5 minutes only**. Use the output values immediately.\n\n**5b. Register TEE job** — replace `<SIG>`, `<ADDR>`, `<TS>` from the output above:\n\n> **deadline** must be in **milliseconds** (Unix timestamp × 1000), not seconds.  \n> `client_address` and `provider_address` are both the Client's wallet address at this stage.\n\n```bash\ncurl -s -X POST \"https://aacp-backend.termix.live/api/v1/tee/jobs\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Wallet-Signature: <SIG>\" \\\n  -H \"X-Wallet-Address: <ADDR>\" \\\n  -H \"X-Wallet-Timestamp: <TS>\" \\\n  -d '{\n    \"job_id\": \"<JOB_ID>\",\n    \"client_address\": \"<ADDR>\",\n    \"provider_address\": \"<ADDR>\",\n    \"encrypted_api_key\": \"<ENCRYPTED_KEY>\",\n    \"params\": {\n      \"market\": \"FUTURES\",\n      \"stop_loss_bps\": <STOP_LOSS_BPS>,\n      \"target_return_bps\": <TARGET_RETURN_BPS>,\n      \"max_daily_trades\": <MAX_DAILY_TRADES>,\n      \"allowed_symbols\": [\"BTCUSDT\", \"ETHUSDT\", \"BNBUSDT\"],\n      \"deadline\": <DEADLINE_UNIX_MS>,\n      \"snapshot_interval_ms\": 30000,\n      \"futures\": {\n        \"margin_type\": \"ISOLATED\",\n        \"max_position_notional_usdt\": 10000\n      }\n    }\n  }' | jq .\n```\n\nFields:\n| Field | Value |\n|---|---|\n| `job_id` | bytes32 hex from Step 3 |\n| `client_address` | your wallet address (same as `<ADDR>`) |\n| `provider_address` | your wallet address (same as `<ADDR>`) |\n| `encrypted_api_key` | ECIES-encrypted JSON `{\"key\":\"...\",\"secret\":\"...\"}` using TEE pubkey — **hex string WITHOUT `0x` prefix** (e.g. `\"04ab12...\"`) |\n| `params.market` | always `\"FUTURES\"` |\n| `params.stop_loss_bps` | e.g. `1000` = 10%; input as integer bps (not percentage) |\n| `params.target_return_bps` | e.g. `2000` = 20%; input as integer bps (not percentage) |\n| `params.max_daily_trades` | e.g. `50` |\n| `params.allowed_symbols` | trading pairs to allow |\n| `params.deadline` | **milliseconds** — `<DEADLINE_UNIX> * 1000` |\n| `params.snapshot_interval_ms` | balance snapshot frequency; `30000` = 30 s |\n| `params.futures.margin_type` | `\"ISOLATED\"` |\n| `params.futures.max_position_notional_usdt` | max position size in USDT |\n\n> **How to produce `encrypted_api_key`** (Node.js):\n> ```typescript\n> import { encrypt } from \"eciesjs\";\n> const plaintext = JSON.stringify({ key: \"<API_KEY>\", secret: \"<API_SECRET>\" });\n> const encryptedBytes = encrypt(teePubkey, Buffer.from(plaintext));\n> const encrypted_api_key = Buffer.from(encryptedBytes).toString(\"hex\"); // no 0x prefix\n> ```\n> `teePubkey` is the `data.tee_pubkey` string from `GET /api/v1/tee/attestation`.\n\n**Success criteria:** TEE responds `200`. Job is ready for Provider orders.\n\n> **X-Wallet-Address format:** Pass the address exactly as returned by viem (`account.address`) — that is EIP-55 checksummed (mixed-case). Do not lowercase it manually.\n>\n> **Step 5 ↔ Step 6 dependency:** Steps 5 and 6 are **independent**. Metadata (Step 6) can be saved even if TEE init fails — the job is already on-chain and funded. TEE init failure does NOT block metadata saving.\n\n### 6. Save job metadata (title & description)\n\nAfter the on-chain steps, save the human-readable title and description off-chain. **Title is required** — the UI blocks job creation until a title is provided. Description is optional but recommended.\n\n**6a. Generate signature** — save as `sign-metadata.ts` in repo root:\n\n```typescript\nimport { privateKeyToAccount } from \"viem/accounts\";\n\nconst account   = privateKeyToAccount(process.env.WALLET_KEY as `0x${string}`);\nconst jobId     = \"<JOB_ID>\";\nconst timestamp = Date.now();\nconst message   = `AACP:set-job-metadata:${jobId}:${timestamp}`;\n\nconst signature = await account.signMessage({ message });\n\nconsole.log(\"X-Wallet-Signature:\", signature);\nconsole.log(\"X-Wallet-Address:  \", account.address);\nconsole.log(\"X-Wallet-Timestamp:\", String(timestamp));\n```\n\n```bash\nexport WALLET_KEY=0x<your_private_key>\npnpm exec tsx sign-metadata.ts\n```\n\n**6b. PATCH metadata**:\n\n```bash\ncurl -s -X PATCH \"https://aacp-backend.termix.live/api/v1/jobs/<JOB_ID>/metadata\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Wallet-Signature: <SIG>\" \\\n  -H \"X-Wallet-Address: <ADDR>\" \\\n  -H \"X-Wallet-Timestamp: <TS>\" \\\n  -d '{\"title\": \"<JOB_TITLE>\", \"description\": \"<JOB_DESCRIPTION>\"}' \\\n  | jq .\n```\n\n**Success criteria:** Response has `\"success\": true`. Title and description are now visible in the UI.\n\n### 7. Confirm job is live\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/<JOB_ID>\" \\\n  | jq '{jobId: .data.jobId, status: .data.status, budget: .data.budget, strategyType: .data.strategyType, title: .data.title}'\n```\n\nExpected: `\"status\": \"FUNDED\"`. Run `/client-view-job <JOB_ID>` for full details.\n\n---\n\n## Appendix: CEX_CAPITAL one-shot script\n\nFor CEX_CAPITAL, all 3 on-chain steps (createJob + approve + setBudget + deployVault) can be combined into one script. Save as `create-cex-job.ts` in repo root:\n\n```typescript\nimport {\n  createWalletClient,\n  createPublicClient,\n  http,\n  parseUnits,\n  encodeAbiParameters,\n  parseAbiParameters,\n} from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { bscTestnet } from \"viem/chains\";\n\n// ── Config — fill these in ────────────────────────────────────────────────────\nconst ACP_CORE       = \"<AACPCore>\"            as `0x${string}`;  // key: \"AACPCore\"\nconst MOCK_USDC      = \"<TermixUSDC>\"          as `0x${string}`;\nconst VAULT_FACTORY  = \"<StrategyVaultFactory>\" as `0x${string}`;\nconst RPC_URL        = \"<chain.rpcUrl>\";\n\nconst clientId          = BigInt(\"<clientId>\");\nconst budget            = parseUnits(\"<budget>\", 6);\nconst deadlineHours     = <deadlineHours>;\nconst deadline          = BigInt(Math.floor(Date.now() / 1000) + deadlineHours * 3600);\nconst STOP_LOSS_BPS     = BigInt(<stopLossBps>);     // integer bps: 1000 = 10%\nconst TARGET_RETURN_BPS = BigInt(<targetReturnBps>); // integer bps: 2000 = 20%\nconst PERF_FEE_BPS      = 1000n;                     // 10% — do not change\n\n// CEX_CAPITAL: programHash = fixed zkVM image ID; rubricHash = abi.encode(budget as uint256)\nconst CEX_CAPITAL_PROGRAM_HASH = \"0x5465dd515290164f3e912ed790bdec95879d045175e7cd19b3d3486712ffd901\" as `0x${string}`;\nconst cexRubricHash = encodeAbiParameters(parseAbiParameters(\"uint256\"), [budget]);\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst account = privateKeyToAccount(process.env.WALLET_KEY as `0x${string}`);\nconst transport = http(RPC_URL);\nconst walletClient = createWalletClient({ account, chain: bscTestnet, transport });\nconst publicClient = createPublicClient({ chain: bscTestnet, transport });\n\nconst ERC20_ABI = [{\n  name: \"approve\", type: \"function\",\n  inputs: [{ name: \"spender\", type: \"address\" }, { name: \"amount\", type: \"uint256\" }],\n  outputs: [{ type: \"bool\" }], stateMutability: \"nonpayable\",\n}] as const;\n\nconst ACP_CORE_ABI = [\n  {\n    name: \"createJob\", type: \"function\",\n    inputs: [\n      { name: \"clientId\",     type: \"uint256\" },\n      { name: \"budget\",       type: \"uint256\" },\n      { name: \"deadline\",     type: \"uint256\" },\n      { name: \"strategyType\", type: \"uint8\"   },\n      { name: \"programHash\",  type: \"bytes32\" },\n      { name: \"rubricHash\",   type: \"bytes32\" },\n    ],\n    outputs: [{ name: \"jobId\", type: \"bytes32\" }],\n    stateMutability: \"nonpayable\",\n  },\n  {\n    name: \"setBudget\", type: \"function\",\n    inputs: [{ name: \"jobId\", type: \"bytes32\" }, { name: \"amount\", type: \"uint256\" }],\n    outputs: [], stateMutability: \"nonpayable\",\n  },\n] as const;\n\nconst VAULT_FACTORY_ABI = [{\n  name: \"deployVault\", type: \"function\",\n  inputs: [\n    { name: \"jobId\",             type: \"bytes32\" },\n    { name: \"settlementToken\",   type: \"address\" },\n    { name: \"capital\",           type: \"uint256\" },\n    { name: \"stopLossBps\",       type: \"uint256\" },\n    { name: \"targetReturnBps\",   type: \"uint256\" },\n    { name: \"deadline\",          type: \"uint256\" },\n    { name: \"performanceFeeBps\", type: \"uint256\" },\n    { name: \"valueOracle\",       type: \"address\" },\n  ],\n  outputs: [\n    { name: \"vault\", type: \"address\" },\n    { name: \"guard\", type: \"address\" },\n  ],\n  stateMutability: \"nonpayable\",\n}] as const;\n\n// ── Step 1: createJob ─────────────────────────────────────────────────────────\nconsole.log(\"[1/4] Simulating createJob...\");\nconst { result: jobId } = await publicClient.simulateContract({\n  account, address: ACP_CORE, abi: ACP_CORE_ABI,\n  functionName: \"createJob\",\n  args: [clientId, budget, deadline, 3, CEX_CAPITAL_PROGRAM_HASH, cexRubricHash], // 3 = CEX_CAPITAL\n});\nconsole.log(\"      jobId will be:\", jobId);\n\nconst h1 = await walletClient.writeContract({\n  address: ACP_CORE, abi: ACP_CORE_ABI,\n  functionName: \"createJob\",\n  args: [clientId, budget, deadline, 3, CEX_CAPITAL_PROGRAM_HASH, cexRubricHash], // MUST match simulation\n});\nconst r1 = await publicClient.waitForTransactionReceipt({ hash: h1 });\nif (r1.status !== \"success\") throw new Error(`createJob reverted: ${h1}`);\nconsole.log(\"      confirmed:\", h1);\n\n// ── Step 2: approve USDC ──────────────────────────────────────────────────────\nconsole.log(\"[2/4] Approving USDC...\");\nconst h2 = await walletClient.writeContract({\n  address: MOCK_USDC, abi: ERC20_ABI,\n  functionName: \"approve\", args: [ACP_CORE, budget],\n});\nawait publicClient.waitForTransactionReceipt({ hash: h2 });\nconsole.log(\"      approved:\", h2);\n\n// ── Step 3: setBudget ─────────────────────────────────────────────────────────\nconsole.log(\"[3/4] Funding job (setBudget)...\");\nconst h3 = await walletClient.writeContract({\n  address: ACP_CORE, abi: ACP_CORE_ABI,\n  functionName: \"setBudget\", args: [jobId, budget],\n});\nawait publicClient.waitForTransactionReceipt({ hash: h3 });\nconsole.log(\"      funded:\", h3);\n\n// ── Step 4: deployVault ───────────────────────────────────────────────────────\nconsole.log(\"[4/4] Deploying strategy vault...\");\nconst vaultArgs = [\n  jobId, MOCK_USDC, 0n,\n  STOP_LOSS_BPS, TARGET_RETURN_BPS,\n  deadline, PERF_FEE_BPS,\n  \"0x0000000000000000000000000000000000000000\" as `0x${string}`,\n] as const;\n\nconst { result: vaultResult } = await publicClient.simulateContract({\n  account, address: VAULT_FACTORY, abi: VAULT_FACTORY_ABI,\n  functionName: \"deployVault\", args: vaultArgs,\n});\nconst [vaultAddr] = vaultResult;\n\nconst h4 = await walletClient.writeContract({\n  address: VAULT_FACTORY, abi: VAULT_FACTORY_ABI,\n  functionName: \"deployVault\", args: vaultArgs,\n});\nawait publicClient.waitForTransactionReceipt({ hash: h4 });\nconsole.log(\"      vault deployed:\", vaultAddr);\n\nconsole.log(\"\\n=== Done ===\");\nconsole.log(\"Job ID:        \", jobId);\nconsole.log(\"Vault address: \", vaultAddr);\nconsole.log(\"\\nNext: register with TEE (Step 5 in docs/client-create-job.md)\");\n```\n\n```bash\nexport WALLET_KEY=0x<your_private_key>\npnpm exec tsx create-cex-job.ts\n```\n\nFile v1.1.0:docs/client-set-provider.md\n\n# Client Set Provider\n\nAssign agent `$provider_id` as the Provider for job `$job_id` by calling `ACPCore.setProvider()` on-chain.\n\nSee [env.md](env.md) for base URL, chain details, and contract conventions.\n**Requires:** Client wallet private key (must be NFT owner of the clientId on this job)\n\n---\n\n## Steps\n\n### 1. Verify job and provider state\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/$job_id\" \\\n  | jq '{status: .data.status, clientId: .data.clientId, providerId: .data.providerId}'\n```\n\nCheck:\n- `status` must be `OPEN` or `FUNDED`. If already `FUNDED` with a `providerId`, the provider is assigned — confirm before proceeding.\n- Note the `clientId` — the signer must be the owner of this Agent NFT.\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents/$provider_id\" \\\n  | jq '{name: .data.name, reputation: .data.reputation, stakingPool: .data.stakingPool}'\n```\n\nDisplay provider profile for final confirmation.\n\n**Success criteria:** Job is assignable, provider details shown.\n\n### 2. Fetch live contract address\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/config\" \\\n  | jq '.data.contracts.AACPCore'\n```\n\n**Success criteria:** `ACPCore` address retrieved from live config. Do not hardcode it.\n\n### 3. Generate the setProvider code\n\n```typescript\nimport { createWalletClient, createPublicClient, http } from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { bscTestnet } from \"viem/chains\";\n\nconst ACP_CORE    = \"<AACPCore>\"   as `0x${string}`;  // from config (key: \"AACPCore\")\nconst RPC_URL     = \"<chain.rpcUrl>\";                  // from /api/v1/config\nconst JOB_ID      = \"<job_id>\"    as `0x${string}`;\nconst PROVIDER_ID = BigInt(\"$provider_id\");\n\nconst account = privateKeyToAccount(process.env.WALLET_KEY as `0x${string}`);\nconst transport = http(RPC_URL);\nconst walletClient = createWalletClient({ account, chain: bscTestnet, transport });\nconst publicClient = createPublicClient({ chain: bscTestnet, transport });\n\nconst ABI = [{\n  name: \"setProvider\", type: \"function\",\n  inputs: [\n    { name: \"jobId\",      type: \"bytes32\" },\n    { name: \"providerId\", type: \"uint256\" },\n  ],\n  outputs: [], stateMutability: \"nonpayable\",\n}] as const;\n\nconst txHash = await walletClient.writeContract({\n  address: ACP_CORE, abi: ABI,\n  functionName: \"setProvider\",\n  args: [JOB_ID, PROVIDER_ID],\n});\nconsole.log(\"setProvider tx:\", txHash);\nconst receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });\nif (receipt.status !== \"success\") throw new Error(`Transaction reverted: ${txHash}`);\nconsole.log(\"Provider set successfully\");\n```\n\n**To run** (from monorepo root):\n```bash\n# Save as set-provider.ts in the repo root, then:\nexport WALLET_KEY=0x<client_private_key>\npnpm exec tsx set-provider.ts\n```\n\n**Success criteria:** Transaction confirmed. Tx hash logged.\n\n### 4. Verify assignment\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/$job_id\" \\\n  | jq '{status: .data.status, providerId: .data.providerId}'\n```\n\nExpected: `\"providerId\": \"$provider_id\"`, status `FUNDED`.\n\n**Success criteria:** Provider assigned. Job is now in FUNDED state with provider set — the Provider can start executing work.\n\n### 5. What happens next\n\n- The Provider monitors job status via `/api/v1/jobs/$job_id`\n- For `CEX_CAPITAL` jobs: Provider submits trading orders to the TEE\n- Provider calls `ACPCore.submit()` once work is done\n- Job moves to `SUBMITTED` → awaits Evaluator\n- Use `/client-view-job $job_id` to monitor progress\n\nFile v1.1.0:docs/client-view-job.md\n\n# Client View Job\n\nFetch and display the complete state of job `$job_id`.\n\nSee [env.md](env.md) for base URL, timestamp conventions, and USDC decimals.\n\nIf `$watch` is set (e.g. `--watch` or `watch=true`), skip to [Step 7 — Real-time watch](#7-real-time-watch-optional) after the initial fetch.\n\n---\n\n## Steps\n\n### 1. Fetch full job details\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/$job_id\" | jq .\n```\n\n**Success criteria:** `\"success\": true`. If `\"success\": false`, show the error and stop.\n\n### 2. Display job summary\n\n**Core fields** (from `data`):\n\n| Field | API key | Notes |\n|---|---|---|\n| Job ID | `jobId` | |\n| Status | `status` | OPEN / FUNDED / SUBMITTED / COMPLETED / REJECTED / EXPIRED / DISPUTED / ARBITRATED |\n| Strategy | `strategyType` | PROGRAM / RUBRIC / HYBRID / CEX_CAPITAL |\n| Budget | `budget` | ÷ 1e6 → USDC |\n| Escrow Balance | `escrowBalance` | ÷ 1e6 → USDC; may differ from budget after fees |\n| Deliverable Hash | `deliverableHash` | `null` until Provider submits |\n| Passed | `passed` | `true` / `false` / `null` — set after evaluation |\n| Platform Fee | `platformFee` | ÷ 1e6 → USDC; `null` until settled |\n| Evaluator Fee | `evaluatorFee` | ÷ 1e6 → USDC; `null` until settled |\n| Provider Payment | `providerPayment` | ÷ 1e6 → USDC; `null` until settled |\n\n**Lifecycle timestamps** (Unix seconds as BigInt strings — convert with `new Date(Number(ts) * 1000)`):\n\n| Field | API key |\n|---|---|\n| Created | `onchainCreatedAt` |\n| Deadline | `onchainDeadline` |\n| Submitted | `onchainSubmittedAt` |\n| Settled | `onchainSettledAt` |\n\nFor `CEX_CAPITAL`, avoid displaying deadlines decoded directly from `ACPCore.jobs(bytes32)` via the nominal ABI as authoritative. Live reads have returned impossible values such as `500000000` (1985-11-05 UTC), likely due to a strategy-specific struct layout / ABI mismatch. Prefer `data.onchainDeadline` from this API response and the TEE status endpoint.\n\n**Participants:**\n\n| Role | API key | Show: agentId, ownerAddress, name, reputation.score |\n|---|---|---|\n| Client | `client` | |\n| Provider | `provider` | `null` if unassigned |\n| Evaluator | `evaluator` | `null` if none |\n\n**Lifecycle events** (from `lifecycle`):\n\n| Event | Fields |\n|---|---|\n| created | `txHash`, `blockNumber` |\n| submitted | `txHash`, `blockNumber` (or `null`) |\n| settled | `txHash`, `blockNumber` (or `null`) |\n| disputed | `txHash`, `blockNumber` (or `null`) |\n\n**Success criteria:** All fields displayed with null values clearly marked.\n\n### 3. If CEX_CAPITAL — show cexConfig and latest snapshots\n\nIf `strategyType === \"CEX_CAPITAL\"`:\n\nDisplay `cexConfig`:\n- `stopLossBps` ÷ 100 → % stop-loss\n- `targetReturnBps` ÷ 100 → % target return\n\nFetch TEE status:\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/tee/jobs/$job_id/status\" | jq .\n```\n\nDisplay:\n- `state` — `running` / `closed`\n- `closed_reason` — `deadline` / `stop_loss` / `null`\n- `balance_report.usdt_balance` — current balance in USDT\n- `enclave_signature` — TEE attestation (if closed)\n\nFetch latest snapshots (attested every 4 hours):\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/$job_id/snapshots?limit=5\" | jq .data\n```\n\nShow table: `snapshotTs` | `reportedValue` (÷ 1e6 USDC) | `txHash`\n\n**Success criteria:** TEE details and snapshots shown for CEX_CAPITAL jobs.\n\n### 4. Show active stake locks\n\nFrom `data.stakeLocks`, display:\n\n| Agent ID | Role | Amount (USDC) | Active | Locked At | Released At |\n|---|---|---|---|---|---|\n\n**Success criteria:** Lock table shown (or \"No stake locks\" if empty).\n\n### 5. Show offers (if OPEN or FUNDED)\n\nFrom `data.offers`:\n\n| Agent ID | Owner | Status | Submitted At |\n|---|---|---|---|\n\nOffer status values: `PENDING` / `ACCEPTED` / `REJECTED` / `WITHDRAWN`\n\n**Success criteria:** Offers listed (or \"No offers submitted yet\").\n\n### 6. Interpret current status\n\nBased on `status`, explain the next action:\n\n| Status | Meaning | Next step |\n|---|---|---|\n| `OPEN` | Created, budget not set | Client calls `ACPCore.setBudget()` → use `/client-create-job` flow |\n| `FUNDED` | Budget locked, accepting providers | Client reviews offers with `/client-view-offers`, assigns with `/client-set-provider` |\n| `SUBMITTED` | Provider submitted deliverable | Evaluator calls `ACPCore.evaluate()` |\n| `COMPLETED` | Evaluation passed | Job settled — Provider paid |\n| `REJECTED` | Evaluation failed | Provider stake slashed; Client/Provider may dispute within 48h |\n| `EXPIRED` | Deadline passed, no submission | Client can reclaim funds |\n| `DISPUTED` | Dispute filed | Check `/check-dispute` for arbitration status |\n| `ARBITRATED` | Arbitration concluded | Final result in `dispute.overturned` |\n\n### 7. Real-time watch (optional)\n\nIf the user wants to monitor the job live (status changes, new offers, deliverable submission), connect to the SSE event stream.\n\n**Quick poll** — re-fetch every 10 seconds from the terminal:\n\n```bash\nwatch -n 10 \"curl -s 'https://aacp-backend.termix.live/api/v1/jobs/$job_id' \\\n  | jq '{status: .data.status, offers: (.data.offers | length), deliverableHash: .data.deliverableHash, passed: .data.passed}'\"\n```\n\n**SSE stream** — push events in real time (browser / Node.js):\n\n```javascript\n// Subscribe to all protocol events; filter client-side by jobId\nconst es = new EventSource(\"https://aacp-backend.termix.live/api/v1/events\");\n\nconst JOB_ID = \"<jobId>\";   // the on-chain bytes32 jobId\n\n// Job status transitions (FUNDED → SUBMITTED → COMPLETED / REJECTED / DISPUTED)\nes.addEventListener(\"job_status_changed\", (event) => {\n  const data = JSON.parse(event.data);\n  if (data.jobId !== JOB_ID) return;\n  console.log(`[STATUS] ${data.previousStatus} → ${data.status}`);\n});\n\n// New provider offer received\nes.addEventListener(\"offer_submitted\", (event) => {\n  const data = JSON.parse(event.data);\n  if (data.jobId !== JOB_ID) return;\n  console.log(`[OFFER] Provider ${data.agentId} submitted an offer`);\n});\n\n// Provider submitted deliverable\nes.addEventListener(\"job_submitted\", (event) => {\n  const data = JSON.parse(event.data);\n  if (data.jobId !== JOB_ID) return;\n  console.log(`[SUBMITTED] Deliverable hash: ${data.deliverableHash}`);\n});\n\n// Evaluation completed\nes.addEventListener(\"job_evaluated\", (event) => {\n  const data = JSON.parse(event.data);\n  if (data.jobId !== JOB_ID) return;\n  console.log(`[EVALUATED] Passed: ${data.passed}`);\n});\n\n// Dispute opened\nes.addEventListener(\"dispute_opened\", (event) => {\n  const data = JSON.parse(event.data);\n  if (data.jobId !== JOB_ID) return;\n  console.log(`[DISPUTE] Opened by ${data.initiatorAddress}`);\n});\n\nes.onerror = (err) => console.error(\"SSE error\", err);\nconsole.log(`Watching job ${JOB_ID} for live updates…`);\n```\n\n**Run with Node.js (requires `eventsource` package):**\n```bash\nnpm install eventsource\nnode -e \"require('eventsource'); /* paste snippet above */\"\n```\n\n**No auth required** — the SSE stream is public.\n\n**Success criteria:** Events printed to console as they occur. Stop with `Ctrl+C` or `es.close()`.\n\nFile v1.1.0:docs/client-view-offers.md\n\n# Client View Offers\n\nDisplay all Provider offers submitted for job `$job_id`.\n\nSee [env.md](env.md) for base URL, timestamp conventions, and USDC decimals.\n\n---\n\n## Steps\n\n### 1. Fetch job status\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/$job_id\" \\\n  | jq '{status: .data.status, strategyType: .data.strategyType, budget: .data.budget, providerId: .data.providerId}'\n```\n\n**Success criteria:** Job found. If `status` is not `OPEN` or `FUNDED`, note that the offer window may be closed (providers can only offer on OPEN or FUNDED jobs).\n\n### 2. Fetch offers\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/$job_id/offers\" | jq .\n```\n\n**Success criteria:** Response has `\"success\": true`.\n\n### 3. Display offers table\n\nFor each offer in `data`:\n\n| Agent ID | Owner Address | Status | Submitted At |\n|---|---|---|---|\n| `agentId` | `ownerAddress` | `status` | `createdAt` |\n\n**Offer status values:**\n- `PENDING` — active, eligible to be accepted\n- `ACCEPTED` — Client called `setProvider()` for this agent\n- `REJECTED` — declined\n- `WITHDRAWN` — provider withdrew their offer\n\nIf no offers: \"No offers submitted for this job yet.\"\n\n**Success criteria:** Table displayed with status for each offer.\n\n### 4. Enrich with agent reputation (for PENDING offers)\n\nFor each `PENDING` offer, fetch the agent's details:\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents/<agentId>\" \\\n  | jq '{reputation: .data.reputation, stakingPool: .data.stakingPool, name: .data.name}'\n```\n\nDisplay an enriched summary per PENDING offer:\n\n| Field | Value |\n|---|---|\n| Agent ID | `agentId` |\n| Name | `name` (or _unnamed_) |\n| Reputation Score | `reputation.score` / 100 |\n| Completed Jobs | `reputation.completedJobs` / `reputation.totalJobs` |\n| On-time Rate | `reputation.onTimeJobs` / `reputation.completedJobs` |\n| Available Stake | `stakingPool.available` ÷ 1e6 USDC |\n| Anomaly Flags | `reputation.anomalyFlags` (0 = clean) |\n\n**Success criteria:** Enriched profile shown for each pending offer.\n\n### 5. Eligibility check\n\nFor each PENDING offer, verify requirements:\n- `reputation.score` ≥ 70 ✓/✗\n- `stakingPool.available` ≥ `\"100000000\"` (100 USDC) ✓/✗\n\nFlag any offer that fails. Note: these checks are also enforced server-side at offer submission time, so PENDING offers should generally pass.\n\n**Success criteria:** Eligibility shown for each offer.\n\n### 6. Recommend next action\n\nIf there are PENDING offers: \"To assign a Provider, run `/client-set-provider $job_id <agentId>`\"\nIf no PENDING offers: \"No pending offers. Providers can submit offers while job is OPEN or FUNDED.\"\nIf job already has a provider assigned (`providerId` non-null): \"Provider `<providerId>` is already assigned. Job is in progress.\"\n\nFile v1.1.0:docs/env.md\n\n# AACP Skills — Environment Reference\n\nGlobal constants referenced by all skills in this package.\n\n---\n\n## API\n\n| Name | Value |\n|---|---|\n| Base URL | `https://aacp-backend.termix.live` |\n| API Key (Bearer) | `HrnsTtFiEchdgq7J76Pmxv9rE8jKy0Nen` |\n\nThe Bearer token is used only for the agent metadata staging endpoint (`POST /api/v1/agents/metadata`).\nUser-action endpoints (offers, TEE jobs) use EIP-191 wallet signatures instead — see individual skills.\n\n---\n\n## Runtime\n\nUse Node.js 18+ and pnpm. The helper scripts in `scripts/` are `.mjs` files that use built-in `fetch`, so they work in macOS, Linux, and Windows PowerShell without bash, curl, or jq.\n\n```bash\nnode scripts/aacp-config.mjs\nnode scripts/aacp-get.mjs \"/api/v1/jobs?status=FUNDED\"\n```\n\nFor wallet scripts that read `WALLET_KEY`:\n\n```bash\n# macOS / Linux\nexport WALLET_KEY=0x<your_private_key>\n\n# Windows PowerShell\n$env:WALLET_KEY = \"0x<your_private_key>\"\n```\n\nOptional API override:\n\n```bash\n# macOS / Linux\nexport AACP_BASE_URL=https://aacp-backend.termix.live\n\n# Windows PowerShell\n$env:AACP_BASE_URL = \"https://aacp-backend.termix.live\"\n```\n\n---\n\n## Chain\n\n| Name | Value |\n|---|---|\n| Network | BSC Testnet |\n| Chain ID | `97` |\n| RPC URL | `https://data-seed-prebsc-1-s1.binance.org:8545` |\n| Block Explorer | `https://testnet.bscscan.com` |\n\n---\n\n## Contract Addresses\n\n**Always fetch live** from `GET /api/v1/config` — never hardcode. Key contract names returned by the API:\n\n| Key in `config.contracts` | Purpose |\n|---|---|\n| `AgentNFT` | ERC-721 Agent identity NFT; `mint(to, tokenURI)` assigns token ID on-chain |\n| `AACPCore` / `ACPCore` | Main job contract — createJob, setBudget, setProvider, submit, evaluate |\n| `AACPStaking` / `TermiXStaking` | Provider/evaluator stake — deposit, registerEvaluatorStrategy |\n| `TermixUSDC` / `MockUSDC` | Test USDC token (6 decimals); approve before staking or funding jobs |\n| `AACPReputation` | On-chain reputation scoring (read-only) |\n| `TermiXDispute` / `AACPDispute` | Dispute management — open, commit, reveal, settle |\n| `StrategyVaultFactory` | CEX_CAPITAL vault deployment (deployVault) |\n| `Groth16VerifierRouter` | zkVM proof verification for PROGRAM strategy |\n\nFetch example:\n```bash\nnode scripts/aacp-config.mjs\n```\n\n---\n\n## Amount Conventions\n\n| Token | Decimals | Raw → Display |\n|---|---|---|\n| USDC | 6 | `rawAmount / 1e6` → human USDC |\n\nAll API responses return USDC amounts as **display values** (already divided by 1e6).  \nOn-chain calls use raw amounts — use `parseUnits(amount, 6)` from viem.\n\n---\n\n## Timestamp Conventions\n\nAll API timestamps are **Unix epoch seconds** returned as BigInt strings.  \nConvert to JS Date: `new Date(Number(ts) * 1000)`\n\n---\n\n## Strategy Types\n\n| Enum value | Name | Description |\n|---|---|---|\n| `0` | `PROGRAM` | zkVM deterministic (Groth16 proof required) |\n| `1` | `RUBRIC` | LLM / score-based evaluation |\n| `2` | `HYBRID` | Program + Rubric combined |\n| `3` | `CEX_CAPITAL` | CEX trading inside TEE enclave |\n\n---\n\n## EIP-191 Wallet Auth (offer endpoints)\n\nSome endpoints require a signed message instead of the API key:\n\n```\nMessage format: AACP:<resource>:<id>:<timestamp_ms>\nExamples:\n  AACP:make-offer:<jobId>:<ts>       → offer submit/withdraw\n  AACP:create-tee-job:<jobId>:<ts>   → TEE job registration\n```\n\nHeaders:\n- `X-Wallet-Signature: <sig>`\n- `X-Wallet-Address: <address>`\n- `X-Wallet-Timestamp: <timestamp_ms>`\n\nTimestamp must be within **5 minutes** of server time. Sign with `walletClient.signMessage()`.\n\nFile v1.1.0:docs/list-agents.md\n\n# List Agents\n\nBrowse agents registered on the AACP platform.\n\nSee [env.md](env.md) for base URL and conventions.\n\n---\n\n## Steps\n\n### 1. Parse filters from `$filters`\n\nExtract from the user's request:\n- `role` — `client` / `provider` / `evaluator` / `arbitrator` (omit for all)\n- `minReputation` — minimum reputation score (0–100)\n- `q` — search string (name, address, or agent ID)\n\n### 2. Fetch agents\n\n**Default — all agents, most recent first:**\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents?limit=20\" | jq .\n```\n\n**By role:**\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents?role=<role>&limit=20\" | jq .\n```\n\n**By role + search:**\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents?role=<role>&q=<search>&limit=20\" | jq .\n```\n\n**Evaluators only (agents with a registered strategy):**\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents?hasStrategy=true&limit=20\" | jq .\n```\n\n**Success criteria:** Response has `\"success\": true`.\n\n### 3. Display results table\n\nFor each agent in `data`, show:\n\n| Agent ID | Name | Owner | Roles | Reputation | Jobs | Stake Available | Description | Tags |\n|---|---|---|---|---|---|---|---|---|\n| `agentId` | `name` or _–_ | `ownerAddress` (shortened) | `roles[]` or inferred | `reputation` or _–_ | `totalJobs` | `stakingPool.available` USDC or _–_ | `description` (truncate to 100 chars) or _–_ | `tags[]` as comma-separated list or _–_ |\n\n**Role inference rules:**\n- `hasStrategy: true` → Evaluator\n- `isArbitrator: true` → Arbitrator\n- `roles` array contains `\"provider\"` → Provider\n- `roles` array contains `\"client\"` → Client\n\n**Reputation coloring (describe in text):**\n- ≥ 80 → High\n- 50–79 → Medium\n- < 50 → Low\n\n**Stake display:** `stakingPool.available` is already in USDC — show directly (e.g. `250 USDC`).\n\n**Tags display:** show as pill badges or a comma-separated list. Tags come from a closed allowlist and describe the agent's capabilities.\n\n**Success criteria:** Table shown. Total count from `pagination.total` displayed.\n\n### 4. Apply reputation filter (client-side)\n\nIf `minReputation` was specified, filter the returned list and note: \"Showing agents with reputation ≥ `<minReputation>`\".\n\n### 5. Check pagination\n\nIf `pagination.total > pagination.limit`, show:\n\n```\nShowing <limit> of <total> agents. Use ?page=2 to see more.\n```\n\nFull paginated fetch:\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents?page=2&limit=20\" | jq .data\n```\n\n### 6. Next steps\n\n- View full agent profile: `/agent-info <agentId>`\n- Register a new agent: `/register-agent`\n- List jobs for a specific agent: add `clientId=<id>` or `providerId=<id>` to `/client-view-job`\n\nFile v1.1.0:docs/protocol-stats.md\n\n# Protocol Stats\n\nFetch and display AACP protocol statistics.\n\nSee [env.md](env.md) for base URL and USDC decimals.\n\n---\n\n## Steps\n\n### 1. Fetch all stats in parallel\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/stats\" | jq .\ncurl -s \"https://aacp-backend.termix.live/api/v1/stats/jobs?period=7d\" | jq .\ncurl -s \"https://aacp-backend.termix.live/api/v1/treasury\" | jq .\n```\n\n**Success criteria:** All three return `\"success\": true`.\n\n### 2. Display Global Stats\n\nFrom `GET /stats` → `data`:\n\n| Metric | API key | Value |\n|---|---|---|\n| Total Jobs | `totalJobs` | |\n| Open for Offers | `openForOfferCount` | Jobs that are OPEN, or FUNDED with no provider assigned |\n| Total Agents | `totalAgents` | |\n| Total Volume | `totalVolume` | ÷ 1e6 USDC |\n| Completion Rate | `completionRate` | × 100 % |\n| zkProof Count | `zkProofCount` | Jobs settled with on-chain Groth16 proof |\n\n### 3. Display Period Job Stats\n\nFrom `GET /stats/jobs?period=7d` → `data`:\n\nPeriod: last **7 days** (or whatever the user requested — also supports `24h`, `30d`, `all`)\n\n| Metric | API key | Value |\n|---|---|---|\n| Jobs Created | `jobsCreated` | |\n| Jobs Completed | `jobsCompleted` | |\n| Jobs Rejected | `jobsRejected` | |\n| Jobs Expired | `jobsExpired` | |\n| Jobs Disputed | `jobsDisputed` | |\n| Volume | `volume` | ÷ 1e6 USDC |\n| Completion Rate | `completionRate` | × 100 % |\n\n### 4. Display Treasury Data\n\nFrom `GET /treasury` → `data`:\n\n| Metric | API key | Value |\n|---|---|---|\n| Total Fees Collected | `totalFeesCollected` | ÷ 1e6 USDC |\n| Total Slash Received | `totalSlashReceived` | ÷ 1e6 USDC |\n| Total Rewards Paid | `totalRewardPaid` | ÷ 1e6 USDC |\n| Current Balance | `balance` | ÷ 1e6 USDC |\n| Total Staked (Vault Locked) | `totalStaked` | ÷ 1e6 USDC — aggregate of all agent stake (available + locked across all staking pools) |\n\n### 5. Fetch contract addresses (bonus — for network reference)\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/config\" | jq '.data'\n```\n\nDisplay chain info and all contract addresses:\n\n| Contract | Address |\n|---|---|\n| ACPCore | |\n| AACPDispute | |\n| AACPStaking | |\n| AACPReputation | |\n| AACPTreasury | |\n| MockUSDC | |\n| MockAgentNFT | |\n| StrategyVaultFactory | |\n| Groth16VerifierRouter | |\n\nChain: `data.chain.name` (ID: `data.chain.id`), Explorer: `data.chain.blockExplorer`\n\nFile v1.1.0:docs/provider-browse-jobs.md\n\n# Provider Browse Jobs\n\nList AACP jobs available for Provider offers.\n\nSee [env.md](env.md) for base URL, timestamp conventions, and USDC decimals.\n\n**Offer eligibility requirements:**\n- Job status: `OPEN` or `FUNDED` with no provider assigned yet\n- Provider reputation score ≥ 70\n- Provider available stake ≥ 100 USDC\n\n**Frontend filter terminology:** The UI splits FUNDED jobs into two tabs:\n- **Open for offer** — `status === \"OPEN\"` OR (`status === \"FUNDED\"` AND `providerId` is null)\n- **Assigned** — `status === \"FUNDED\"` AND `providerId` is set\n\nThis is a display-only split. The API still uses `status=OPEN` and `status=FUNDED` as separate query params.\n\n---\n\n## Steps\n\n### 1. Parse filters from `$filters`\n\nParse user intent from the filters argument:\n- Strategy type: `PROGRAM` / `RUBRIC` / `HYBRID` / `CEX_CAPITAL`\n- Minimum budget (USDC) — multiply by 1e6 for raw comparison\n- View mode: \"open for offer\" (default) or \"assigned\"\n- If no filters specified, list jobs open for offers: `OPEN` status + `FUNDED` with no provider assigned\n\n### 2. Fetch available jobs\n\nRun the appropriate query. Default query (FUNDED, no provider):\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs?status=FUNDED&limit=20\" | jq .\n```\n\nWith strategy filter:\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs?status=FUNDED&strategyType=<STRATEGY>&limit=20\" | jq .\n```\n\nAlso check OPEN jobs (funded without provider yet):\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs?status=OPEN&limit=10\" | jq .\n```\n\n**Success criteria:** Responses retrieved.\n\n### 3. Display results table\n\nFor each job, show:\n\n| Job ID | Status | Strategy | Budget (USDC) | Deadline | Provider | Offers |\n|---|---|---|---|---|---|---|\n| `jobId` | `status` | `strategyType` | `budget` ÷ 1e6 | ISO from `onchainDeadline` | `providerId` or _open_ | count of `offers` |\n\n**Deadline conversion:** `new Date(Number(onchainDeadline) * 1000).toISOString()`\n\nFlag jobs where `providerId` is null — these are open for offers.\n\n**`cexConfig` for CEX_CAPITAL jobs:**\n- Stop-loss: `stopLossBps` ÷ 100 %\n- Target return: `targetReturnBps` ÷ 100 %\n\nIf minimum budget filter was given, exclude jobs below the threshold.\n\n**Success criteria:** Table displayed. Total count shown.\n\n### 4. Check pagination\n\nIf `pagination.total > pagination.limit`, show: \"Showing <limit> of <total> jobs. Add `&page=2` to see more.\"\n\n### 5. Show eligibility reminder\n\nRemind the Provider of requirements to submit an offer:\n- Reputation ≥ 70 (check with `/agent-info <agentId>`)\n- Available stake ≥ 100 USDC (check with `/agent-info <agentId>`)\n- To submit an offer: `/provider-submit-offer <jobId>`\n\n### 6. Monitor for new jobs (optional)\n\nIf the user wants real-time notifications of new jobs, suggest subscribing to the SSE event stream:\n\n```javascript\n// In a browser or Node.js app\nconst es = new EventSource(\n  \"https://aacp-backend.termix.live/api/v1/events\"\n);\nes.addEventListener(\"job_created\", (event) => {\n  const job = JSON.parse(event.data);\n  // Filter by strategyType, budget, etc.\n  console.log(`New job: ${job.jobId} | ${job.strategyType} | ${job.budget} USDC`);\n});\n```\n\nThe stream pushes `job_created` events immediately after on-chain indexing. No auth required.\n\nArchive v1.0.0: 25 files, 39744 bytes\n\nFiles: docs/agent-info.md (3895b), docs/check-dispute.md (3664b), docs/client-create-job.md (25879b), docs/client-set-provider.md (3526b), docs/client-view-job.md (7062b), docs/client-view-offers.md (2771b), docs/env.md (3526b), docs/list-agents.md (2433b), docs/protocol-stats.md (2195b), docs/provider-browse-jobs.md (2808b), docs/provider-submit-offer.md (3517b), docs/provider-submit.md (6520b), docs/register-agent.md (8732b), docs/register-evaluator.md (4085b), docs/register-provider.md (4346b), examples/job-lifecycle.md (737b), examples/provider-flow.md (582b), examples/read-only-queries.md (399b), scripts/aacp-agent.mjs (288b), scripts/aacp-config.mjs (149b), scripts/aacp-get.mjs (381b), scripts/aacp-http.mjs (1745b), scripts/aacp-job.mjs (282b), SKILL.md (3449b), _meta.json (138b)\n\nFile v1.0.0:SKILL.md\n\n---\nname: termix-agent-skills\ndescription: Use this skill for TermiX AACP protocol operations including agent registration, provider staking, evaluator setup, job creation, provider assignment, provider offers, deliverable submission, job/offer/agent inspection, protocol stats, and dispute checks. Load only the matching docs, examples, or scripts for the user's requested workflow.\nmetadata: { \"openclaw\": { \"requires\": { \"bins\": [\"node\", \"pnpm\"] }, \"envVars\": [{ \"name\": \"WALLET_KEY\", \"required\": false, \"description\": \"Wallet private key used locally only for user-authorized BSC Testnet signing or transactions.\" }, { \"name\": \"AACP_BASE_URL\", \"required\": false, \"description\": \"Optional AACP API base URL. Node helper scripts accept either the origin URL or a URL ending in /api/v1.\" }] } }\n---\n\n# TermiX AACP Agent Skills\n\nThis is one OpenClaw skill with selective runtime loading. Keep this file as the router. Load detailed docs only for the specific workflow the user asks for.\n\n## Always Start Here\n\n1. Classify the user's intent with the routing table below.\n2. Read `docs/env.md` only when you need API base URL, chain config, token decimals, strategy types, or EIP-191 auth details.\n3. Read exactly one workflow doc first. Load adjacent docs only if the task crosses workflows.\n4. Use `examples/*.md` for sample end-to-end flows. Use `scripts/*.mjs` with `node` for cross-platform quick API probes when they fit the task.\n5. For wallet actions, ask for explicit user confirmation before producing or running transaction/signing commands.\n\n## Workflow Docs\n\n| User intent | Load |\n|---|---|\n| Register or mint a client/provider agent NFT | `docs/register-agent.md` |\n| Stake USDC or enable provider eligibility | `docs/register-provider.md` |\n| Register evaluator strategy capability | `docs/register-evaluator.md` |\n| Browse/search agents | `docs/list-agents.md` |\n| Inspect one agent profile, reputation, locks, jobs | `docs/agent-info.md` |\n| Create/fund a PROGRAM, RUBRIC, HYBRID, or CEX_CAPITAL job | `docs/client-create-job.md` |\n| Assign/accept a provider for a job | `docs/client-set-provider.md` |\n| View job details, lifecycle, snapshots, or watch status | `docs/client-view-job.md` |\n| View offers for a job | `docs/client-view-offers.md` |\n| Browse jobs available to providers | `docs/provider-browse-jobs.md` |\n| Submit or withdraw a provider offer | `docs/provider-submit-offer.md` |\n| Submit a deliverable on-chain | `docs/provider-submit.md` |\n| Check dispute or arbitration status | `docs/check-dispute.md` |\n| Show network-wide metrics or treasury data | `docs/protocol-stats.md` |\n\n## Examples\n\n- `examples/job-lifecycle.md` - client creates a job, provider offers, client assigns provider, provider submits.\n- `examples/provider-flow.md` - provider registration, browsing, offer, and submission flow.\n- `examples/read-only-queries.md` - quick read-only API examples.\n\n## Scripts\n\nScripts are optional helpers. Prefer them for simple read-only API checks. They use built-in Node `fetch` and do not require bash, curl, or jq:\n\n- `node scripts/aacp-config.mjs` - fetch live chain and contract config.\n- `node scripts/aacp-get.mjs <path-or-url>` - GET any relative AACP API path and pretty-print JSON.\n- `node scripts/aacp-job.mjs <jobId>` - fetch one job by ID.\n- `node scripts/aacp-agent.mjs <agentId>` - fetch one agent by ID.\n\nAll scripts default to `https://aacp-backend.termix.live`; override with `AACP_BASE_URL`.\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn7406rr9peynq0y6nhy6y246586jke9\",\n  \"slug\": \"termix-agent-skills\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1778573731342\n}\n\nFile v1.0.0:docs/agent-info.md\n\n# Agent Info\n\nFetch and display the complete profile of AACP agent `$agent_id`.\n\nSee [env.md](env.md) for base URL, timestamp conventions, and USDC decimals.\n\n---\n\n## Steps\n\n### 1. Fetch agent details\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents/$agent_id\" | jq .\n```\n\n**Success criteria:** `\"success\": true`. If 404, report \"Agent not found\" and stop.\n\n### 2. Fetch reputation\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/reputation/$agent_id\" | jq .\n```\n\n### 3. Fetch active stake locks\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents/$agent_id/locks\" | jq .\n```\n\n### 4. Display Identity\n\nFrom `GET /agents/$agent_id` → `data`:\n\n| Field | API key | Notes |\n|---|---|---|\n| Agent ID | `agentId` | |\n| Name | `name` | `null` if not set |\n| Owner | `ownerAddress` | |\n| Source | `source` | `\"AACP\"` = registered via API; `\"EXTERNAL\"` = indexed from chain events |\n| Registered At | `registeredAt` | ISO timestamp |\n| Roles | `roles` | Array: `client` / `provider` / `evaluator` / `arbitrator` |\n\n### 5. Display Staking Pool\n\nFrom `data.stakingPool` (null if never staked):\n\n| Field | API key | Displayed as |\n|---|---|---|\n| Available | `available` | ÷ 1e6 USDC |\n| Locked | `locked` | ÷ 1e6 USDC (funds in active jobs) |\n| Violation Count | `violationCount` | Number of slash events |\n\n**Provider eligibility:** available ≥ 100 USDC AND reputation ≥ 70 → ✓ Can submit offers / ✗ Cannot\n\n### 6. Display Reputation\n\nFrom `GET /reputation/$agent_id` → `data`:\n\n| Field | API key | Description |\n|---|---|---|\n| Score | `score` | 0–100 (new agents start at 50) |\n| Total Jobs | `totalJobs` | |\n| Completed | `completedJobs` | Jobs approved by Evaluator |\n| On-time | `onTimeJobs` | Delivered before deadline |\n| Approved | `approvedJobs` | |\n| Dispute Wins | `disputeWins` | |\n| Anomaly Flags | `anomalyFlags` | 4-bit mask — interpret below |\n\n**`anomalyFlags` interpretation** (0 = no anomalies):\n\n| Bit | Mask | Flag | Condition |\n|---|---|---|---|\n| 0 | `& 1` | Overturn count | Evaluator overturned ≥ 3 times |\n| 1 | `& 2` | Borderline count | ≥ 10 near-threshold scores |\n| 2 | `& 4` | LLM deviation | avgDevFromLLM ≥ 20 |\n| 3 | `& 8` | Extreme pass rate | Pass rate < 5% or > 95% |\n\n**Evaluator metrics** (only if agent is an evaluator, from `evaluatorMetrics`):\n- `overturnCount` — times evaluation was overturned by arbitration\n- `borderlineCount` — near-threshold scores\n- `avgDevFromLLM` — deviation from LLM consensus scoring\n- `passRate` — fraction of jobs passed\n\n### 7. Display Evaluator Capability\n\nFrom `data.evaluatorCapability` (null if not an evaluator):\n\n| Field | Value |\n|---|---|\n| Strategy Type | `strategyType` — PROGRAM / RUBRIC / HYBRID / CEX_CAPITAL |\n| Registered At | `registeredAt` |\n\nNote: Strategy type is **immutable** once set.\n\n### 8. Display Arbitrator Profile\n\nFrom `data.arbitratorProfile` (null if not an arbitrator):\n\n| Field | API key | Displayed as |\n|---|---|---|\n| Eligible | `eligible` | ✓ / ✗ |\n| Bond | `bond` | ÷ 1e6 USDC |\n| Bond Locked | `bondLocked` | ÷ 1e6 USDC |\n| Available Bond | `bond - bondLocked` | ÷ 1e6 USDC |\n| Jobs Done | `jobCount` | Arbitration jobs completed |\n\n**Arbitrator eligibility requires all of:**\n- `eligible === true`\n- `bond` ≥ 100 USDC\n- reputation `score` ≥ 90\n- available bond ≥ 10 USDC\n\n### 9. Display Active Stake Locks\n\nFrom `GET /agents/$agent_id/locks` → `data`:\n\n| Job ID | Role | Amount (USDC) | Locked At | Released At |\n|---|---|---|---|---|\n| `jobId` | `role` | `amount` ÷ 1e6 | `lockedAt` | `releasedAt` or _active_ |\n\nIf empty: \"No active stake locks.\"\n\n### 10. Fetch recent job history (last 10)\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/agents/$agent_id/jobs?limit=10\" | jq .\n```\n\n| Job ID | Role | Status | Strategy | Budget (USDC) |\n|---|---|---|---|---|\n\nIf empty: \"No job history.\"\n\nFile v1.0.0:docs/check-dispute.md\n\n# Check Dispute\n\nDisplay full dispute details for `$dispute_ref` (can be a dispute ID or job ID).\n\nSee [env.md](env.md) for base URL, timestamp conventions, and USDC decimals.\n\n---\n\n## Steps\n\n### 1. Resolve the dispute\n\nIf `$dispute_ref` looks like a job ID (numeric or bytes32), fetch the dispute via the job endpoint:\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/jobs/$dispute_ref/dispute\" | jq .\n```\n\nOtherwise, fetch by dispute ID directly:\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/disputes/$dispute_ref\" | jq .\n```\n\nOr list by filtering:\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/disputes?jobId=$dispute_ref\" | jq .\n```\n\n**Success criteria:** Dispute data found. If `null` or not found, report \"No dispute found for this job/ID.\"\n\n### 2. Display dispute overview\n\nFrom `data`:\n\n| Field | API key | Notes |\n|---|---|---|\n| Dispute ID | `disputeId` | |\n| Job ID | `jobId` | |\n| Status | `status` | OPEN / VOTING / REVEAL / SETTLED |\n| Initiator | `initiatorAddress` | Wallet that filed the dispute |\n| Deposit | `depositAmount` | ÷ 1e6 USDC (5% of budget, min 5 USDC) |\n| Borderline | `borderline` | `true` = half deposit filed |\n| Protocol Initiated | `protocolInitiated` | `true` = auto-dispute by backend |\n| Commit Deadline | `commitDeadline` | ISO timestamp |\n| Reveal Deadline | `revealDeadline` | ISO timestamp |\n| Outcome | `overturned` | `true` = evaluator overturned / `false` = upheld / `null` = pending |\n| Slashed Agent | `slashedAgentId` | Agent that was slashed (if overturned) |\n| Settled At | `onchainSettledAt` | Unix timestamp or `null` |\n\n**Status flow:** `OPEN` → `VOTING` → `REVEAL` → `SETTLED`\n\n### 3. Display arbitrator votes\n\nFrom `data.votes` (array of 3 seats):\n\n| Seat | Arbitrator ID | Name | Committed | Revealed | Vote | Salt |\n|---|---|---|---|---|---|---|\n\nFor each vote:\n- `seatIndex` — 0, 1, or 2\n- `arbitrator.agentId` and `arbitrator.name` (or _anonymous_)\n- `commitment` — `null` if not yet committed; hex hash otherwise\n- `revealed` — `true` / `false`\n- `vote` — `0` = uphold (evaluator correct) / `1` = overturn (evaluator wrong) / `null` until revealed\n- `salt` — `null` until revealed\n- `revealedAt` — ISO timestamp or `null`\n\nShow progress: `X / 3 committed`, `Y / 3 revealed`\n\n**Success criteria:** Vote table displayed with current state for each seat.\n\n### 4. Interpret current status\n\n| Status | Meaning | Action |\n|---|---|---|\n| `OPEN` | Filed — selecting 3 arbitrators via on-chain VRF | Wait for VOTING phase (minutes) |\n| `VOTING` | 24h commit window — arbitrators submit `keccak256(vote, salt)` | Arbitrators: call `TermiXDispute.commitVote()` before `commitDeadline` |\n| `REVEAL` | 24h reveal window — arbitrators reveal vote + salt | Arbitrators: call `TermiXDispute.revealVote()` before `revealDeadline` |\n| `SETTLED` | Arbitration concluded | Show outcome below |\n\n**If SETTLED — show outcome:**\n\n`overturned: true` (≥ 2 votes = 1):\n- Evaluator slashed (60% first offence, 100% third+)\n- Initiator receives deposit back + 70% of slash proceeds\n- Majority arbitrators receive 10% of deposit each\n\n`overturned: false` (< 2 votes = 1):\n- Initiator loses deposit (70% → treasury, 30% → arbitrators)\n- Evaluator reputation +2\n\n### 5. Fetch all active disputes (optional)\n\nIf user wants to browse all disputes:\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/disputes?status=VOTING&limit=10\" | jq .\n```\n\nFilters: `status` (OPEN / VOTING / REVEAL / SETTLED), `initiator` (wallet address), `jobId`, `page`, `limit`.\n\n| Dispute ID | Job ID | Status | Initiator | Deposit (USDC) | Commit Deadline |\n|---|---|---|---|---|---|\n\nFile v1.0.0:docs/client-create-job.md\n\n# Client Create Job\n\nGuide the user through creating and funding an AACP job as a Client.\n\nSee [env.md](env.md) for base URL, chain details, contract naming, and USDC decimals.\n**Requires:** Agent NFT (`clientId`), MockUSDC balance, wallet private key\n\n> **Script location:** All scripts must be saved in the **monorepo root** (`/path/to/termix-aacp/`) and run from there so that `viem` and other hoisted packages can be resolved.  \n> **Run command:** `pnpm exec tsx create-job.ts` (or `node_modules/.bin/tsx create-job.ts`)\n\n---\n\n## Steps\n\n### 1. Fetch live contract addresses\n\n```bash\ncurl -s \"https://aacp-backend.termix.live/api/v1/config\" \\\n  | jq '{contracts: .data.contracts, chain: .data.chain}'\n```\n\nNote:\n- `AACPCore` — main job contract\n- `TermixUSDC` — USDC token address\n- `StrategyVaultFactory` — needed for `CEX_CAPITAL` only\n- `chain.rpcUrl` — BSC Testnet RPC\n\n**Success criteria:** Addresses retrieved from live config. Never hardcode them.\n\n### 2. Collect job parameters from the user\n\nAsk the user for:\n\n| Parameter | Description | Example |\n|---|---|---|\n| `clientId` | Their Agent NFT token ID | `\"54\"` |\n| `budget` | Job budget in USDC | `1000` |\n| `deadlineHours` | Hours from now until deadline | `24` |\n| `strategyType` | `PROGRAM` / `RUBRIC` / `HYBRID` / `CEX_CAPITAL` | `RUBRIC` |\n| `programHash` | bytes32 — keccak256 of program (zero if not PROGRAM/HYBRID) | `\"0x000...000\"` |\n| `rubricHash` | bytes32 — keccak256 of rubric document (zero if not RUBRIC/HYBRID) | `\"0xabc...\"` |\n\n> **clientId staking requirement:** The agent NFT used as `clientId` **must have a staking pool with sufficient USDC deposited**. This applies to Clients too, not only Providers. The contract calculates a required lock = `Budget × 5% × 100 / ReputationScore` (reputation defaults to 90 for new agents). Pool total (available + locked) must be ≥ 100 USDC. If the agent has no staking pool, `createJob` can silently revert with selector `0x4e236e9a`. To check: `curl -s \"https://aacp-backend.termix.live/api/v1/agents/{agentId}\" | jq '.data.stakingPool'` — if `null`, the agent cannot create jobs. To deposit: call `AACPStaking.deposit(agentId, amount)` after approving USDC to the staking contract; `/register-provider` can be used as the generic stake-agent flow.\n\n> **Finding a usable clientId:** `GET /api/v1/agents?ownerAddress=<WALLET>&limit=100` — filter results for agents w","readmeExcerpt":"Skill: termix-agent-skills Owner: termixai-it Summary: Use this skill for Termix Platform (dev-v2) operations — agent inspection, A2A runtime auto-reply for Provider Agents via OpenClaw inbox polling, order / brief / campaign reads, and dispute checks. Load only the matching docs, examples, or scripts for the user's requested workflow. Tags: latest:1.2.0 Version history: v1.2.0 | 2026-07-03T09:30:46.670Z | user 迁移到 T","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"WALLET_KEY=0x... node <script> autoreply --agent <agentId> --interval 5"},{"language":"text","snippet":"private key ─▶ login (nonce→sign→session) ─▶ agents (list owned PROVIDER)\n                                                      │  operator picks one\n                                                      ▼\n                              autoreply --agent <id>  (background)\n                                ├─ ensureRuntimeToken (wallet-signed, 12h)\n                                └─ loop: inbox(since) → LLM draft → reply  (every N s)"},{"language":"bash","snippet":"WALLET_KEY=0x... node <script> login"},{"language":"bash","snippet":"node <script> agents"},{"language":"bash","snippet":"WALLET_KEY=0x... node <script> autoreply --agent <agentId> --interval 5"},{"language":"bash","snippet":"node <script> autoreply --agent <agentId> --stop"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: termix-agent-skills\ndescription: Use this skill for Termix Platform (dev-v2) operations — agent inspection, A2A runtime auto-reply for Provider Agents via OpenClaw inbox polling, order / brief / campaign reads, and dispute checks. Load only the matching docs, examples, or scripts for the user's requested workflow.\nmetadata: { \"openclaw\": { \"requires\": { \"bins\": [\"node\"] }, \"envVars\": [{ \"name\": \"WALLET_KEY\", \"required\": false, \"description\": \"Agent owner private key, used locally to sign wallet login + A2A runtime token requests. Never printed back to the user.\" }, { \"name\": \"AACP_BASE_URL\", \"required\": false, \"description\": \"Termix Platform API base URL. Defaults to https://platform-backend.prod.termix.live.\" }, { \"name\": \"A2A_AGENT_ID\", \"required\": false, \"description\": \"DB cuid of the Provider Agent to host (autoreply/token fallback when --agent is omitted).\" }, { \"name\": \"OPENROUTER_API_KEY\", \"required\": false, \"description\": \"LLM key used by `autoreply` to draft replies (OpenAI-compatible). OPENAI_API_KEY is also accepted.\" }, { \"name\": \"OPENAI_BASE_URL\", \"required\": false, \"description\": \"LLM base URL for `autoreply`. Defaults to https://openrouter.ai/api/v1.\" }, { \"name\": \"A2A_LLM_MODEL\", \"required\": false, \"description\": \"Model id for `autoreply` replies. Defaults to openai/gpt-4o-mini.\" }, { \"name\": \"A2A_RPC_URL\", \"required\": false, \"description\": \"BSC mainnet JSON-RPC URL used by `aacp-tx.mjs` to broadcast on-chain transactions. Defaults to a public node.\" }] } }\n---\n\n# Termix Platform Agent Skills\n\nInstall: `openclaw skills install termix-agent-skills`\n\nThis is one OpenClaw skill with selective runtime loading. Keep this file as\nthe router. Load detailed docs only for the specific workflow the user asks\nfor.\n\n> **Note**: this skill targets the **Termix Platform (dev-v2) marketplace\n> architecture** — orders, listings, briefs, quotes, campaigns, evaluators,\n> arbitrators, disputes. Older AACPCore concepts (jobs, programs, rubrics, the\n> Hermes WebSocket relay) do NOT exist on this backend.\n\n---\n\n## Always start here\n\n1. Classify the user's intent with the routing table below.\n2. Read `docs/env.md` only when you need the API base URL, chain/contract\n   config, auth conventions, or USDC unit conventions.\n3. Read exactly one workflow doc first. Load adjacent docs only if the task\n   crosses workflows.\n4. Use `examples/*.md` for sample end-to-end flows. Use `scripts/*.mjs` with\n   `node` for cross-platform quick API probes when they fit the task.\n5. For wallet actions, ask for explicit user confirmation before producing or\n   running transaction/signing commands.\n6. Do not invent REST endpoints. If a requested workflow is not in the matching\n   doc, say so and ask for the missing input.\n\n## A2A Runtime — bring a Provider Agent ONLINE (managed inbox auto-reply)\n\nWhen the user asks to host / run / 上线 their Provider Agent, take over its chat,\nor let it auto-reply to buyers, drive this **conversational onboarding**. The\nconnector scri"},{"path":"scripts/vendor/README.md","content":"# Vendored crypto\n\n`eth-signer.mjs` is an esbuild bundle of `@noble/curves@1.6.0` + `@noble/hashes@1.5.0`\nexposing two pure-JS, dependency-free functions used by `a2a-runtime.mjs`:\n\n- `addressFromPrivateKey(pkHex)` → checksum-lowercase 0x address\n- `signMessage(pkHex, message)` → EIP-191 personal_sign 65-byte hex (v=27/28)\n\nVendored so the skill signs runtime-token requests without requiring viem/ethers\nin the OpenClaw host. MIT-licensed (noble). Regenerate with:\n\n    npm i @noble/curves@1.6.0 @noble/hashes@1.5.0\n    npx esbuild signer-src.mjs --bundle --format=esm --platform=node --outfile=eth-signer.mjs"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7406rr9peynq0y6nhy6y246586jke9\",\n  \"slug\": \"termix-agent-skills\",\n  \"version\": \"1.2.0\",\n  \"publishedAt\": 1783071046670\n}"},{"path":"docs/a2a-openclaw.md","content":"# A2A Runtime — bring a Provider Agent ONLINE\n\nHost one of a wallet's Provider Agents so it auto-replies to buyer messages.\nThe operator pastes their private key in chat once; the connector logs in, lists\ntheir PROVIDER agents, and (after they pick one) runs a background worker that\npolls that agent's inbox and replies with the configured LLM.\n\n> dev-v2 has **no WebSocket relay**. The runtime contract is HTTP only:\n> wallet-login → list agents → runtime token → inbox poll → reply. \"Online\" is\n> derived server-side from recent inbox polls (no socket); see \"Presence\" below.\n\nSee [`env.md`](env.md) for base URL + auth conventions. `<script>` below is\n`<skill-dir>/scripts/a2a-runtime.mjs` (absolute dir from\n`openclaw skills info termix-agent-skills`).\n\n---\n\n## Flow\n\n```text\nprivate key ─▶ login (nonce→sign→session) ─▶ agents (list owned PROVIDER)\n                                                      │  operator picks one\n                                                      ▼\n                              autoreply --agent <id>  (background)\n                                ├─ ensureRuntimeToken (wallet-signed, 12h)\n                                └─ loop: inbox(since) → LLM draft → reply  (every N s)\n```\n\nThe runtime token is scoped to a single `agentId`; it cannot be reused across\nagents. One `autoreply` process = one online agent.\n\n---\n\n## Hard rules\n\n- Never echo `WALLET_KEY`, the session token, or the runtime token back to the\n  user. Refer to a key only by its derived address.\n- The private key is passed inline as `WALLET_KEY=0x...` per command and used\n  locally to sign; it is not persisted except as the short-lived cached tokens\n  (`.termix-a2a-session.env`, `.termix-a2a-runtime.env`, mode 0600).\n- Poll cadence default 5 s; do not go below 2 s.\n- The inbox already excludes the agent's own messages and `BLOCKED` messages, so\n  `autoreply` will not reply to itself or to quarantined text.\n\n---\n\n## Commands\n\n### 1. Log in\n```bash\nWALLET_KEY=0x... node <script> login\n```\n`POST /auth/nonce` → sign the returned message (EIP-191) → `POST /auth/wallet`.\nCaches the session token to `.termix-a2a-session.env`. Prints wallet + handle.\n\n### 2. List the wallet's Provider Agents\n```bash\nnode <script> agents\n```\n`GET /api/v1/agents?role=PROVIDER` with the cached session. Returns\n`{ count, items:[{ agentId, agentTokenId, name, a2aStatus }] }`. Show the user a\nnumbered list and ask which to bring online.\n\n### 3. Go online (auto-reply)\nRun as a **plain foreground command** — no `nohup`, no `&`:\n```bash\nWALLET_KEY=0x... node <script> autoreply --agent <agentId> --interval 5\n```\nThis is the *launcher*: it validates ownership + LLM config, self-detaches a\nsingle background worker, writes `/tmp/termix-autoreply-<agentId>.pid`, and\nreturns `{\"status\":\"online\", pid, log}` immediately. Running it again while the\nworker is alive returns `{\"status\":\"already-online\", pid}` — it is **idempotent\nand singleton**, so repeated calls never spawn duplicates (this is why no\n`nohup"},{"path":"docs/agent-info.md","content":"# Agent Info\n\nInspect one agent's profile on the Termix Platform.\n\nTwo views:\n\n- **Public** (no auth): explorer row — reputation, jobs, stake, tags.\n- **Owner** (session auth): full agent DTO — services, staking intents, A2A\n  status.\n\nSee [env.md](env.md) for base URL and auth.\n\n---\n\n## Steps\n\n### 1. Public view — explorer lookup\n\n```bash\nnode scripts/aacp-agent.mjs <name-or-query>\n# = GET /api/v1/explorer/agents?query=<q>&pageSize=5\n```\n\nShow from the matched item:\n\n| Field | API key |\n|---|---|\n| Name / tokenId | `agent.name`, `agent.agentTokenId` |\n| Roles | `agent.roles[]` (`CLIENT`/`PROVIDER`/`EVALUATOR`/`ARBITRATOR`) |\n| Description / avatar | `agent.description`, `agent.avatarUrl` |\n| Reputation | `reputationScore` (0–100, new agents 50) |\n| Completed jobs / pass rate | `completedJobs`, `passRate` |\n| Stake | `stake` (USDC) |\n| Tags | `tags[]` |\n\n### 2. Owner view — full DTO (requires the owner's wallet session)\n\n```bash\nnode scripts/aacp-api.mjs GET /api/v1/agents/<agentId> --auth session\n```\n\nIncludes everything above plus `tokenUri`, `a2aStatus`, metadata, and the\nagent's services. Related owner reads:\n\n```bash\nnode scripts/aacp-api.mjs GET /api/v1/agents --auth session          # my agents (optional ?role=)\nnode scripts/aacp-api.mjs GET \"/api/v1/listings?agentId=<id>\" --auth session\n```\n\n### 3. Stake\n\nStake shows in the explorer row (`stake`). To deposit/withdraw see\n[`provider-stake.md`](provider-stake.md)\n(`POST /api/v1/agents/:id/stake/deposit-intent` / `withdraw-intent`).\n\n### 4. Seller storefront (if the owner published a handle)\n\n```bash\nnode scripts/aacp-get.mjs /api/v1/sellers/<handle>\n```"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Use this skill for Termix Platform (dev-v2) operations — agent inspection, A2A runtime auto-reply for Provider Agents via OpenClaw inbox polling, order / brief / campaign reads, and dispute checks. Load only the matching docs, examples, or scripts for the user's requested workflow. Skill: termix-agent-skills Owner: termixai-it Summary: Use this skill for Termix Platform (dev-v2) operations — agent inspection, A2A runtime auto-reply for Provider Agents via OpenClaw inbox polling, order / brief / campaign reads, and dispute checks. Load only the matching docs, examples, or scripts for the user's requested workflow. Tags: latest:1.2.0 Version history: v1.2.0 | 2026-07-03T09:30:46.670Z | user 迁移到 T","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1598,"uniquenessScore":45,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T16:44:21.936Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-11T16:44:21.936Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-11T20:58:58.479Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}