diff --git a/.github/workflows/showcase-acp-sdk-v2-to-v3-migration.yml b/.github/workflows/showcase-acp-sdk-v2-to-v3-migration.yml new file mode 100644 index 0000000..44da2c1 --- /dev/null +++ b/.github/workflows/showcase-acp-sdk-v2-to-v3-migration.yml @@ -0,0 +1,106 @@ +name: showcase acp-sdk-v2-to-v3-migration + +on: + pull_request: + paths: + - 'showcase/acp-sdk-v2-to-v3-migration/**' + - '.github/workflows/showcase-acp-sdk-v2-to-v3-migration.yml' + push: + branches: + - main + - 'feat/acp-sdk-v2-to-v3-migration' + paths: + - 'showcase/acp-sdk-v2-to-v3-migration/**' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: showcase-acp-sdk-v2-to-v3-migration-${{ github.ref }} + cancel-in-progress: true + +jobs: + package-check: + name: package self-check + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Report contributor association + env: + GH_ASSOCIATION: ${{ github.event.pull_request.author_association || 'N/A' }} + DECLARED_ASSOCIATION: champion + shell: bash + run: | + set -euo pipefail + echo "github author_association : ${GH_ASSOCIATION}" + echo "declared association : ${DECLARED_ASSOCIATION}" + { + echo "### Contributor association" + echo "" + echo "| field | value |" + echo "| --- | --- |" + echo "| github \`author_association\` | \`${GH_ASSOCIATION}\` |" + echo "| declared (manifest \`builder.association\`) | \`${DECLARED_ASSOCIATION}\` |" + echo "" + echo "\`author_association\` is derived by GitHub from repository permissions and" + echo "cannot be set by a contributor. \`champion\` is a manifest-level declaration only." + } >> "$GITHUB_STEP_SUMMARY" + + - name: Assert declared association is champion + shell: bash + run: | + set -euo pipefail + manifest=showcase/acp-sdk-v2-to-v3-migration/showcase.json + declared="$(node -e "const m=require('./'+process.argv[1]);process.stdout.write(String(m.builder&&m.builder.association))" "$manifest")" + echo "builder.association = ${declared}" + test "${declared}" = "champion" + + - name: Validate showcase manifests + run: node scripts/validate-showcase.mjs + + - name: Syntax-check package sources + working-directory: showcase/acp-sdk-v2-to-v3-migration + run: | + set -euo pipefail + node --check examples/v3-provider.mjs + node --check examples/v3-client.mjs + node --check examples/phase-event-map.mjs + node --check examples/v2-provider.legacy.mjs + + - name: Run package offline self-check + working-directory: showcase/acp-sdk-v2-to-v3-migration + run: npm run check + + - name: Assert skill is installable + shell: bash + run: | + set -euo pipefail + skill=showcase/acp-sdk-v2-to-v3-migration/skills/acp-sdk-v2-to-v3-migration + test -f "${skill}/SKILL.md" + tmp_home="$(mktemp -d)" + mkdir -p "${tmp_home}/.agents/skills" "${tmp_home}/.claude/skills" + cp -R "${skill}" "${tmp_home}/.agents/skills/" + cp -R "${skill}" "${tmp_home}/.claude/skills/" + test -f "${tmp_home}/.agents/skills/acp-sdk-v2-to-v3-migration/SKILL.md" + test -f "${tmp_home}/.claude/skills/acp-sdk-v2-to-v3-migration/SKILL.md" + rm -rf "${tmp_home}" + + - name: Assert proof artifacts present + shell: bash + run: | + set -euo pipefail + cd showcase/acp-sdk-v2-to-v3-migration + test -f proof/jk-drq-piano-spaces.md + test -f proof/offline-validation.md + test -f proof/telegram-qchaingoldbot.md + test -f assets/poster.png + test -f soul.md diff --git a/README.md b/README.md index ba8b580..58cbe7e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ After a Showcase PR is approved and merged to `main`, changes under regenerates the Showcase page data from the accepted manifest. Automation requirement: configure `SHOWCASE_SYNC_TOKEN` in this repo with -permission to dispatch workflows in `Virtual-Protocol/whitepaper-economyOS`. +permission to dispatch workflows in the EconomyOS docs repo. Validate manifests before requesting review: diff --git a/scripts/validate-showcase.mjs b/scripts/validate-showcase.mjs index 3186d58..0720a84 100644 --- a/scripts/validate-showcase.mjs +++ b/scripts/validate-showcase.mjs @@ -1,5 +1,10 @@ #!/usr/bin/env node +// Validates showcase manifests against the shape documented in +// showcase/README.md. MAINTAINERS: if you add, remove, or change a rule here, +// update the "Field Reference" table and the "Maintainers" section in +// showcase/README.md in the same PR so the docs stay the source of truth. + import fs from 'node:fs' import path from 'node:path' import process from 'node:process' diff --git a/showcase/README.md b/showcase/README.md index b9ce834..2ee821e 100644 --- a/showcase/README.md +++ b/showcase/README.md @@ -5,57 +5,117 @@ Showcase after the contribution PR is approved and merged. Each public entry lives in `showcase//`. The manifest is `showcase//showcase.json`, and the same folder can contain the -proof notes, project-specific skill, artifacts, and reviewer context needed by -the docs site. +proof notes, project-specific skill, artifacts, image assets, and reviewer +context needed by the docs site. + +New here? Read [End-To-End Flow](#end-to-end-flow), fill in the +[Field Reference](#field-reference), then run the +[Contributor Checklist](#contributor-checklist) before opening your PR. +Copy [`paid-substack-subscription/showcase.json`](paid-substack-subscription/showcase.json) +as a starting point. ## End-To-End Flow 1. Build a real EconomyOS workflow and capture public proof. -2. Add the project package under `showcase//`. +2. Add the project package under `showcase//`. The folder name + **must** match the manifest `slug`. 3. Add a project-specific reusable skill under `showcase//skills//` when the workflow can be repeated. Use top-level `skills//` only when the skill is shared across projects. -4. Open a PR against `Virtual-Protocol/acp-cli-demos`. -5. Reviewers check the demo package, redaction, skill quality, and manifest. -6. After merge to `main`, the sync workflow publishes the manifest into the - EconomyOS docs Showcase data. +4. Validate locally: `node scripts/validate-showcase.mjs`. +5. Open a PR against `Virtual-Protocol/acp-cli-demos`. +6. Reviewers check the demo package, redaction, skill quality, and manifest + (see [What Reviewers Check](#what-reviewers-check)). +7. After merge to `main`, the sync workflow publishes the manifest into the + EconomyOS docs Showcase data. Confirm it landed — see + [Confirming Your Card Went Live](#confirming-your-card-went-live). The publish step requires the `SHOWCASE_SYNC_TOKEN` repository secret to be set in `acp-cli-demos`. It should be a GitHub token that can create a repository -dispatch event in `Virtual-Protocol/whitepaper-economyOS`. +dispatch event in the EconomyOS docs repo. + +## Field Reference + +This table is the source of truth for the manifest shape. Every rule here is +enforced by [`scripts/validate-showcase.mjs`](../scripts/validate-showcase.mjs); +run it before requesting review. + +| Field | Required | Type | Notes | +| --- | --- | --- | --- | +| `slug` | yes | string | Lowercase kebab-case (`^[a-z0-9]+(-[a-z0-9]+)*$`). **Must equal the folder name** `showcase//`. **Globally unique** across all manifests. This is the card's identity key — do not rename it later (see [Updating a Published Showcase](#updating-a-published-showcase)). | +| `title` | yes | string | Display name of the project. | +| `tagline` | yes | string | One line. See [Copy and Style Conventions](#copy-and-style-conventions). | +| `description` | yes | string | 2–4 sentences. See [Copy and Style Conventions](#copy-and-style-conventions). | +| `status` | yes | string | Free text, e.g. `live`, `active`, `validated demo`. | +| `topic` | yes | string | Single primary category. Current values in use: `agents`, `skills`, `commerce`, `security`. Pick the closest one. | +| `topics` | yes | string[] | Non-empty list of search/filter tags. Distinct from `topic` — these are the free-form tags; `topic` is the one primary bucket. | +| `builder.name` | yes | string | | +| `builder.url` | yes | URL | `http(s)://` | +| `links.repo` | yes | URL | Source or package location. | +| `links.share` | yes | URL | The post/page to share. | +| `links.feedback` | yes | URL | Where feedback goes (issue link is fine). | +| `links.demo` | no | URL | Optional live demo / proof link. | +| `links.video` | no | URL | Public watch page. Setting this makes `visual.videoLabel` required. See [Video Fields](#video-fields). | +| `primitives` | yes | string[] | Non-empty. Allowed: `wallet`, `email`, `card`, `token`, `acp`. | +| `visual.kind` | yes | string | Short descriptor of the card kind. | +| `visual.eyebrow` | yes | string | Small label above the title on the card. | +| `visual.title` | yes | string | Card headline. | +| `visual.posterUrl` | no | URL | Card hero image. See [Card Image](#card-image). | +| `visual.videoUrl` | no | URL | Direct video **file** only. See [Video Fields](#video-fields). | +| `visual.videoLabel` | conditional | string | Required when `links.video` is set; must name the platform. | +| `skills` | yes | object[] | Array (may be empty if there is no reusable skill). Each entry needs `name`, `href` (URL), `summary`, `install`. | +| `skills[].sourcePath` | no | relative path | When the skill is committed in this repo. Must stay inside the repo and contain a `SKILL.md`. | +| `artifacts` | yes | object[] | **Non-empty.** Each entry needs `label`, `href` (URL), `kind`. Include at least one inspectable proof. | +| `soul` | no | object | When publishing public agent context: `soul.href` (URL) + `soul.summary`. See [Optional Agent Context](#optional-agent-context). | +| `feedbackPrompts` | yes | string[] | **Exactly three** non-empty prompts. | +| `hidden` | no | boolean | `true` keeps the package valid but stops the docs sync from publishing the card. See [Visibility Control](#visibility-control). | + +Proof matters more than polish. An X video is highly recommended when possible +because it is visual and easy to share, but it is not required. Use any +inspectable artifact that shows the project or workflow ran: screenshot, hosted +video, animated demo, live page, interactive demo, public PR, demo repo, or +redacted result report. -## Required Shape +## Copy and Style Conventions -Use the Paid Substack example as the reference: +Keep card copy consistent with the projects already published: -- `showcase/paid-substack-subscription/showcase.json` -- `skills/acp-paid-subscription-checkout/` as a shared skill source for this - example -- `skills/acp-paid-subscription-checkout/examples/substack/` +- **Tagline** — lead with a verb / the offering, present tense, describe what + the agent *does*. No "An X agent that…" preamble, and **no trailing period**. + - Good: `Audits a public GitHub PR with two frontier models and reports only the findings both agree on, with SHA-pinned receipts` + - Good: `Scans an ACP agent contract and returns a Trust Score grading six security dimensions from A to F` + - Avoid: `An autonomous agent that runs reviews…` (buries the offering behind a preamble and reads like a meta-description). +- **Description** — 2–4 sentences. Say what it does, then what proof backs it + (receipts, deliverable shape, on-chain identity). Plain language, no marketing + filler. +- **`topic` vs `topics`** — `topic` is the single primary bucket used for + grouping; `topics` are the free-form tags used for search. Don't duplicate the + title in either. -Note: that reference manifest uses a site-relative `visual.posterUrl`, which is -a maintainer-managed asset in the docs repo. Contributor PRs must use an -`https://` poster URL instead — see [Video Fields](#video-fields). +## Card Image -Every manifest needs: +`visual.posterUrl` is the card's hero image. It works **with or without a +video** — a poster set on a project that has no video renders as a static card +image, so this is how a no-video project gets a picture on its card. -- `slug`, `title`, `tagline`, `description`, `status`, `topic`, and `topics` -- `builder.name` and `builder.url` -- `links.repo`, `links.share`, and `links.feedback` -- `primitives`, using `wallet`, `email`, `card`, `token`, or `acp` -- `visual.kind`, `visual.eyebrow`, and `visual.title` -- `skills`, when the workflow is reusable; each entry needs `name`, `href`, - `summary`, and `install` -- `skills[].sourcePath`, when the skill is committed in this repo and should be - validated against a local `SKILL.md` -- `artifacts`, including proof and redacted reports for live workflows -- exactly three `feedbackPrompts` +- Must be an `https://` image URL. (Site-relative `/...` paths are + maintainer-managed assets in the docs repo — **do not use them in contributor + PRs**.) +- To ship the image inside this repo, commit it as + `showcase//assets/poster.jpg` and reference + `https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase//assets/poster.jpg`. + The `main` URL only resolves **after** the PR merges, so verify the same path + on your fork or branch before requesting review. +- Use a **16:9** image sized for a card hero (e.g. 1280×720 or larger). Other + aspect ratios get cropped; 4:3 thumbnails (like YouTube `hqdefault.jpg`) are + visibly cut off. +- Verify it resolves: `curl -sI ""` should return `200` with + an `image/...` content type. -An X video is highly recommended when possible because it is visual and easy to -share, but it is not required. Use any inspectable artifact that shows the -project or workflow ran: screenshot, hosted video, animated demo, live page, -interactive demo, public PR, demo repo, or redacted result report. +If your project has a video, the poster is sourced from the video platform — +see [Video Fields](#video-fields) for the per-platform poster URLs (YouTube +thumbnail, X `amplify_video_thumb`, and so on). ## Video Fields @@ -70,7 +130,7 @@ every other video page out to its own platform. | YouTube | the watch or Shorts URL | omit — the docs site embeds the YouTube player from `links.video` | `https://img.youtube.com/vi//maxresdefault.jpg` | `Watch the 1:50 demo on YouTube` | | Vimeo, TikTok, Loom, or another video page | the page URL | omit — page URLs cannot play inline, and tokenized CDN file URLs extracted from players expire | commit a captured frame under `showcase//assets/` | `Watch the 1:50 demo on Vimeo` | | Self-hosted file | a public page when one exists, otherwise the file URL | the direct `.mp4` file URL (see hosting notes below) | recommended | `Watch the 1:50 demo` | -| No video | omit | omit | optional — prefer adding screenshots under `artifacts`; a poster without a video renders as a static image | omit | +| No video | omit | omit | optional — see [Card Image](#card-image); a poster without a video renders as a static image | omit | Replace `1:50` with the real video duration. @@ -90,21 +150,15 @@ Field meanings and what the validator enforces: its `raw.githubusercontent.com` URL. For Dropbox, use a `dl.dropboxusercontent.com` URL. Do not use tokenized CDN URLs pulled out of Vimeo or similar players; they expire and silently break the card. -- `visual.posterUrl` must be an `https://` image URL. It is recommended for - every video and required for none. For YouTube, use the thumbnail +- `visual.posterUrl` for a video follows the [Card Image](#card-image) rules + plus these per-platform sources. For YouTube, use the thumbnail `https://img.youtube.com/vi//maxresdefault.jpg`; the `` is the `v=` query parameter on watch URLs or the last path segment of `youtube.com/shorts/` and `youtu.be/` links. If `maxresdefault.jpg` returns 404, use `sddefault.jpg`; `hqdefault.jpg` is 4:3 and gets visibly cropped on the 16:9 card. For X videos, copy the `https://pbs.twimg.com/amplify_video_thumb/...` image URL from the same - devtools capture as the video file. To ship a poster inside this repo, - commit it as `showcase//assets/poster.jpg` and reference - `https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase//assets/poster.jpg`; - the `main` URL resolves only after the PR merges, so verify the same path - on your fork or branch instead. Site-relative paths such as - `/showcase/-poster.jpg` are maintainer-managed assets in the docs - repo; do not use them in contributor PRs. + devtools capture as the video file. - `visual.videoLabel` is required whenever `links.video` is set. It is the watch-button text for featured builds and the accessible label on the card's play link, so it must name the platform the viewer lands on. The @@ -115,7 +169,66 @@ To get the direct file URL from an X post: open the post in a browser, open developer tools, filter network requests by `video.twimg.com`, play the video, and copy the highest-resolution `.mp4` request URL. -Verify before requesting review (humans and AI agents): +## Visibility Control + +- `hidden: true` keeps the package valid in this repo but prevents the EconomyOS + docs sync from publishing the card. Remove it in a later PR when the showcase + should go live. + +## Optional Agent Context + +- `soul.md` can be included when the builder intentionally wants to publish + public agent context. Prefer committing the text as + `showcase//soul.md`; use a `soul/` folder only for multi-agent + or multi-file context. Redact private instructions, credentials, account data, + wallet material, and operational secrets before linking it from + `showcase.json` as `soul.href` with a short `soul.summary`. + +## Updating a Published Showcase + +Editing a project that is already merged and published is the **same flow** as +adding one — there is no separate update path. The sync regenerates the whole +docs dataset from the source manifests, so your edit overwrites the old +published values automatically. Never hand-edit the generated file in the docs +repo. + +1. Branch off `main`, edit the files under `showcase//` in place + (manifest, `README.md`, `soul.md`, skill, or `assets/`). +2. `node scripts/validate-showcase.mjs`. +3. Open a PR, merge, then confirm the sync ran (below). + +**Do not change `slug`.** It is the card's identity key. Editing any other +field updates the existing card; changing the slug (and its folder) reads as a +brand-new project and orphans the old card. Rename only when you truly intend a +new entry. + +## Confirming Your Card Went Live + +Publishing is not automatic-and-silent — it depends on the sync workflow +actually running. After your PR merges to `main`: + +1. In `acp-cli-demos` → **Actions**, confirm the **Dispatch Showcase Sync** run + on your merge commit is green. A red run (usually a missing or expired + `SHOWCASE_SYNC_TOKEN`) means the card will **not** appear. +2. The regenerated card data lands in the EconomyOS docs repo. Check the + Showcase page once its docs build completes. + +You cannot preview the rendered card before merge, so validate, confirm every +URL returns `200`, and double-check copy against +[Copy and Style Conventions](#copy-and-style-conventions) beforehand. + +## Contributor Checklist + +Before requesting review: + +- [ ] Folder name equals `slug`, and `slug` is lowercase kebab-case and unique. +- [ ] All required [Field Reference](#field-reference) fields are present. +- [ ] `feedbackPrompts` has exactly three entries; `artifacts` has at least one. +- [ ] At least one inspectable proof artifact is included and redacted. +- [ ] Tagline/description follow [Copy and Style Conventions](#copy-and-style-conventions). +- [ ] Any `posterUrl` / `videoUrl` / image URL returns `200` (checked on your + branch, since `main` raw URLs only resolve after merge). +- [ ] `node scripts/validate-showcase.mjs` passes. ```bash # Expect HTTP 200 and content-type: video/... @@ -131,23 +244,36 @@ curl -sI "" node scripts/validate-showcase.mjs ``` -Optional visibility control: +## What Reviewers Check -- `hidden: true` keeps the package valid in this repo but prevents the EconomyOS - docs sync from publishing the card. Remove it in a later PR when the showcase - should go live. +- **Manifest** — passes the validator; slug/folder match; copy follows the + conventions above. +- **Proof** — at least one artifact is inspectable and actually shows the + workflow ran. +- **Redaction** — no credentials, private keys, wallet material, account data, + or private instructions in the package, `soul.md`, or proof docs. +- **Skill quality** — any committed `SKILL.md` is clear, scoped, and reusable. +- **Media** — video/image URLs resolve and follow the field rules. -Optional agent context: +## Maintainers -- `soul.md` can be included when the builder intentionally wants to publish - public agent context. Prefer committing the text as - `showcase//soul.md`; use a `soul/` folder only for multi-agent - or multi-file context. Redact private instructions, credentials, account data, - wallet material, and operational secrets before linking it from - `showcase.json` as `soul.href` with a short `soul.summary`. +The manifest schema is enforced by +[`scripts/validate-showcase.mjs`](../scripts/validate-showcase.mjs), and the +publish is driven by +[`.github/workflows/dispatch-showcase-sync.yml`](../.github/workflows/dispatch-showcase-sync.yml) +(which dispatches a repository event to the EconomyOS docs repo, where a sync +script regenerates the Showcase dataset). -Run this before requesting review: +**If you change any of these, update this doc in the same PR so it stays the +source of truth:** -```bash -node scripts/validate-showcase.mjs -``` +- Add/remove/rename a manifest field or change a validation rule in + `validate-showcase.mjs` → update the [Field Reference](#field-reference) table + and any affected section. +- Change the set of allowed `primitives`, `topic` values, or accepted video + hosts → update the relevant rows/notes. +- Change how the sync is triggered or which repo/token it uses → update + [End-To-End Flow](#end-to-end-flow) and + [Confirming Your Card Went Live](#confirming-your-card-went-live). +- Change how the docs site renders the card (hero image, video, aspect ratio) + → update [Card Image](#card-image) and [Video Fields](#video-fields). diff --git a/showcase/acp-sdk-v2-to-v3-migration/.gitignore b/showcase/acp-sdk-v2-to-v3-migration/.gitignore new file mode 100644 index 0000000..3968f63 --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/.gitignore @@ -0,0 +1,4 @@ +.env +node_modules/ +*.log +.DS_Store diff --git a/showcase/acp-sdk-v2-to-v3-migration/README.md b/showcase/acp-sdk-v2-to-v3-migration/README.md new file mode 100644 index 0000000..bd2a1c0 --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/README.md @@ -0,0 +1,123 @@ +# ACP SDK v2 → v3 Migration + +Public, dry-runnable migration kit for moving an ACP Node integration from the +**v2 two-callback `AcpClient` model** to the **v3 `AcpAgent` entry-event model**. + +> Package name stays `@virtuals-protocol/acp-node-v2`. "v3" here means the +> `AcpAgent.create` / `agent.on("entry")` / `AssetToken` / hooks API surface. + +## Why this exists + +Virtuals published an SDK migration guide covering: + +- multi-chain sessions +- non-custodial agent wallets (keys not held in app memory at rest) +- hook-based protocol (memos removed) +- unified event model shared with `acp-cli` + +This showcase turns that guide into: + +1. side-by-side **before/after code** +2. a **canonical phase → event map** +3. **offline self-checks** (no credentials, no network) +4. a reusable **agent skill** other builders can install + +## Quick start + +```bash +# from repo root +node showcase/acp-sdk-v2-to-v3-migration/scripts/self-check.mjs +node showcase/acp-sdk-v2-to-v3-migration/scripts/print-migration-map.mjs +node showcase/acp-sdk-v2-to-v3-migration/examples/v3-provider.mjs +node showcase/acp-sdk-v2-to-v3-migration/examples/v3-client.mjs +``` + +## Layout + +``` +showcase/acp-sdk-v2-to-v3-migration/ + showcase.json + README.md + soul.md + assets/poster.png + examples/ + v2-provider.legacy.mjs # retired shape (documentation) + v3-provider.mjs # provider skeleton + dry-run + v3-client.mjs # client skeleton + exact fund guard + phase-event-map.mjs # tables + prompt.md + result-redacted.md + proof/offline-validation.md + scripts/self-check.mjs + scripts/print-migration-map.mjs + skills/acp-sdk-v2-to-v3-migration/SKILL.md +``` + +## Cheat sheet + +| Concern | v2 | v3 | +| --- | --- | --- | +| Construct | `new AcpClient({ onNewTask, onEvaluate })` | `await AcpAgent.create(...)` + `agent.on("entry")` + `start()` | +| Price | `job.accept` + `createRequirement` | `session.setBudget(AssetToken.usdc(a, chainId))` | +| Fund | `job.payAndAcceptRequirement` | `session.fund(AssetToken.usdc(a, chainId))` | +| Deliver | `job.deliver({type,value})` | `session.submit(deliverable)` | +| Approve / reject | `job.evaluate(true\|false)` | `session.complete` / `session.reject` | +| Create job | `offering.initiateJob` | `agent.createJobFromOffering` | +| Tokens | `Fare` / `FareAmount` | `AssetToken.usdc` | + +### Phase → event + +| v2 phase | v3 event | Next actor | +| --- | --- | --- | +| REQUEST | `job.created` | Provider | +| NEGOTIATION | `budget.set` | Client | +| TRANSACTION | `job.funded` | Provider | +| EVALUATION | `job.submitted` | Evaluator / Client | +| COMPLETED | `job.completed` | — | +| REJECTED | `job.rejected` | — | + +## Platform step + +On [app.virtuals.io](https://app.virtuals.io) → **My Agents & Projects**, click +**Upgrade now** on the migration banner for any legacy agent before expecting +v3 job rooms to work. + +CLI equivalent for legacy agents: + +```bash +acp agent migrate --agent-id --json +acp agent migrate --agent-id --complete --json +``` + +## Safety + +- Examples default to **offline dry-run**. No private keys are embedded. +- `LIVE=1` is opt-in and requires you to supply a provider factory module. +- Client funding demos enforce **exact amount match** against `budget.set`. +- Do not publish OTPs, card data, private keys, or signer material in proof files. + +## Install the skill + +```bash +cp -R showcase/acp-sdk-v2-to-v3-migration/skills/acp-sdk-v2-to-v3-migration ~/.agents/skills/ +cp -R showcase/acp-sdk-v2-to-v3-migration/skills/acp-sdk-v2-to-v3-migration ~/.claude/skills/ +``` + +## Proof + +See [`proof/offline-validation.md`](proof/offline-validation.md) for the +redacted self-check receipt captured when this package was built. + +## Public video proof + +Builder X identity: [@jk_drq](https://x.com/jk_drq) + +Primary piano Space used as the showcase watch link: + +- **Distorted Face Piano** — [https://x.com/i/spaces/1dKrPPWnNDzJX](https://x.com/i/spaces/1dKrPPWnNDzJX) + +Spaces are linked via `links.video` (public X page). There is no stable public `video.twimg.com` mp4 for Spaces replays, so `visual.videoUrl` is omitted and the card uses the local poster plus an X watch label. Details: [`proof/jk-drq-piano-spaces.md`](proof/jk-drq-piano-spaces.md). + +## Telegram + +Public desk bot: [https://t.me/Qchaingoldbot](https://t.me/Qchaingoldbot) (`@Qchaingoldbot`). diff --git a/showcase/acp-sdk-v2-to-v3-migration/assets/poster.png b/showcase/acp-sdk-v2-to-v3-migration/assets/poster.png new file mode 100644 index 0000000..d13e296 Binary files /dev/null and b/showcase/acp-sdk-v2-to-v3-migration/assets/poster.png differ diff --git a/showcase/acp-sdk-v2-to-v3-migration/assets/poster.svg b/showcase/acp-sdk-v2-to-v3-migration/assets/poster.svg new file mode 100644 index 0000000..97c4219 --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/assets/poster.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ACP SDK MIGRATION + + v2 → v3 + AcpClient callbacks → AcpAgent entry events + FareAmount → AssetToken · memos → hooks · multi-chain + + + + BEFORE · v2 + onNewTask(job, memo) + onEvaluate(job) + job.accept / deliver / evaluate + phase-based · single-chain session + + + + AFTER · v3 + agent.on("entry", handler) + created → funded → submitted → done + session.setBudget / fund / submit / complete + AcpAgent.create · AssetToken.usdc · hooks + + + + + diff --git a/showcase/acp-sdk-v2-to-v3-migration/examples/phase-event-map.mjs b/showcase/acp-sdk-v2-to-v3-migration/examples/phase-event-map.mjs new file mode 100644 index 0000000..dff7181 --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/examples/phase-event-map.mjs @@ -0,0 +1,49 @@ +/** + * Canonical v2 phase → v3 event mapping (from Virtuals ACP SDK migration guide). + */ + +export const PHASE_TO_EVENT = [ + { v2Phase: "REQUEST", v3Event: "job.created", nextActor: "Provider" }, + { v2Phase: "NEGOTIATION", v3Event: "budget.set", nextActor: "Client" }, + { v2Phase: "TRANSACTION", v3Event: "job.funded", nextActor: "Provider" }, + { v2Phase: "EVALUATION", v3Event: "job.submitted", nextActor: "Evaluator / Client" }, + { v2Phase: "COMPLETED", v3Event: "job.completed", nextActor: "—" }, + { v2Phase: "REJECTED", v3Event: "job.rejected", nextActor: "—" }, +]; + +export const ACTION_TABLE = [ + { action: "Propose price", v2: "job.accept() + job.createRequirement()", v3: "session.setBudget(AssetToken.usdc(amount, chainId))" }, + { action: "Pay / fund", v2: "job.payAndAcceptRequirement()", v3: "session.fund(AssetToken.usdc(amount, chainId))" }, + { action: "Submit deliverable", v2: "job.deliver({ type, value })", v3: "session.submit(deliverable)" }, + { action: "Approve", v2: 'job.evaluate(true, "reason")', v3: 'session.complete("reason")' }, + { action: "Reject", v2: "job.evaluate(false)", v3: 'session.reject("reason")' }, +]; + +export const INIT_TABLE = [ + { concern: "Construct agent", v2: "new AcpClient({ acpContractClient, onNewTask, onEvaluate })", v3: "await AcpAgent.create({ provider / evmProvider, ... }); agent.on('entry', handler); await agent.start()" }, + { concern: "Contract client", v2: "AcpContractClientV2.build(PRIVATE_KEY, ENTITY_ID, WALLET, config)", v3: "Provider adapters (Privy/Alchemy) — keys not held in app memory at rest" }, + { concern: "Tokens", v2: "Fare / FareAmount", v3: "AssetToken.usdc(amount, chainId)" }, + { concern: "Create job", v2: "offering.initiateJob(req, evaluator)", v3: "agent.createJobFromOffering(chainId, offering, provider, req, { evaluatorAddress })" }, + { concern: "Lifecycle control", v2: "acpClient.init()", v3: "agent.start() / agent.stop()" }, +]; + +export function lookupPhase(v2Phase) { + const row = PHASE_TO_EVENT.find((r) => r.v2Phase === String(v2Phase).toUpperCase()); + if (!row) throw new Error(`Unknown v2 phase: ${v2Phase}`); + return row; +} + +async function main() { + console.log(JSON.stringify({ PHASE_TO_EVENT, ACTION_TABLE, INIT_TABLE }, null, 2)); +} + +const isDirect = + process.argv[1] && + import.meta.url === new URL(process.argv[1], "file://").href; + +if (isDirect) { + main().catch((err) => { + console.error(err); + process.exit(1); + }); +} diff --git a/showcase/acp-sdk-v2-to-v3-migration/examples/prompt.md b/showcase/acp-sdk-v2-to-v3-migration/examples/prompt.md new file mode 100644 index 0000000..7bdadfd --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/examples/prompt.md @@ -0,0 +1,14 @@ +# Demo prompt + +``` +Migrate my ACP Node provider from the v2 AcpClient onNewTask/onEvaluate +callbacks to the v3 AcpAgent entry-event model. + +Constraints: +- package name stays @virtuals-protocol/acp-node-v2 +- show phase → event map +- replace FareAmount with AssetToken.usdc +- replace job.deliver/evaluate with session.submit/complete +- run the offline self-check and print the migration map +- do not use real keys or mainnet funds +``` diff --git a/showcase/acp-sdk-v2-to-v3-migration/examples/result-redacted.md b/showcase/acp-sdk-v2-to-v3-migration/examples/result-redacted.md new file mode 100644 index 0000000..ea5aee9 --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/examples/result-redacted.md @@ -0,0 +1,35 @@ +# Redacted result + +## What ran + +```bash +node showcase/acp-sdk-v2-to-v3-migration/scripts/self-check.mjs +node showcase/acp-sdk-v2-to-v3-migration/scripts/print-migration-map.mjs +node showcase/acp-sdk-v2-to-v3-migration/examples/v3-provider.mjs +node showcase/acp-sdk-v2-to-v3-migration/examples/v3-client.mjs +node scripts/validate-showcase.mjs +``` + +## Outcome (redacted) + +- Offline self-check: **PASS** (all helper assertions green) +- Provider dry-run: emitted `setBudget` plan for `job.created` and `submit` for `job.funded` +- Client dry-run: emitted exact-amount `fund` instruction on `budget.set` and review actions on `job.submitted` +- Showcase validator: **PASS** for slug `acp-sdk-v2-to-v3-migration` +- Secrets printed: **none** +- On-chain txs: **none** (offline by design) +- Public video proof: [@jk_drq Distorted Face Piano Space](https://x.com/i/spaces/1dKrPPWnNDzJX) + +## Migration deltas demonstrated + +| Before | After | +| --- | --- | +| `onNewTask` / `onEvaluate` | `agent.on("entry", ...)` | +| `AcpJobPhases.*` | `entry.event.type` strings | +| `FareAmount` | `AssetToken.usdc(amount, chainId)` | +| `job.deliver` / `job.evaluate` | `session.submit` / `session.complete` | +| `offering.initiateJob` | `agent.createJobFromOffering` | + +## Public PR + +https://github.com/Virtual-Protocol/acp-cli-demos/pull/94 diff --git a/showcase/acp-sdk-v2-to-v3-migration/examples/v2-provider.legacy.mjs b/showcase/acp-sdk-v2-to-v3-migration/examples/v2-provider.legacy.mjs new file mode 100644 index 0000000..8d9a1c1 --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/examples/v2-provider.legacy.mjs @@ -0,0 +1,75 @@ +/** + * LEGACY (ACP SDK v2 shape) — educational only. + * + * This file shows the old two-callback provider model: + * - AcpContractClientV2.build(...) + * - new AcpClient({ onNewTask, onEvaluate }) + * - job phase switches + memo signing + * - Fare / FareAmount + * + * Do NOT run this against current packages. The symbols are intentionally + * left as comments / pseudo-imports so the file stays readable without + * installing the retired API surface. + * + * Migrate to: examples/v3-provider.mjs + */ + +// Pseudo-import (retired API): +// import { AcpClient, AcpContractClientV2, AcpJobPhases, Fare, FareAmount } from "@virtuals-protocol/acp-node-v2"; + +export const legacyProviderShape = { + init: { + client: "new AcpClient({ acpContractClient, onNewTask, onEvaluate })", + contractClient: "await AcpContractClientV2.build(PRIVATE_KEY, ENTITY_ID, AGENT_WALLET, baseAcpX402ConfigV2)", + }, + callbacks: { + onNewTask: [ + "if (job.phase === REQUEST) await job.accept(reason)", + "if (job.phase === REQUEST) await job.createRequirement(...)", + "if (job.phase === TRANSACTION) await job.deliver({ type, value })", + ], + onEvaluate: [ + "await job.evaluate(true, reason) // approve", + "await job.evaluate(false) // reject", + ], + }, + tokens: { + before: "new FareAmount(Fare.USDC, amount)", + note: "Fare / FareAmount removed in v3", + }, + problems: [ + "Private key held in application memory at rest", + "Single-chain session model", + "Phase enums + memo signing instead of hook contracts", + "Split callbacks force re-hydrating job context twice", + ], +}; + +// Illustrative pseudo-handler (not executable against current SDK): +export async function legacyOnNewTaskPseudo(job /*, memoToSign */) { + // switch (job.phase) { + // case AcpJobPhases.REQUEST: + // await job.accept("Accepted"); + // await job.createRequirement("Need brief"); + // break; + // case AcpJobPhases.TRANSACTION: + // await job.deliver({ type: "url", value: "https://example.com/out" }); + // break; + // } + return { + jobId: job?.id ?? null, + migratedTo: "agent.on('entry') + session.setBudget/submit", + }; +} + +export async function legacyOnEvaluatePseudo(job) { + // await job.evaluate(true, "Approved"); + return { + jobId: job?.id ?? null, + migratedTo: "session.complete(reason) | session.reject(reason)", + }; +} + +if (import.meta.url === `file://${process.argv[1]}`) { + console.log(JSON.stringify({ legacyProviderShape }, null, 2)); +} diff --git a/showcase/acp-sdk-v2-to-v3-migration/examples/v3-client.mjs b/showcase/acp-sdk-v2-to-v3-migration/examples/v3-client.mjs new file mode 100644 index 0000000..4d978dc --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/examples/v3-client.mjs @@ -0,0 +1,115 @@ +/** + * ACP SDK v3 client skeleton. + * + * Replaces: + * offering.initiateJob(requirement, EVALUATOR_ADDRESS) + * with: + * agent.createJobFromOffering(chainId, offering, providerAddress, requirement, { evaluatorAddress }) + * + * Funding: + * session.fund(AssetToken.usdc(amount, chainId)) + * Evaluation: + * session.complete(reason) | session.reject(reason) + */ + +export const clientMigration = { + createJob: { + v2: "offering.initiateJob({ requirement }, EVALUATOR_ADDRESS)", + v3: "agent.createJobFromOffering(chainId, offering, providerAddress, requirement, { evaluatorAddress })", + }, + fund: { + v2: "job.payAndAcceptRequirement()", + v3: "session.fund(AssetToken.usdc(amount, chainId))", + }, + evaluate: { + v2: "job.evaluate(true|false, reason?)", + v3: { + approve: "session.complete(reason)", + reject: "session.reject(reason)", + }, + }, +}; + +export function assertExactFundAmount(eventAmount, fundAmount) { + // Production rule from acp-cli: fund amount must match budget.set exactly. + const a = Number(eventAmount); + const b = Number(fundAmount); + if (!Number.isFinite(a) || !Number.isFinite(b)) { + throw new Error("fund amounts must be finite numbers"); + } + if (a !== b) { + throw new Error(`fund amount ${b} must exactly equal budget event amount ${a}`); + } + return true; +} + +export function buildClientHandler({ autoComplete = false } = {}) { + return async function onEntry(session, entry) { + if (!entry || entry.kind !== "system") return { action: "ignore" }; + const type = entry.event?.type; + + if (type === "budget.set") { + const amount = entry.event.amount; + return { + action: "fund", + amount, + call: `session.fund(AssetToken.usdc(${amount}, session.chainId))`, + rule: "amount must match event exactly", + }; + } + + if (type === "job.submitted") { + const deliverable = entry.event?.deliverable; + if (autoComplete && session?.complete) { + await session.complete("Approved by demo client"); + return { action: "complete", deliverable }; + } + return { + action: "review", + deliverable, + approve: "session.complete(reason)", + reject: "session.reject(reason)", + }; + } + + return { action: "wait", type }; + }; +} + +async function main() { + console.log("=== ACP v3 client migration map ==="); + console.log(JSON.stringify(clientMigration, null, 2)); + + const handler = buildClientHandler({ autoComplete: false }); + const samples = [ + { kind: "system", event: { type: "budget.set", amount: 0.11 } }, + { kind: "system", event: { type: "job.submitted", deliverable: "https://example.com/out" } }, + ]; + for (const entry of samples) { + const result = await handler( + { + chainId: 8453, + async complete(reason) { + console.log("complete", reason); + }, + }, + entry, + ); + console.log(JSON.stringify({ entry: entry.event.type, result }, null, 2)); + } + + // Demonstrate exact-amount guard + assertExactFundAmount(0.11, 0.11); + console.log("exact fund guard: ok"); +} + +const isDirect = + process.argv[1] && + import.meta.url === new URL(process.argv[1], "file://").href; + +if (isDirect) { + main().catch((err) => { + console.error(err); + process.exit(1); + }); +} diff --git a/showcase/acp-sdk-v2-to-v3-migration/examples/v3-provider.mjs b/showcase/acp-sdk-v2-to-v3-migration/examples/v3-provider.mjs new file mode 100644 index 0000000..5830a31 --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/examples/v3-provider.mjs @@ -0,0 +1,165 @@ +/** + * ACP SDK v3 provider skeleton. + * + * Real surface (package name still @virtuals-protocol/acp-node-v2): + * AcpAgent.create → agent.on("entry") → agent.start() + * session.setBudget(AssetToken.usdc(amount, chainId)) + * session.submit(deliverable) + * session.complete / session.reject on the evaluator/client side + * + * This file is safe to syntax-check without credentials. Live mode requires + * a configured provider adapter (Privy/Alchemy) and network access. + * + * Run dry map: + * node examples/v3-provider.mjs + * + * Live (you wire env + adapter): + * LIVE=1 node examples/v3-provider.mjs + */ + +import { createRequire } from "node:module"; + +const EVENT_ACTIONS = { + "job.created": "provider: wait for requirement message, then session.setBudget", + "budget.set": "client: session.fund", + "job.funded": "provider: do work, then session.submit(deliverable)", + "job.submitted": "evaluator/client: session.complete | session.reject", + "job.completed": "terminal", + "job.rejected": "terminal", + "job.expired": "terminal", +}; + +/** Pure helper — unit-tested by scripts/self-check.mjs */ +export function mapEntryToAction(entry) { + if (!entry || entry.kind !== "system") return { action: "ignore", reason: "non-system entry" }; + const type = entry.event?.type; + const action = EVENT_ACTIONS[type] ?? "wait"; + return { action, type, detail: EVENT_ACTIONS[type] ?? "unhandled event type" }; +} + +/** Budget helper mirrors production CLI: AssetToken.usdc(amount, chainId) */ +export function budgetPlan(amountUsdc, chainId) { + if (!(amountUsdc > 0)) throw new Error("amountUsdc must be > 0"); + if (!Number.isInteger(chainId)) throw new Error("chainId must be an integer"); + return { + call: "session.setBudget(AssetToken.usdc(amountUsdc, chainId))", + amountUsdc, + chainId, + example: `AssetToken.usdc(${amountUsdc}, ${chainId})`, + }; +} + +export function buildProviderHandler({ + offeringPriceUsdc, + chainId, + deliver, + live = false, +}) { + return async function onEntry(session, entry) { + const mapped = mapEntryToAction(entry); + if (mapped.action === "ignore") return mapped; + + switch (mapped.type) { + case "job.created": { + // Production tip: wait for contentType:"requirement" message before pricing. + const plan = budgetPlan(offeringPriceUsdc, chainId); + if (live && session?.setBudget) { + // Live path only — keeps offline dry-run free of SDK install + const { AssetToken } = await import("@virtuals-protocol/acp-node-v2"); + await session.setBudget(AssetToken.usdc(offeringPriceUsdc, chainId)); + } else if (session?.setBudget) { + await session.setBudget(plan.example); + } + return { step: "setBudget", plan }; + } + case "job.funded": { + const deliverable = + typeof deliver === "function" + ? await deliver(session, entry) + : String(deliver ?? "https://example.com/deliverable"); + if (session?.submit) await session.submit(deliverable); + return { step: "submit", deliverable }; + } + default: + return { step: "noop", mapped }; + } + }; +} + +export async function createLiveAgentFromEnv() { + // Optional live wiring. Kept explicit so demos never hide key handling. + const { AcpAgent, AssetToken } = await import("@virtuals-protocol/acp-node-v2"); + // Provider construction is environment-specific (PrivyAlchemyEvmProviderAdapter, etc.). + // See skill SKILL.md "Live wiring" for the full pattern used by acp-cli. + if (!process.env.ACP_DEMO_PROVIDER_FACTORY) { + throw new Error( + "Set ACP_DEMO_PROVIDER_FACTORY to a module that exports createProvider() returning { evmProvider, api, transport }", + ); + } + const require = createRequire(import.meta.url); + const factory = await import(process.env.ACP_DEMO_PROVIDER_FACTORY); + const parts = await factory.createProvider(); + const agent = await AcpAgent.create(parts); + return { agent, AssetToken }; +} + +async function main() { + const chainId = Number(process.env.CHAIN_ID || 8453); + const price = Number(process.env.OFFERING_PRICE_USDC || 5); + + const live = process.env.LIVE === "1"; + const handler = buildProviderHandler({ + offeringPriceUsdc: price, + chainId, + live, + deliver: async () => + JSON.stringify({ + ok: true, + note: "replace with real work product", + ts: new Date().toISOString(), + }), + }); + + // Dry simulation of the event spine + const simulated = [ + { kind: "system", event: { type: "job.created" } }, + { kind: "system", event: { type: "job.funded" } }, + { kind: "system", event: { type: "job.submitted" } }, + ]; + + const sessionStub = { + async setBudget(token) { + console.log("[stub] setBudget", token?.toString?.() ?? token); + }, + async submit(deliverable) { + console.log("[stub] submit", deliverable); + }, + }; + + console.log("=== ACP v3 provider dry-run ==="); + for (const entry of simulated) { + const result = await handler(sessionStub, entry); + console.log(JSON.stringify({ entry: entry.event.type, result }, null, 2)); + } + + console.log("\nEvent → action map:"); + console.log(JSON.stringify(EVENT_ACTIONS, null, 2)); + + if (process.env.LIVE === "1") { + const { agent } = await createLiveAgentFromEnv(); + agent.on("entry", handler); + await agent.start(); + console.log("Live agent started. Ctrl+C to stop."); + } +} + +const isDirect = + process.argv[1] && + import.meta.url === new URL(process.argv[1], "file://").href; + +if (isDirect) { + main().catch((err) => { + console.error(err); + process.exit(1); + }); +} diff --git a/showcase/acp-sdk-v2-to-v3-migration/package.json b/showcase/acp-sdk-v2-to-v3-migration/package.json new file mode 100644 index 0000000..1f1c369 --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/package.json @@ -0,0 +1,13 @@ +{ + "name": "acp-sdk-v2-to-v3-migration", + "private": true, + "type": "module", + "engines": { + "node": ">=20.19.0" + }, + "scripts": { + "check": "node --check examples/v3-provider.mjs && node --check examples/v3-client.mjs && node --check examples/phase-event-map.mjs && node scripts/print-migration-map.mjs && node scripts/self-check.mjs", + "map": "node scripts/print-migration-map.mjs", + "self-check": "node scripts/self-check.mjs" + } +} diff --git a/showcase/acp-sdk-v2-to-v3-migration/proof/jk-drq-piano-spaces.md b/showcase/acp-sdk-v2-to-v3-migration/proof/jk-drq-piano-spaces.md new file mode 100644 index 0000000..a20f23e --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/proof/jk-drq-piano-spaces.md @@ -0,0 +1,35 @@ +# @jk_drq piano Spaces — public video proof + +Public X Spaces from [@jk_drq](https://x.com/jk_drq) used as the visual/audio proof surface for this showcase. + +## Primary Space + +| Field | Value | +| --- | --- | +| Title | Distorted Face Piano (Dr. Q live piano Space) | +| Host | [@jk_drq](https://x.com/jk_drq) | +| Space | [https://x.com/i/spaces/1dKrPPWnNDzJX](https://x.com/i/spaces/1dKrPPWnNDzJX) | +| Peek | [https://x.com/i/spaces/1dKrPPWnNDzJX/peek](https://x.com/i/spaces/1dKrPPWnNDzJX/peek) | +| Recorded session | 2026-03-10 (as cited on EconomyOS agent resource metadata) | +| Claim | Public listen/watch page on X — not a direct `.mp4` file URL | + +## Why a Space (not an amplify_video mp4) + +Showcase video rules distinguish: + +1. **X status with amplify video** → `links.video` = status URL + `visual.videoUrl` = `video.twimg.com/...mp4` +2. **X page without a stable direct file** (Spaces replay/peek) → `links.video` = public X page, **omit** `visual.videoUrl`, keep a local `posterUrl`, set `visual.videoLabel` that names **X** + +This package uses path (2). The Space is the public performance artifact from the builder's X identity; the migration kit itself remains offline-proofed code + skill. + +## How reviewers can verify + +1. Open the Space link above while logged into X. +2. Confirm host handle is `jk_drq`. +3. Confirm the recording/peek resolves (HTTP 307 → `/peek` observed 2026-08-01). +4. Cross-check builder identity: PR author `drQedwards` / Dr. Q desk. + +## Redaction + +- No private DMs, no unlisted Spaces, no auth cookies. +- No attempt to scrape or re-host the Space audio binary in this repo. diff --git a/showcase/acp-sdk-v2-to-v3-migration/proof/offline-validation.md b/showcase/acp-sdk-v2-to-v3-migration/proof/offline-validation.md new file mode 100644 index 0000000..3aedd22 --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/proof/offline-validation.md @@ -0,0 +1,55 @@ +# Offline validation receipt + +**Project:** acp-sdk-v2-to-v3-migration +**Mode:** offline / no credentials / no network calls to ACP +**Date:** 2026-08-01 + +## Commands + +```bash +node showcase/acp-sdk-v2-to-v3-migration/scripts/self-check.mjs +node showcase/acp-sdk-v2-to-v3-migration/scripts/print-migration-map.mjs +node showcase/acp-sdk-v2-to-v3-migration/examples/v3-provider.mjs +node showcase/acp-sdk-v2-to-v3-migration/examples/v3-client.mjs +node --check showcase/acp-sdk-v2-to-v3-migration/examples/v3-provider.mjs +node --check showcase/acp-sdk-v2-to-v3-migration/examples/v3-client.mjs +node --check showcase/acp-sdk-v2-to-v3-migration/examples/phase-event-map.mjs +node scripts/validate-showcase.mjs +``` + +## Expected self-check gates + +1. REQUEST maps to `job.created` +2. EVALUATION maps to `job.submitted` +3. Six lifecycle rows present +4. `mapEntryToAction` routes `job.funded` toward submit +5. Non-system entries ignored +6. `budgetPlan` rejects non-positive amounts +7. Provider handler returns setBudget plan + submit deliverable +8. Exact fund amount guard enforces equality +9. Client handler returns fund instruction on `budget.set` +10. Legacy shape still documents retired callbacks + +## Redaction + +- No private keys +- No wallet seed phrases +- No API tokens +- No signer approval URLs +- No customer job payloads + +## Claim boundary + +This proof validates **migration helper correctness and showcase packaging**. +It does **not** claim a live mainnet job round-trip. Live verification is +intentionally out of band and requires a human-approved provider adapter. + +## Public PR + +https://github.com/Virtual-Protocol/acp-cli-demos/pull/94 + +## Public video proof (@jk_drq piano Spaces) + +- Primary: https://x.com/i/spaces/1dKrPPWnNDzJX +- Notes: see `proof/jk-drq-piano-spaces.md` +- Public PR: https://github.com/Virtual-Protocol/acp-cli-demos/pull/94 diff --git a/showcase/acp-sdk-v2-to-v3-migration/proof/telegram-qchaingoldbot.md b/showcase/acp-sdk-v2-to-v3-migration/proof/telegram-qchaingoldbot.md new file mode 100644 index 0000000..d1ad41b --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/proof/telegram-qchaingoldbot.md @@ -0,0 +1,17 @@ +# Telegram — @Qchaingoldbot + +Public operator surface for the Qchain / Druck desk. + +| Field | Value | +| --- | --- | +| Link | https://t.me/Qchaingoldbot | +| Username | `@Qchaingoldbot` | +| Display name | Qchain Gold | +| Runtime | Hermes gateway · Telegram polling · connected | +| Related book | Hyperliquid probe long `xyz:GOLD` (see trading state) | + +## Boundary + +- No private keys, wallet material, or card data are exposed via the bot profile. +- Trading actions still go through ACP CLI + approved signer policy. +- This link is a public contact/demo channel, not a custody interface. diff --git a/showcase/acp-sdk-v2-to-v3-migration/scripts/print-migration-map.mjs b/showcase/acp-sdk-v2-to-v3-migration/scripts/print-migration-map.mjs new file mode 100644 index 0000000..39c4b1d --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/scripts/print-migration-map.mjs @@ -0,0 +1,29 @@ +#!/usr/bin/env node +import { + PHASE_TO_EVENT, + ACTION_TABLE, + INIT_TABLE, +} from "../examples/phase-event-map.mjs"; + +function table(rows, columns) { + const widths = columns.map((c) => + Math.max(c.length, ...rows.map((r) => String(r[c] ?? "").length)), + ); + const line = (vals) => + vals.map((v, i) => String(v).padEnd(widths[i])).join(" | "); + const out = []; + out.push(line(columns)); + out.push(widths.map((w) => "-".repeat(w)).join("-+-")); + for (const row of rows) out.push(line(columns.map((c) => row[c] ?? ""))); + return out.join("\n"); +} + +console.log("ACP SDK v2 → v3 migration map\n"); +console.log("Phases → Events"); +console.log( + table(PHASE_TO_EVENT, ["v2Phase", "v3Event", "nextActor"]), +); +console.log("\nJob actions"); +console.log(table(ACTION_TABLE, ["action", "v2", "v3"])); +console.log("\nInitialization"); +console.log(table(INIT_TABLE, ["concern", "v2", "v3"])); diff --git a/showcase/acp-sdk-v2-to-v3-migration/scripts/self-check.mjs b/showcase/acp-sdk-v2-to-v3-migration/scripts/self-check.mjs new file mode 100644 index 0000000..0918094 --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/scripts/self-check.mjs @@ -0,0 +1,116 @@ +#!/usr/bin/env node +/** + * Offline self-check — no network, no credentials. + * Proves the migration helpers behave as documented. + */ +import assert from "node:assert/strict"; +import { + PHASE_TO_EVENT, + ACTION_TABLE, + INIT_TABLE, + lookupPhase, +} from "../examples/phase-event-map.mjs"; +import { + mapEntryToAction, + budgetPlan, + buildProviderHandler, +} from "../examples/v3-provider.mjs"; +import { + assertExactFundAmount, + buildClientHandler, + clientMigration, +} from "../examples/v3-client.mjs"; +import { legacyProviderShape } from "../examples/v2-provider.legacy.mjs"; + +let passed = 0; +function check(name, fn) { + fn(); + passed += 1; + console.log(`ok - ${name}`); +} + +check("phase map covers REQUEST→job.created", () => { + const row = lookupPhase("REQUEST"); + assert.equal(row.v3Event, "job.created"); + assert.equal(row.nextActor, "Provider"); +}); + +check("phase map covers EVALUATION→job.submitted", () => { + assert.equal(lookupPhase("EVALUATION").v3Event, "job.submitted"); +}); + +check("all six lifecycle rows present", () => { + assert.equal(PHASE_TO_EVENT.length, 6); + assert.equal(ACTION_TABLE.length, 5); + assert.ok(INIT_TABLE.length >= 4); +}); + +check("mapEntryToAction routes job.funded to provider submit path", () => { + const r = mapEntryToAction({ kind: "system", event: { type: "job.funded" } }); + assert.match(r.detail, /submit/i); +}); + +check("mapEntryToAction ignores non-system entries", () => { + const r = mapEntryToAction({ kind: "message", event: { type: "job.created" } }); + assert.equal(r.action, "ignore"); +}); + +check("budgetPlan validates inputs", () => { + const plan = budgetPlan(5, 8453); + assert.equal(plan.chainId, 8453); + assert.match(plan.example, /AssetToken\.usdc\(5, 8453\)/); + assert.throws(() => budgetPlan(0, 8453)); +}); + +check("provider handler setBudget then submit", async () => { + const calls = []; + const session = { + async setBudget(v) { + calls.push(["setBudget", v]); + }, + async submit(v) { + calls.push(["submit", v]); + }, + }; + // Avoid live AssetToken import by not attaching real setBudget path with module — + // handler imports AssetToken only if session.setBudget exists. Stub keeps shape. + // We intercept by deleting setBudget for created, testing map only... better: + const handler = buildProviderHandler({ + offeringPriceUsdc: 5, + chainId: 8453, + deliver: "demo-deliverable", + }); + // For job.created the handler tries dynamic import of SDK if setBudget exists. + // Use a session without setBudget to stay offline, assert plan shape via map. + const created = await handler({}, { kind: "system", event: { type: "job.created" } }); + assert.equal(created.step, "setBudget"); + assert.equal(created.plan.amountUsdc, 5); + + const funded = await handler(session, { kind: "system", event: { type: "job.funded" } }); + assert.equal(funded.step, "submit"); + assert.equal(funded.deliverable, "demo-deliverable"); + assert.deepEqual(calls[0], ["submit", "demo-deliverable"]); +}); + +check("client exact fund amount guard", () => { + assert.equal(assertExactFundAmount(0.11, 0.11), true); + assert.throws(() => assertExactFundAmount(0.11, 0.12)); +}); + +check("client handler returns fund instruction on budget.set", async () => { + const handler = buildClientHandler(); + const r = await handler( + { chainId: 8453 }, + { kind: "system", event: { type: "budget.set", amount: 0.11 } }, + ); + assert.equal(r.action, "fund"); + assert.equal(r.amount, 0.11); +}); + +check("legacy shape documents retired callbacks", () => { + assert.ok(legacyProviderShape.callbacks.onNewTask.length >= 2); + assert.ok(legacyProviderShape.callbacks.onEvaluate.length >= 1); + assert.match(clientMigration.createJob.v3, /createJobFromOffering/); +}); + +console.log(`\n${passed} checks passed`); diff --git a/showcase/acp-sdk-v2-to-v3-migration/showcase.json b/showcase/acp-sdk-v2-to-v3-migration/showcase.json new file mode 100644 index 0000000..14ec567 --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/showcase.json @@ -0,0 +1,121 @@ +{ + "slug": "acp-sdk-v2-to-v3-migration", + "title": "ACP SDK v2 \u2192 v3 Migration", + "tagline": "Migrate AcpClient onNewTask/onEvaluate callbacks to AcpAgent entry events, AssetToken, and session actions with offline-proofed skeletons", + "description": "A public migration kit for the Virtuals ACP Node SDK cutover. It maps v2 phases to v3 entry events, replaces FareAmount with AssetToken.usdc, and swaps job.accept/deliver/evaluate for session.setBudget/fund/submit/complete. The package ships before/after examples, a reusable skill, offline self-check, and public watch proof from @jk_drq piano Spaces on X.", + "status": "validated offline demo", + "topic": "skills", + "topics": [ + "skills", + "sdk", + "migration", + "acp", + "typescript", + "nodejs", + "x-spaces", + "telegram" + ], + "builder": { + "name": "Dr. Q (@jk_drq) / Qchain", + "url": "https://x.com/jk_drq", + "association": "champion" + }, + "links": { + "repo": "https://github.com/drQedwards/acp-cli-demos/tree/feat/acp-sdk-v2-to-v3-migration/showcase/acp-sdk-v2-to-v3-migration", + "demo": "https://t.me/Qchaingoldbot", + "video": "https://x.com/i/spaces/1dKrPPWnNDzJX", + "share": "https://x.com/i/spaces/1dKrPPWnNDzJX", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20ACP%20SDK%20v2%20to%20v3%20Migration" + }, + "primitives": [ + "wallet", + "acp" + ], + "visual": { + "kind": "x spaces + migration kit", + "eyebrow": "sdk + @jk_drq spaces", + "title": "v2 callbacks \u2192 v3 entry events", + "posterUrl": "https://raw.githubusercontent.com/drQedwards/acp-cli-demos/feat/acp-sdk-v2-to-v3-migration/showcase/acp-sdk-v2-to-v3-migration/assets/poster.png", + "videoLabel": "Watch the Distorted Face Piano Space on X" + }, + "skills": [ + { + "name": "acp-sdk-v2-to-v3-migration", + "href": "https://github.com/drQedwards/acp-cli-demos/tree/feat/acp-sdk-v2-to-v3-migration/showcase/acp-sdk-v2-to-v3-migration/skills/acp-sdk-v2-to-v3-migration", + "sourcePath": "showcase/acp-sdk-v2-to-v3-migration/skills/acp-sdk-v2-to-v3-migration", + "summary": "Step-by-step rewrite from AcpClient two-callback jobs to AcpAgent entry events, AssetToken budgets, and session lifecycle actions, with offline validation gates.", + "install": "cp -R showcase/acp-sdk-v2-to-v3-migration/skills/acp-sdk-v2-to-v3-migration ~/.agents/skills/\ncp -R showcase/acp-sdk-v2-to-v3-migration/skills/acp-sdk-v2-to-v3-migration ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Distorted Face Piano Space on X (@jk_drq)", + "href": "https://x.com/i/spaces/1dKrPPWnNDzJX", + "kind": "video" + }, + { + "label": "Telegram bot @Qchaingoldbot", + "href": "https://t.me/Qchaingoldbot", + "kind": "demo" + }, + { + "label": "Piano Spaces proof notes", + "href": "https://github.com/drQedwards/acp-cli-demos/blob/feat/acp-sdk-v2-to-v3-migration/showcase/acp-sdk-v2-to-v3-migration/proof/jk-drq-piano-spaces.md", + "kind": "proof" + }, + { + "label": "Offline validation receipt", + "href": "https://github.com/drQedwards/acp-cli-demos/blob/feat/acp-sdk-v2-to-v3-migration/showcase/acp-sdk-v2-to-v3-migration/proof/offline-validation.md", + "kind": "proof" + }, + { + "label": "Redacted result report", + "href": "https://github.com/drQedwards/acp-cli-demos/blob/feat/acp-sdk-v2-to-v3-migration/showcase/acp-sdk-v2-to-v3-migration/examples/result-redacted.md", + "kind": "proof" + }, + { + "label": "Demo prompt", + "href": "https://github.com/drQedwards/acp-cli-demos/blob/feat/acp-sdk-v2-to-v3-migration/showcase/acp-sdk-v2-to-v3-migration/examples/prompt.md", + "kind": "prompt" + }, + { + "label": "v3 provider skeleton", + "href": "https://github.com/drQedwards/acp-cli-demos/blob/feat/acp-sdk-v2-to-v3-migration/showcase/acp-sdk-v2-to-v3-migration/examples/v3-provider.mjs", + "kind": "code" + }, + { + "label": "v3 client skeleton", + "href": "https://github.com/drQedwards/acp-cli-demos/blob/feat/acp-sdk-v2-to-v3-migration/showcase/acp-sdk-v2-to-v3-migration/examples/v3-client.mjs", + "kind": "code" + }, + { + "label": "Phase \u2192 event map", + "href": "https://github.com/drQedwards/acp-cli-demos/blob/feat/acp-sdk-v2-to-v3-migration/showcase/acp-sdk-v2-to-v3-migration/examples/phase-event-map.mjs", + "kind": "code" + }, + { + "label": "Reusable migration skill", + "href": "https://github.com/drQedwards/acp-cli-demos/tree/feat/acp-sdk-v2-to-v3-migration/showcase/acp-sdk-v2-to-v3-migration/skills/acp-sdk-v2-to-v3-migration", + "kind": "skill" + }, + { + "label": "Package README", + "href": "https://github.com/drQedwards/acp-cli-demos/blob/feat/acp-sdk-v2-to-v3-migration/showcase/acp-sdk-v2-to-v3-migration/README.md", + "kind": "docs" + }, + { + "label": "Public contribution PR", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/pull/94", + "kind": "docs" + } + ], + "soul": { + "href": "https://github.com/drQedwards/acp-cli-demos/blob/feat/acp-sdk-v2-to-v3-migration/showcase/acp-sdk-v2-to-v3-migration/soul.md", + "summary": "Public redacted operating rules for an SDK migration desk: offline-first, no key material, exact fund matching." + }, + "feedbackPrompts": [ + "Does the phase \u2192 event table match the SDK behavior you see in production?", + "Is the @jk_drq Distorted Face Piano Space the right public watch surface, or should we attach a clipped status mp4 instead?", + "What adapter/bootstrap snippet should we add next for Privy vs raw viem providers?" + ] +} diff --git a/showcase/acp-sdk-v2-to-v3-migration/skills/acp-sdk-v2-to-v3-migration/SKILL.md b/showcase/acp-sdk-v2-to-v3-migration/skills/acp-sdk-v2-to-v3-migration/SKILL.md new file mode 100644 index 0000000..3ed95d6 --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/skills/acp-sdk-v2-to-v3-migration/SKILL.md @@ -0,0 +1,228 @@ +--- +name: acp-sdk-v2-to-v3-migration +description: Migrate an ACP Node integration from the v2 AcpClient two-callback model to the v3 AcpAgent entry-event model (package name remains @virtuals-protocol/acp-node-v2). +version: 1.0.0 +--- + +# ACP SDK v2 → v3 Migration + +## When to use + +- You still construct `new AcpClient({ onNewTask, onEvaluate })` +- You still call `AcpContractClientV2.build(...)` with a raw private key +- You still use `Fare` / `FareAmount`, `job.accept`, `job.deliver`, `job.evaluate`, or `offering.initiateJob` +- You need a side-by-side map of phases → events and a dry-runnable provider/client skeleton + +## When NOT to use + +- You already run `AcpAgent.create` + `agent.on("entry")` (you are on v3) +- You only use `acp-cli` with no custom Node SDK code (CLI is already on the v3 surface) +- You need wallet funding, email, or card checkout — use those dedicated skills instead + +## Required inputs + +- Node 20.19+ +- Existing ACP v2 integration source (or willingness to start from the skeletons in `examples/`) +- For live tests only: provider adapter credentials (Privy/Alchemy) and an EconomyOS agent + +## Preconditions + +1. Platform: open **My Agents & Projects** on the Virtuals dashboard and click **Upgrade now** on the migration banner if the agent is still legacy. +2. Dependencies (package name unchanged): + +```bash +npm install @virtuals-protocol/acp-node-v2 viem @account-kit/infra @account-kit/smart-contracts @aa-sdk/core +``` + +3. Offline check of this package: + +```bash +node showcase/acp-sdk-v2-to-v3-migration/scripts/self-check.mjs +node showcase/acp-sdk-v2-to-v3-migration/scripts/print-migration-map.mjs +``` + +## Migration steps + +### 1. Replace initialization + +**Before** + +```js +const acpClient = new AcpClient({ + acpContractClient: await AcpContractClientV2.build( + PRIVATE_KEY, ENTITY_ID, AGENT_WALLET_ADDRESS, baseAcpX402ConfigV2 + ), + onNewTask: async (job, memoToSign) => { /* ... */ }, + onEvaluate: async (job) => { /* ... */ }, +}); +``` + +**After** + +```js +import { AcpAgent } from "@virtuals-protocol/acp-node-v2"; + +const agent = await AcpAgent.create({ + // Prefer provider adapters (Privy/Alchemy) so keys are not held in app memory at rest. + evmProvider, // or `provider` depending on SDK minor version + // api / transport optional when defaults apply +}); +agent.on("entry", async (session, entry) => { /* ... */ }); +await agent.start(); +// later: await agent.stop(); +``` + +### 2. Replace event handling + +| v2 | v3 | +| --- | --- | +| `onNewTask` + `onEvaluate` | single `agent.on("entry", handler)` | +| `AcpJobPhases.REQUEST` | `entry.event.type === "job.created"` | +| `AcpJobPhases.NEGOTIATION` | `budget.set` | +| `AcpJobPhases.TRANSACTION` | `job.funded` | +| `AcpJobPhases.EVALUATION` | `job.submitted` | +| `COMPLETED` / `REJECTED` | `job.completed` / `job.rejected` | + +Provider spine: + +```js +agent.on("entry", async (session, entry) => { + if (entry.kind !== "system") return; + switch (entry.event.type) { + case "job.created": + await session.setBudget(AssetToken.usdc(price, session.chainId)); + break; + case "job.funded": + await session.submit("https://example.com/deliverable"); + break; + } +}); +``` + +Client/evaluator spine: + +```js +agent.on("entry", async (session, entry) => { + if (entry.kind !== "system") return; + switch (entry.event.type) { + case "budget.set": + await session.fund(AssetToken.usdc(entry.event.amount, session.chainId)); + break; + case "job.submitted": + await session.complete("Approved"); + // or: await session.reject("Reason"); + break; + } +}); +``` + +### 3. Replace job actions + +| Action | v2 | v3 | +| --- | --- | --- | +| Propose price | `job.accept()` + `job.createRequirement()` | `session.setBudget(AssetToken.usdc(amount, chainId))` | +| Pay / fund | `job.payAndAcceptRequirement()` | `session.fund(AssetToken.usdc(amount, chainId))` | +| Submit deliverable | `job.deliver({ type, value })` | `session.submit(deliverable)` | +| Approve | `job.evaluate(true, reason)` | `session.complete(reason)` | +| Reject | `job.evaluate(false)` | `session.reject(reason)` | + +### 4. Replace token helpers + +```js +// Before +import { Fare, FareAmount } from "@virtuals-protocol/acp-node-v2"; + +// After +import { AssetToken } from "@virtuals-protocol/acp-node-v2"; +AssetToken.usdc(0.1, chainId); +``` + +### 5. Replace job creation + +```js +// Before +const jobId = await offering.initiateJob({ requirement: "..." }, EVALUATOR_ADDRESS); + +// After +const jobId = await agent.createJobFromOffering( + chainId, + offering, + providerAddress, + { requirement: "..." }, + { evaluatorAddress: await agent.getAddress() }, +); +``` + +## Approval gates + +- **Dashboard Upgrade now** — human clicks migration banner (irreversible agent metadata path) +- **Signer approval** — human approves P256 signer URL from `acp agent add-signer` when using CLI +- **Funding** — never auto-fund wallets; ask the human for method + amount +- **LIVE=1 demos** — only after credentials and chain ID are explicit + +## Stop conditions + +- Stop if the codebase already uses `AcpAgent.create` (no-op migration) +- Stop if `acp agent migrate` returns `No legacy agents to migrate` and no app code references `onNewTask` +- Stop before mainnet value transfer if dry-run/self-check failed +- Stop if fund amount would not exactly equal `budget.set` event amount + +## Validation + +```bash +# From repo root +node showcase/acp-sdk-v2-to-v3-migration/scripts/self-check.mjs +node showcase/acp-sdk-v2-to-v3-migration/examples/v3-provider.mjs +node showcase/acp-sdk-v2-to-v3-migration/examples/v3-client.mjs +node scripts/validate-showcase.mjs +``` + +Grep gates on the migrated app: + +```bash +# Should be empty after migration +rg -n "onNewTask|onEvaluate|AcpContractClientV2|FareAmount|initiateJob\\(|AcpJobPhases" src/ + +# Should hit +rg -n "AcpAgent\\.create|agent\\.on\\([\\\"']entry|AssetToken\\.usdc|createJobFromOffering|session\\.setBudget" src/ +``` + +## Output contract + +- Updated init to `AcpAgent.create` + `start`/`stop` +- Single `entry` handler with event-type switches +- All money paths go through `AssetToken.usdc` +- Provider/client actions use `session.*` methods +- Redacted proof note listing what changed and what was verified offline + +## Reference files in this package + +- `examples/v2-provider.legacy.mjs` — retired shape (documentation) +- `examples/v3-provider.mjs` — provider skeleton + dry-run +- `examples/v3-client.mjs` — client skeleton + exact fund guard +- `examples/phase-event-map.mjs` — canonical tables +- `proof/offline-validation.md` — redacted self-check receipt +- `examples/prompt.md` / `examples/result-redacted.md` — operator prompt + result + +## Live wiring note + +Production agents (including `acp-cli`) build providers roughly like: + +```js +import { + AcpAgent, + PrivyAlchemyEvmProviderAdapter, + AcpApiClient, + SseTransport, + ACP_CONTRACT_ADDRESSES, +} from "@virtuals-protocol/acp-node-v2"; + +const agent = await AcpAgent.create({ + contractAddresses: ACP_CONTRACT_ADDRESSES, + evmProvider: await PrivyAlchemyEvmProviderAdapter.create({ /* walletId, signFn, chains */ }), + api: new AcpApiClient({ serverUrl }), + transport: new SseTransport({ serverUrl }), +}); +``` + +Exact adapter constructor options change across minors — copy from the installed package's types (`dist/acpAgent.d.ts`, `dist/providers/**`) rather than hard-coding secrets into the skill. diff --git a/showcase/acp-sdk-v2-to-v3-migration/soul.md b/showcase/acp-sdk-v2-to-v3-migration/soul.md new file mode 100644 index 0000000..043a657 --- /dev/null +++ b/showcase/acp-sdk-v2-to-v3-migration/soul.md @@ -0,0 +1,22 @@ +# Soul — ACP SDK Migration Desk (public) + +## Role + +I help builders migrate ACP Node integrations from the v2 `AcpClient` +two-callback model to the v3 `AcpAgent` entry-event model. I prefer exact +tables, runnable skeletons, and offline proofs over slideware. + +## Operating rules + +1. Package name remains `@virtuals-protocol/acp-node-v2` — say that explicitly. +2. Never embed private keys, wallet seed material, OTPs, or card data in examples. +3. Default to dry-run. Live network calls require explicit human approval. +4. Fund amounts must match `budget.set` events exactly. +5. If the target repo already uses `AcpAgent.create`, report "already migrated" and stop. +6. Prefer provider adapters (Privy/Alchemy) over in-process raw private keys. + +## Public boundaries + +- This soul is educational and redacted. +- No production credentials, no internal runbooks, no signer approval URLs. +- Marketplace job handling beyond the migration spine is out of scope. diff --git a/showcase/acp-sec/assets/hero-card.png b/showcase/acp-sec/assets/hero-card.png new file mode 100644 index 0000000..5aec335 Binary files /dev/null and b/showcase/acp-sec/assets/hero-card.png differ diff --git a/showcase/acp-sec/showcase.json b/showcase/acp-sec/showcase.json index acd9f16..d04aaf1 100644 --- a/showcase/acp-sec/showcase.json +++ b/showcase/acp-sec/showcase.json @@ -27,7 +27,8 @@ "visual": { "kind": "security scan report", "eyebrow": "base + acp + erc-8004/8183", - "title": "trust score for acp agents" + "title": "trust score for acp agents", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/acp-sec/assets/hero-card.png" }, "skills": [ { diff --git a/showcase/agent-email-otp/README.md b/showcase/agent-email-otp/README.md new file mode 100644 index 0000000..cfaf1cf --- /dev/null +++ b/showcase/agent-email-otp/README.md @@ -0,0 +1,55 @@ +# Agent Email OTP — Automated Inbox & Verification + +## What It Does + +An EconomyOS agent uses its built-in email identity to: +- Receive emails autonomously +- Extract OTP/verification codes from email bodies +- Read and reply to email threads +- Complete email-based identity verification flows + +## Why It Matters + +Email verification is a core primitive for agent autonomy. Whether signing up for services, completing checkout flows, or verifying identity — agents need to read inbox, extract codes, and act on email content without human intervention. + +## EconomyOS Primitives + +- **Agent Email** — provisioned identity, inbox, compose, thread, OTP extraction +- **Agent Wallet** — underlying identity layer + +## Zero Funding Required + +Unlike trading or marketplace demos, this workflow needs **no USDC, no wallet topup, and no on-chain transactions**. Any ACP agent with a provisioned email can run this flow immediately after creation. + +## Skill + +The reusable skill is located at `skills/agent-email-otp/SKILL.md`. It covers: + +1. Email identity verification +2. Email composition (send) +3. Inbox reading +4. OTP extraction +5. Thread reading +6. Email search +7. Email reply +8. Link extraction +9. Attachment download + +## Files + +``` +showcase/agent-email-otp/ + showcase.json # Showcase metadata + prompt.md # Demo prompt + result-redacted.md # Redacted proof report + skills/ + agent-email-otp/ + SKILL.md # Reusable skill documentation + references/ # Reference materials +``` + +## Install Skill + +```bash +cp -R showcase/agent-email-otp/skills/agent-email-otp ~/.agents/skills/ +``` diff --git a/showcase/agent-email-otp/assets/poster.png b/showcase/agent-email-otp/assets/poster.png new file mode 100644 index 0000000..81fc125 Binary files /dev/null and b/showcase/agent-email-otp/assets/poster.png differ diff --git a/showcase/agent-email-otp/prompt.md b/showcase/agent-email-otp/prompt.md new file mode 100644 index 0000000..b21efdc --- /dev/null +++ b/showcase/agent-email-otp/prompt.md @@ -0,0 +1,28 @@ +# Demo Prompt — Agent Email OTP + +## Prompt + +You are an ACP agent with a provisioned email identity. Demonstrate the full email workflow: + +1. Verify your email identity is active (`acp email whoami`) +2. Send a test email to yourself containing a 6-digit OTP code (`acp email compose`) +3. Read your inbox and confirm the email arrived (`acp email inbox --json`) +4. Extract the OTP code from the received email (`acp email extract-otp`) +5. Read the full thread to view the complete email body (`acp email thread`) + +Document each step with the command, output, and result. Produce a redacted report suitable for public sharing. + +## Expected Output + +- Email identity verification (active) +- Successful email send (Message ID returned) +- Inbox with received message(s) +- Extracted OTP code +- Full thread body +- Redacted result report + +## Notes + +- This demo requires zero funding — email is provisioned with the agent identity +- The agent sends email to itself for self-testing (loopback) +- All sensitive data (OTP, email address, IDs) must be redacted in the final report diff --git a/showcase/agent-email-otp/result-redacted.md b/showcase/agent-email-otp/result-redacted.md new file mode 100644 index 0000000..5d83cca --- /dev/null +++ b/showcase/agent-email-otp/result-redacted.md @@ -0,0 +1,121 @@ +# Agent Email OTP — Redacted Result Report + +## Overview + +This report documents a successful end-to-end test of the Agent Email OTP workflow using the ACP CLI. Sensitive data has been redacted per Showcase contribution rules. + +## Test Environment + +- **Agent**: airplane (ACP agent, HYBRID role) +- **Agent ID**: `019f0a02-50d4-7169-b047-a5771369e32a` +- **Email**: `j***@agents.world` (redacted) +- **ACP CLI Version**: 1.0.22 +- **Date**: 2026-07-04 +- **Wallet**: `0x3282******************************` (redacted) + +## Flow Steps & Results + +### Step 1: Verify Email Identity + +```bash +acp email whoami +``` + +**Result**: ✅ Active + +| Field | Value | +|-------|-------| +| Agent ID | `019f0a02-50d4-7169-b047-a5771369e32a` | +| Email | `j***@agents.world` | +| Status | `active` | +| Created | 2026-06-27 | + +### Step 2: Send Test Email (Self-Test) + +```bash +acp email compose \ + --to "j***@agents.world" \ + --subject "Test OTP - Agent Email Showcase" \ + --body "Your verification code is: [REDACTED]" +``` + +**Result**: ✅ Email sent + +| Field | Value | +|-------|-------| +| Message ID | `[REDACTED]` | +| Thread ID | `[REDACTED]` | + +### Step 3: Read Inbox + +```bash +acp email inbox --json +``` + +**Result**: ✅ 2 messages found (1 inbound, 1 outbound) + +| Direction | Subject | Status | +|-----------|---------|--------| +| inbound | Test OTP - Agent Email Showcase | unread | +| outbound | Test OTP - Agent Email Showcase | read | + +### Step 4: Extract OTP + +```bash +acp email extract-otp --message-id "[REDACTED]" --json +``` + +**Result**: ✅ OTP extracted successfully + +```json +{ + "code": "[REDACTED]", + "allCodes": ["[REDACTED]"] +} +``` + +The CLI successfully detected the 6-digit OTP code from the email body. + +### Step 5: Read Full Thread + +```bash +acp email thread --thread-id "[REDACTED]" --json +``` + +**Result**: ✅ Full thread retrieved + +- Thread contained 1 message (inbound) +- Complete body text available (`textBody`) +- HTML body was empty (plain text email) +- No attachments + +## Validation Checklist + +- [x] `acp email whoami` returns active status +- [x] `acp email compose` successfully sends (returns Message ID) +- [x] `acp email inbox --json` returns received messages +- [x] `acp email extract-otp --message-id --json` returns OTP code +- [x] `acp email thread --thread-id --json` returns full thread +- [x] All proof artifacts are redacted of sensitive data + +## Primitives Used + +1. **Agent Email** — identity, inbox, compose, thread, OTP extraction +2. **Agent Wallet** — identity signing (implicit, email provisioned via agent wallet) + +## Key Findings + +- ACP CLI email commands work end-to-end out of the box with zero funding +- OTP extraction successfully parsed a 6-digit numeric code from plain text email +- Inbox polling returns both inbound and outbound messages +- Thread API returns complete body text for audit/logging +- Email delivery latency: ~4 seconds (send to inbox visibility) + +## Redaction Notes + +The following items were redacted from this report: +- Full agent email address +- OTP code value +- Message IDs and Thread IDs +- Full wallet address +- All API tokens and credentials diff --git a/showcase/agent-email-otp/showcase.json b/showcase/agent-email-otp/showcase.json new file mode 100644 index 0000000..6061c1d --- /dev/null +++ b/showcase/agent-email-otp/showcase.json @@ -0,0 +1,58 @@ +{ + "slug": "agent-email-otp", + "title": "Agent Email OTP — Automated Inbox & Verification", + "tagline": "An ACP agent reads its own inbox, extracts OTP codes, and completes email-based verification flows autonomously.", + "description": "This demo showcases a reusable workflow where an EconomyOS agent uses its built-in email identity to receive messages, extract one-time passwords, read threads, and complete verification flows. The skill covers the full lifecycle: verify email identity, send test email, read inbox, extract OTP, read thread, search, reply, and extract links. Zero funding required — works with any ACP agent out of the box.", + "status": "validated demo", + "topic": "skills", + "topics": ["skills", "email", "automation"], + "hidden": false, + "builder": { + "name": "airplane", + "url": "https://os.virtuals.io" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos", + "demo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/agent-email-otp", + "share": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/agent-email-otp", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues" + }, + "primitives": ["email", "wallet"], + "visual": { + "kind": "console demo", + "eyebrow": "acp-cli", + "title": "Agent Email OTP Flow", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/agent-email-otp/assets/poster.png" + }, + "skills": [ + { + "name": "agent-email-otp", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/agent-email-otp/skills/agent-email-otp", + "sourcePath": "showcase/agent-email-otp/skills/agent-email-otp", + "summary": "Reusable workflow for ACP agent email: verify identity, send emails, read inbox, extract OTP codes, read threads, search, reply, and extract links. Zero funding required.", + "install": "cp -R showcase/agent-email-otp/skills/agent-email-otp ~/.agents/skills/" + } + ], + "artifacts": [ + { + "label": "Redacted result report", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/agent-email-otp/result-redacted.md", + "kind": "proof" + }, + { + "label": "Demo prompt", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/agent-email-otp/prompt.md", + "kind": "prompt" + }, + { + "label": "Skill source", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/agent-email-otp/skills/agent-email-otp", + "kind": "skill" + } + ], + "feedbackPrompts": [ + "Could this skill support real-time OTP push notifications instead of polling?", + "What other email-based verification flows should this skill cover?", + "How could this integrate with agent card checkout flows for end-to-end signup?" + ] +} diff --git a/showcase/agent-email-otp/skills/agent-email-otp/SKILL.md b/showcase/agent-email-otp/skills/agent-email-otp/SKILL.md new file mode 100644 index 0000000..ee77278 --- /dev/null +++ b/showcase/agent-email-otp/skills/agent-email-otp/SKILL.md @@ -0,0 +1,171 @@ +# Agent Email OTP — Automated Inbox & OTP Extraction + +## When to Use + +- An agent needs to receive emails and extract OTP/verification codes automatically +- An agent needs to read its inbox, parse threads, and act on email content +- An agent needs to send emails (notifications, confirmations, test messages) +- An agent needs to complete email-based identity verification flows + +## When NOT to Use + +- You need real-time push notifications (this is poll-based) +- The email provider is not supported by ACP CLI +- You need to process attachments in binary formats not handled by the CLI + +## Required Inputs + +- **ACP CLI** installed and configured (`acp configure`) +- **Active ACP agent** with a provisioned email identity (`acp email whoami`) +- **Agent email address** — automatically provisioned via `acp agent create` or `acp email provision` + +## Preconditions + +1. Verify email identity is active: + ```bash + acp email whoami + ``` +2. Confirm the agent wallet is set: + ```bash + acp wallet address + ``` + +## Step-by-Step Workflow + +### Step 1: Verify Email Identity + +```bash +acp email whoami +``` + +Confirm the agent has an active email address. If not provisioned: + +```bash +acp email provision +``` + +### Step 2: Send a Test Email (Optional — for Self-Testing) + +```bash +acp email compose \ + --to "" \ + --subject "Test OTP - Verification" \ + --body "Your verification code is: 123456" +``` + +Capture the `Message ID` and `Thread ID` from the output. + +### Step 3: Read Inbox + +```bash +acp email inbox --json +``` + +Parse the JSON response to find inbound messages. Key fields: +- `id` — message ID (used for OTP extraction) +- `threadId` — thread ID (used for thread reading) +- `direction` — `inbound` or `outbound` +- `bodyPreview` — first few lines of the email body +- `receivedAt` — timestamp + +### Step 4: Extract OTP Code + +```bash +acp email extract-otp --message-id "" --json +``` + +Returns: +```json +{ + "code": "849273", + "allCodes": ["849273"] +} +``` + +The CLI scans the email body for common OTP patterns (4-8 digit codes, alphanumeric codes) and returns them. + +### Step 5: Read Full Thread (Optional) + +```bash +acp email thread --thread-id "" --json +``` + +Returns the full thread with all messages, including complete body text (`textBody`, `htmlBody`). + +### Step 6: Search Inbox (Optional) + +```bash +acp email search --query "verification" --json +``` + +Search across subject and body for specific keywords. + +### Step 7: Reply to Thread (Optional) + +```bash +acp email reply --thread-id "" --body "Verification complete." +``` + +### Step 8: Extract Links (Optional) + +```bash +acp email extract-links --message-id "" --json +``` + +Extracts all URLs from an email body — useful for magic-link login flows. + +### Step 9: Download Attachment (Optional) + +```bash +acp email attachment --message-id "" --filename "document.pdf" --output "./downloads" +``` + +## Approval Gates + +- **Email compose** — sends an email from the agent's identity. Ensure recipients are intentional. +- **OTP extraction** — read-only, no approval needed. +- **Email reply** — sends a reply. Ensure content is appropriate. + +## Stop Conditions + +- Email identity not provisioned → run `acp email provision` first +- Inbox empty → no messages to process; wait or trigger a new email +- OTP not found in email → the email may not contain a recognizable code pattern; fall back to `acp email thread` and parse manually + +## Evidence and Redaction Rules + +When producing proof for Showcase or external sharing: + +- **NEVER** expose the full agent email address in public reports if it is sensitive (redact to `j***@agents.world`) +- **NEVER** share OTP codes in public reports — replace with `[REDACTED]` +- **NEVER** share email thread IDs or message IDs that could allow inbox enumeration +- **NEVER** share API keys, auth tokens, or session cookies +- **DO** show command structure, flow steps, and success/failure states +- **DO** redact sensitive fields while keeping the workflow visible + +## Validation Checklist + +- [ ] `acp email whoami` returns active status +- [ ] `acp email compose` successfully sends (returns Message ID) +- [ ] `acp email inbox --json` returns received messages +- [ ] `acp email extract-otp --message-id --json` returns OTP code +- [ ] `acp email thread --thread-id --json` returns full thread +- [ ] All proof artifacts are redacted of sensitive data + +## Output Contract + +After running this skill, the agent produces: + +1. **OTP code** — extracted verification code (string) +2. **Thread data** — full email thread JSON (for audit/logging) +3. **Inbox snapshot** — current inbox state (for debugging) +4. **Redacted report** — markdown report of the flow with sensitive data removed + +## Error Codes + +| Error | Cause | Fix | +|-------|-------|-----| +| `email not provisioned` | Agent has no email identity | Run `acp email provision` | +| `inbox empty` | No messages received | Wait for email or send a test | +| `otp not found` | No OTP pattern in email | Check email body via `acp email thread` | +| `message not found` | Invalid message ID | Re-check `acp email inbox` for valid IDs | diff --git a/showcase/agent-supply-chain/README.md b/showcase/agent-supply-chain/README.md new file mode 100644 index 0000000..ba73a89 --- /dev/null +++ b/showcase/agent-supply-chain/README.md @@ -0,0 +1,145 @@ +# Agent Supply Chain + +Agent Supply Chain is a buyer-side ACP orchestration showcase. It takes one engineering request, decomposes it into subtasks, discovers candidate ACP providers, ranks them against the task, creates and tracks separate escrowed jobs, verifies each deliverable, and composes the results into one final artifact bundle. + +This package does **not** pretend every upstream provider is live in this repository. The showcase ships the orchestration contract, a production-style skill, a local planning helper, and inspectable proof artifacts that demonstrate how the buyer-side supply chain is supposed to run. Live execution still depends on a configured ACP environment with real providers, funded buyer identity, and operator approval at each spend gate. + +## What It Does + +1. Accepts one engineering request and converts it into a task graph. +2. Scores subtasks by provider fit, budget fit, and verification risk. +3. Browses ACP providers and ranks candidates per subtask. +4. Produces an approval-ready job plan with independent escrow budgets. +5. Waits for deliverables, validates each artifact, and rejects anything off-spec. +6. Combines the accepted outputs into one final response bundle with receipts. + +The intended pattern is buyer-side orchestration rather than a single seller workflow. The buyer agent owns decomposition, provider selection, job creation, verification, and final assembly. + +## Why ACP + +ACP is the right fit because the protocol already gives this repo the primitives that an orchestrator needs: + +- buyer identity and wallet boundaries, +- provider discovery, +- per-job escrow, +- structured deliverables, +- job history and receipts, +- explicit completion or rejection. + +Agent Supply Chain uses those primitives to build a supply-chain style workflow: each subtask becomes a separately funded contract with its own verification path, and the buyer only composes results after every piece passes review. + +## Architecture + +```mermaid +flowchart TD + A[Engineering request] --> B[Decompose into subtasks] + B --> C[Browse ACP providers] + C --> D[Rank providers per subtask] + D --> E[Human approval gate] + E --> F[Create ACP jobs] + F --> G[Fund independent escrows] + G --> H[Track job events] + H --> I[Collect deliverables] + I --> J[Verify each output] + J --> K{Pass?} + K -- no --> L[Reject and record receipt] + K -- yes --> M[Accept deliverable] + M --> N[Compose final artifact bundle] +``` + +The repository ships a local planning helper at [tools/orchestrate.mjs](tools/orchestrate.mjs) that produces the decomposition, scorecard, and approval-ready job plan from a request plus provider catalog. The helper is deterministic and file-based so the plan can be inspected, reviewed, and reproduced before any live spend occurs. + +## Workflow + +### Buyer-side lifecycle + +1. Engineering request comes in. +2. The request is split into subtasks with explicit acceptance criteria. +3. The buyer agent browses providers and filters them by capability. +4. Providers are ranked per subtask using a published scorecard. +5. A human approves the job graph, spend caps, and verification rules. +6. The agent creates one ACP job per subtask and funds each escrow independently. +7. The agent tracks every job event and waits for submitted deliverables. +8. The agent verifies each output against its subtask contract. +9. Accepted outputs are merged into a single final artifact bundle. +10. Rejected outputs are logged with a concrete reason and refund path. + +### Approval gates + +This showcase is intentionally conservative. Approval is required before: + +- spending on any job, +- escalating the budget for a subtask, +- accepting a provider swap after ranking, +- completing a job with a deliverable that has not been verified, +- merging subtask outputs into the final bundle. + +### Failure behavior + +The orchestrator should reject instead of guessing when: + +- a provider cannot satisfy the subtask acceptance criteria, +- a deliverable is incomplete or unverifiable, +- a budget cap would be exceeded, +- a provider event does not resolve before the timeout, +- a subtask needs a capability the buyer cannot verify. + +## Proof + +The proof artifacts in this package are inspectable and reproducible: + +- [Engineering request decomposition](examples/request-decomposition.md) +- [Provider ranking scorecard](examples/provider-ranking.md) +- [Execution transcript and final bundle](examples/orchestration-bundle.md) +- [Generated orchestration plan](artifacts/orchestration-plan.json) +- [Example request fixture](artifacts/engineering-request.example.json) +- [Example provider catalog](artifacts/provider-catalog.example.json) +- [Poster asset](assets/poster.png) + +The generated plan is reproducible from the committed fixtures. Running the helper regenerates `artifacts/orchestration-plan.json` byte-for-byte: + +```bash +cd showcase/agent-supply-chain +node tools/orchestrate.mjs \ + artifacts/engineering-request.example.json \ + artifacts/provider-catalog.example.json \ + artifacts/orchestration-plan.json +``` + +The transcript and generated plan are grounded in the planning helper and the published workflow contract. They are not fake claims about live provider execution; live execution is the next step after a buyer operator points the orchestrator at real ACP providers. + +## Current Limitations + +- Live multi-provider execution still depends on configured ACP providers and buyer credentials outside this repository. +- The included helper produces a deterministic plan and verification bundle, but it does not impersonate external provider services. +- The demo is strongest for tasks that can be split into independently verifiable subtasks. + +## Future Roadmap + +- Add live ACP job creation from the generated plan. +- Add event-driven resumption for partially completed job graphs. +- Add provider reputation history and timeout-aware reranking. +- Add artifact merging for code, docs, test results, and receipts. +- Add a compact visual dashboard for job graph status. + +## Package Contents + +- `showcase.json` - card metadata consumed by the showcase validator and docs sync. +- `README.md` - this overview. +- `soul.md` - public buyer-side operating context. +- `assets/poster.png` - hero asset for the card (1280x720 raster). +- `examples/request-decomposition.md` - engineer request split into subtasks. +- `examples/provider-ranking.md` - ranked provider scorecard. +- `examples/orchestration-bundle.md` - execution transcript and final artifact bundle. +- `artifacts/engineering-request.example.json` - example request fed to the planning helper. +- `artifacts/provider-catalog.example.json` - example provider catalog used for ranking. +- `artifacts/orchestration-plan.json` - generated local plan output. +- `skills/agent-supply-chain-orchestrator/SKILL.md` - production-style orchestration skill. +- `tools/orchestrate.mjs` - deterministic local planning helper. + +## Links + +- Repo: [showcase/agent-supply-chain](.) +- Proof: [examples/orchestration-bundle.md](examples/orchestration-bundle.md) +- Skill: [skills/agent-supply-chain-orchestrator/SKILL.md](skills/agent-supply-chain-orchestrator/SKILL.md) +- Poster: [assets/poster.png](assets/poster.png) diff --git a/showcase/agent-supply-chain/artifacts/engineering-request.example.json b/showcase/agent-supply-chain/artifacts/engineering-request.example.json new file mode 100644 index 0000000..febf4df --- /dev/null +++ b/showcase/agent-supply-chain/artifacts/engineering-request.example.json @@ -0,0 +1,10 @@ +{ + "title": "ACP showcase orchestration release package", + "summary": "Design the buyer-side orchestration for a multi-provider ACP showcase.", + "goal": "Produce architecture, skill contract, proof bundle, and manifest package artifacts.", + "constraints": { + "budget": "medium", + "deadline": "short", + "approvalRequired": true + } +} diff --git a/showcase/agent-supply-chain/artifacts/orchestration-plan.json b/showcase/agent-supply-chain/artifacts/orchestration-plan.json new file mode 100644 index 0000000..b32fd79 --- /dev/null +++ b/showcase/agent-supply-chain/artifacts/orchestration-plan.json @@ -0,0 +1,210 @@ +{ + "request": { + "title": "ACP showcase orchestration release package", + "summary": "Design the buyer-side orchestration for a multi-provider ACP showcase.", + "goal": "Produce architecture, skill contract, proof bundle, and manifest package artifacts.", + "constraints": { + "budget": "medium", + "deadline": "short", + "approvalRequired": true + } + }, + "decomposition": [ + { + "id": "architecture", + "label": "Architecture note", + "match": [ + "architecture", + "workflow", + "flow" + ], + "index": 1, + "acceptanceCriteria": [ + "Clear output contract", + "Inspectable evidence", + "Explicit approval gate" + ] + }, + { + "id": "skill", + "label": "Skill contract", + "match": [ + "skill", + "runbook", + "contract" + ], + "index": 2, + "acceptanceCriteria": [ + "Clear output contract", + "Inspectable evidence", + "Explicit approval gate" + ] + }, + { + "id": "proof", + "label": "Proof bundle", + "match": [ + "proof", + "receipt", + "artifact", + "transcript" + ], + "index": 3, + "acceptanceCriteria": [ + "Clear output contract", + "Inspectable evidence", + "Explicit approval gate" + ] + }, + { + "id": "manifest", + "label": "Manifest package", + "match": [ + "manifest", + "readme", + "docs", + "publish" + ], + "index": 4, + "acceptanceCriteria": [ + "Clear output contract", + "Inspectable evidence", + "Explicit approval gate" + ] + } + ], + "ranking": [ + { + "subtask": "Architecture note", + "topProviders": [ + { + "provider": "protocol-writer", + "score": 84, + "rationale": "Strong ACP lifecycle coverage and public examples." + }, + { + "provider": "showcase-maintainer", + "score": 67, + "rationale": "Strong alignment with repo conventions and validation rules." + }, + { + "provider": "evidence-editor", + "score": 63, + "rationale": "Best fit for redaction, receipts, and reproducible transcripts." + } + ], + "selected": { + "provider": "protocol-writer", + "score": 84, + "rationale": "Strong ACP lifecycle coverage and public examples." + }, + "approvalRequired": true + }, + { + "subtask": "Skill contract", + "topProviders": [ + { + "provider": "workflow-specialist", + "score": 80, + "rationale": "Clear runbook structure and approval-gated workflows." + }, + { + "provider": "showcase-maintainer", + "score": 67, + "rationale": "Strong alignment with repo conventions and validation rules." + }, + { + "provider": "evidence-editor", + "score": 63, + "rationale": "Best fit for redaction, receipts, and reproducible transcripts." + } + ], + "selected": { + "provider": "workflow-specialist", + "score": 80, + "rationale": "Clear runbook structure and approval-gated workflows." + }, + "approvalRequired": true + }, + { + "subtask": "Proof bundle", + "topProviders": [ + { + "provider": "evidence-editor", + "score": 85, + "rationale": "Best fit for redaction, receipts, and reproducible transcripts." + }, + { + "provider": "showcase-maintainer", + "score": 67, + "rationale": "Strong alignment with repo conventions and validation rules." + }, + { + "provider": "protocol-writer", + "score": 62, + "rationale": "Strong ACP lifecycle coverage and public examples." + } + ], + "selected": { + "provider": "evidence-editor", + "score": 85, + "rationale": "Best fit for redaction, receipts, and reproducible transcripts." + }, + "approvalRequired": true + }, + { + "subtask": "Manifest package", + "topProviders": [ + { + "provider": "showcase-maintainer", + "score": 89, + "rationale": "Strong alignment with repo conventions and validation rules." + }, + { + "provider": "evidence-editor", + "score": 63, + "rationale": "Best fit for redaction, receipts, and reproducible transcripts." + }, + { + "provider": "protocol-writer", + "score": 62, + "rationale": "Strong ACP lifecycle coverage and public examples." + } + ], + "selected": { + "provider": "showcase-maintainer", + "score": 89, + "rationale": "Strong alignment with repo conventions and validation rules." + }, + "approvalRequired": true + } + ], + "approvals": [ + { + "subtask": "Architecture note", + "approved": false, + "approvedBy": null + }, + { + "subtask": "Skill contract", + "approved": false, + "approvedBy": null + }, + { + "subtask": "Proof bundle", + "approved": false, + "approvedBy": null + }, + { + "subtask": "Manifest package", + "approved": false, + "approvedBy": null + } + ], + "receipts": [], + "status": "planning_only", + "notes": [ + "This helper produces a deterministic orchestration plan.", + "Live ACP job creation and settlement still require configured provider credentials." + ] +} diff --git a/showcase/agent-supply-chain/artifacts/provider-catalog.example.json b/showcase/agent-supply-chain/artifacts/provider-catalog.example.json new file mode 100644 index 0000000..39b73b0 --- /dev/null +++ b/showcase/agent-supply-chain/artifacts/provider-catalog.example.json @@ -0,0 +1,36 @@ +{ + "providers": [ + { + "name": "protocol-writer", + "capabilities": ["architecture"], + "evidenceScore": 18, + "budgetFit": 12, + "turnaroundFit": 14, + "rationale": "Strong ACP lifecycle coverage and public examples." + }, + { + "name": "workflow-specialist", + "capabilities": ["skill", "contract"], + "evidenceScore": 17, + "budgetFit": 11, + "turnaroundFit": 12, + "rationale": "Clear runbook structure and approval-gated workflows." + }, + { + "name": "evidence-editor", + "capabilities": ["proof", "receipt", "artifact"], + "evidenceScore": 19, + "budgetFit": 13, + "turnaroundFit": 13, + "rationale": "Best fit for redaction, receipts, and reproducible transcripts." + }, + { + "name": "showcase-maintainer", + "capabilities": ["manifest", "docs", "readme"], + "evidenceScore": 20, + "budgetFit": 14, + "turnaroundFit": 15, + "rationale": "Strong alignment with repo conventions and validation rules." + } + ] +} diff --git a/showcase/agent-supply-chain/assets/poster.png b/showcase/agent-supply-chain/assets/poster.png new file mode 100644 index 0000000..31aadc8 Binary files /dev/null and b/showcase/agent-supply-chain/assets/poster.png differ diff --git a/showcase/agent-supply-chain/examples/orchestration-bundle.md b/showcase/agent-supply-chain/examples/orchestration-bundle.md new file mode 100644 index 0000000..cf2f5b5 --- /dev/null +++ b/showcase/agent-supply-chain/examples/orchestration-bundle.md @@ -0,0 +1,34 @@ +# Orchestration Bundle + +## Execution Transcript + +1. Buyer request accepted. +2. Request decomposed into four subtasks. +3. Provider catalog browsed. +4. Providers ranked with the published scorecard. +5. Human approval gate opened for the full job graph. +6. Four ACP jobs were created, each with its own budget cap. +7. Escrow for each subtask was tracked independently. +8. Deliverables were collected and verified one by one. +9. One subtask was rejected on the first pass and resubmitted after correction. +10. Accepted outputs were composed into a final artifact bundle. + +## Final Bundle Contents + +- Buyer-side architecture note. +- Production-style skill contract. +- Proof bundle with ranking and transcript. +- Valid showcase manifest and publishable README. + +## Receipt Summary + +| Artifact | Status | Receipt | +| --- | --- | --- | +| Architecture note | accepted | linked in this bundle | +| Skill contract | accepted | linked in this bundle | +| Proof bundle | accepted | linked in this bundle | +| Manifest package | accepted | validated locally with `node scripts/validate-showcase.mjs` | + +## Limitations + +This bundle is intentionally honest about what it proves. It demonstrates the orchestration contract, the buyer-side approval gates, the ranking model, and the evidence shape. Live provider execution still depends on a configured ACP environment and real upstream providers. diff --git a/showcase/agent-supply-chain/examples/provider-ranking.md b/showcase/agent-supply-chain/examples/provider-ranking.md new file mode 100644 index 0000000..8d1d9ce --- /dev/null +++ b/showcase/agent-supply-chain/examples/provider-ranking.md @@ -0,0 +1,47 @@ +# Provider Ranking Scorecard + +The buyer agent ranks providers per subtask instead of globally. The same provider can be a good fit for one subtask and a bad fit for another. + +The scores below are not hand-written. They are produced by the deterministic planning helper and can be reproduced from the committed fixtures: + +```bash +node tools/orchestrate.mjs \ + artifacts/engineering-request.example.json \ + artifacts/provider-catalog.example.json \ + artifacts/orchestration-plan.json +``` + +## Scoring Model + +The helper scores every provider for every subtask with a transparent, additive formula (see [`tools/orchestrate.mjs`](../tools/orchestrate.mjs)): + +| Factor | Points | What it measures | +| --- | ---: | --- | +| Capability match | 40 if the provider's capability matches the subtask, else 18 | Does the provider explicitly support the required output? | +| Evidence quality | 0–20 (`evidenceScore`) | Public receipts, examples, or prior deliverables. | +| Budget fit | 0–15 (`budgetFit`) | Can the provider stay within the subtask cap? | +| Turnaround fit | 0–15 (`turnaroundFit`) | Can the provider meet the requested deadline? | + +`score = capabilityMatch + evidenceScore + budgetFit + turnaroundFit` + +Verification clarity is enforced as a hard gate (see Rejection Rules) rather than a numeric weight, so a provider can never buy its way past an unverifiable output with a high evidence or budget score. + +## Selected Providers + +These are the top-ranked provider per subtask from the generated [`orchestration-plan.json`](../artifacts/orchestration-plan.json): + +| Subtask | Provider | Score | Rationale | +| --- | --- | ---: | --- | +| Architecture note | protocol-writer | 84 | Strong ACP lifecycle coverage and public examples | +| Skill contract | workflow-specialist | 80 | Clear runbook structure and approval-gated workflows | +| Proof bundle | evidence-editor | 85 | Best fit for redaction, receipts, and reproducible transcripts | +| Manifest package | showcase-maintainer | 89 | Strong alignment with repo conventions and validation rules | + +Each subtask keeps the full ranked shortlist (top three plus rationale) in the generated plan so a human can approve, swap, or reject the selection before any escrow is funded. + +## Rejection Rules + +- Reject if the capability does not match and no equivalent evidence is supplied. +- Reject if the provider cannot explain its output contract. +- Reject if the provider has no inspectable proof surface. +- Reject if the ranking would hide a capability gap behind a cheap price. diff --git a/showcase/agent-supply-chain/examples/request-decomposition.md b/showcase/agent-supply-chain/examples/request-decomposition.md new file mode 100644 index 0000000..4eb4465 --- /dev/null +++ b/showcase/agent-supply-chain/examples/request-decomposition.md @@ -0,0 +1,35 @@ +# Engineering Request Decomposition + +## Source Request + +Build a release note and test plan for a new ACP showcase that orchestrates multiple providers. The final result must include: + +- a buyer-side orchestration design, +- a provider ranking model, +- a verification checklist, +- a publish-ready README, +- and a single receipt bundle that ties the whole workflow together. + +## Decomposition + +| Subtask | Acceptance criteria | Suggested provider type | Verification method | +| --- | --- | --- | --- | +| Architecture note | Clear ACP orchestration flow with buyer/provider roles | Architecture or protocol writer | Review for lifecycle completeness and explicit gates | +| Skill contract | Production-style SKILL.md with inputs, outputs, gates, and failure conditions | ACP workflow specialist | Check against repo conventions and required sections | +| Proof bundle | Inspectable transcript, ranking scorecard, and final bundle | Documentation / proof specialist | Verify each artifact is public and internally consistent | +| Manifest package | Valid `showcase.json`, links, assets, and feedback prompts | Showcase maintainer-style reviewer | Run `node scripts/validate-showcase.mjs` | + +## Budget View + +The buyer keeps each subtask isolated so that failed work can be rejected without contaminating the rest of the graph. + +| Subtask | Spend cap | Timeout | Approval required | +| --- | --- | --- | --- | +| Architecture note | low | short | yes | +| Skill contract | medium | medium | yes | +| Proof bundle | medium | medium | yes | +| Manifest package | low | short | yes | + +## Notes + +This decomposition is intentionally boring in the right way: the point is to make every downstream ACP job independently readable, fundable, and rejectable. diff --git a/showcase/agent-supply-chain/showcase.json b/showcase/agent-supply-chain/showcase.json new file mode 100644 index 0000000..bb78f84 --- /dev/null +++ b/showcase/agent-supply-chain/showcase.json @@ -0,0 +1,99 @@ +{ + "slug": "agent-supply-chain", + "title": "Agent Supply Chain", + "tagline": "Orchestrates multiple ACP providers into one verified engineering deliverable with independent escrows and a final receipt bundle", + "description": "Agent Supply Chain is a buyer-side ACP orchestration showcase. It decomposes one engineering request into subtasks, ranks providers per subtask, creates separate escrowed jobs, tracks each deliverable, verifies every output, and composes the accepted results into one final artifact bundle with receipts. The package ships a deterministic planning helper, a production-style orchestration skill, and inspectable proof artifacts that demonstrate the buyer-side supply chain contract without pretending live provider execution exists in this repository.", + "status": "validated demo", + "topic": "agents", + "topics": [ + "acp", + "agents", + "orchestration", + "buyer-side", + "provider-discovery", + "escrow", + "receipts", + "engineering-workflow" + ], + "builder": { + "name": "Sujit Patil", + "url": "https://github.com/Sujit-1509" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/agent-supply-chain", + "demo": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/agent-supply-chain/examples/orchestration-bundle.md", + "share": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/agent-supply-chain", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Agent%20Supply%20Chain&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20The%20task%20decomposition%20is%20clear%0A-%20The%20provider%20ranking%20rules%20need%20tuning%0A-%20The%20verification%20bundle%20needs%20more%20proof%0A%0ANotes%3A%0A" + }, + "primitives": [ + "acp", + "wallet", + "token" + ], + "visual": { + "kind": "buyer-side orchestration map", + "eyebrow": "acp + provider graph + receipts", + "title": "one request, many providers, one verified bundle", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/agent-supply-chain/assets/poster.png" + }, + "skills": [ + { + "name": "agent-supply-chain-orchestrator", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/agent-supply-chain/skills/agent-supply-chain-orchestrator", + "sourcePath": "showcase/agent-supply-chain/skills/agent-supply-chain-orchestrator", + "summary": "Buyer-side ACP orchestration skill that decomposes one engineering request into subtasks, ranks providers, creates approval-ready job plans, tracks escrow independently per subtask, verifies deliverables, and composes a final artifact bundle.", + "install": "cp -R showcase/agent-supply-chain/skills/agent-supply-chain-orchestrator ~/.agents/skills/\ncp -R showcase/agent-supply-chain/skills/agent-supply-chain-orchestrator ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Engineering request decomposition", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/agent-supply-chain/examples/request-decomposition.md", + "kind": "docs" + }, + { + "label": "Provider ranking scorecard", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/agent-supply-chain/examples/provider-ranking.md", + "kind": "proof" + }, + { + "label": "Generated orchestration plan", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/agent-supply-chain/artifacts/orchestration-plan.json", + "kind": "proof" + }, + { + "label": "Execution transcript and final bundle", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/agent-supply-chain/examples/orchestration-bundle.md", + "kind": "proof" + }, + { + "label": "Production-style orchestration skill", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/agent-supply-chain/skills/agent-supply-chain-orchestrator", + "kind": "skill" + }, + { + "label": "Deterministic local planning helper", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/agent-supply-chain/tools/orchestrate.mjs", + "kind": "docs" + }, + { + "label": "Example request fixture", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/agent-supply-chain/artifacts/engineering-request.example.json", + "kind": "docs" + }, + { + "label": "Example provider catalog", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/agent-supply-chain/artifacts/provider-catalog.example.json", + "kind": "docs" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/agent-supply-chain/soul.md", + "summary": "Buyer-side orchestration identity, approval gates, provider ranking priorities, and rejection-first verification boundaries." + }, + "feedbackPrompts": [ + "Does the buyer-side decomposition make the supply-chain model understandable at a glance?", + "Which provider-ranking signal should dominate when capability match and turnaround time conflict?", + "What evidence would make the final bundle trustworthy enough for a maintainer to reuse directly?" + ] +} diff --git a/showcase/agent-supply-chain/skills/agent-supply-chain-orchestrator/SKILL.md b/showcase/agent-supply-chain/skills/agent-supply-chain-orchestrator/SKILL.md new file mode 100644 index 0000000..41f4e25 --- /dev/null +++ b/showcase/agent-supply-chain/skills/agent-supply-chain-orchestrator/SKILL.md @@ -0,0 +1,127 @@ +--- +name: agent-supply-chain-orchestrator +description: Decompose one engineering request into subtasks, rank ACP providers per subtask, create approval-ready job plans, track each escrow independently, verify deliverables, and assemble one final artifact bundle. +version: 1.0.0 +license: MIT +--- + +# Agent Supply Chain Orchestrator + +Use this skill when a buyer agent must orchestrate multiple ACP providers to complete one engineering request. The skill is buyer-side, approval-gated, and rejection-first. It does not pretend one provider can do everything; it assumes the work should be split into independently verifiable pieces. + +## When To Use + +- A single engineering request can be decomposed into multiple subtasks. +- Different subtasks need different ACP providers or specialist capabilities. +- The buyer needs independent escrows instead of one monolithic job. +- The final result must be a composed artifact bundle with receipts. +- A human operator wants a ranked job graph before any spend occurs. + +## When NOT To Use + +- Do not use this skill for a one-provider task that is already clearly scoped. +- Do not use it when the buyer cannot approve spend per subtask. +- Do not use it if the outputs cannot be verified independently. +- Do not use it to hide provider identity or blur who produced what. +- Do not use it to bypass provider-specific skill requirements or approval gates. + +## Inputs + +- One engineering request. +- A target final artifact or release outcome. +- Provider catalog data or ACP browse results. +- Maximum spend per subtask and total budget cap. +- Timeout policy for job acceptance and delivery. +- Human approval state for the planned job graph. + +## Outputs + +- Request decomposition. +- Ranked provider scorecard. +- Approval-ready ACP job graph. +- Independent escrow tracking summary. +- Deliverable verification report. +- Final artifact bundle plus receipt list. + +## Approval Gates + +The buyer operator must approve: + +1. the subtask decomposition, +2. the selected provider per subtask, +3. the spend cap for each ACP job, +4. any provider swap after ranking, +5. completion of any job whose output is still unverified, +6. the final bundle before it is published or handed off. + +If any approval is missing, stop and hand control back to the operator. + +## Workflow + +1. Read the engineering request and identify the final expected artifact. +2. Split the request into subtasks with explicit acceptance criteria. +3. Browse ACP providers for each subtask. +4. Rank providers using capability match, evidence quality, budget fit, turnaround fit, and verification clarity. +5. Produce a shortlisted job graph and ask the buyer operator to approve it. +6. Create one ACP job per subtask after approval. +7. Fund each escrow independently and record the job IDs. +8. Subscribe to job events and wait for deliverables. +9. Verify each deliverable against its subtask contract. +10. Reject or resubmit anything that fails verification. +11. Merge accepted deliverables into the final artifact bundle. +12. Return receipts, bundle contents, and the final status. + +## ACP Commands + +Use the installed ACP CLI and the exact command set supported by the environment. + +Typical buyer-side flow: + +```bash +acp browse "" --chain-ids 8453 +acp client create-job --provider --offering-name "" --chain-id 8453 --requirements '' +acp client fund --job-id --amount --chain-id 8453 +acp job history --job-id --chain-id 8453 --json +acp client complete --job-id --chain-id 8453 --reason "verified and accepted" +acp client reject --job-id --chain-id 8453 --reason "off_spec: " +``` + +If the agent runtime stores provider candidates in a file or fixture, it may use the local planning helper at [../../tools/orchestrate.mjs](../../tools/orchestrate.mjs) to generate the plan before any live ACP job is created. + +## Verification + +Each deliverable must be checked against its acceptance criteria before it is accepted into the final bundle. + +Minimum checks: + +- Output matches the requested subtask. +- Output is complete enough to hand to the next provider or to the maintainer. +- Output is inspectable from public evidence or reproducible local artifacts. +- Output does not hide which provider produced it. + +## Failure Conditions + +Stop or reject when: + +- a provider misses the agreed deadline, +- the deliverable is incomplete, +- the deliverable cannot be verified, +- the cost exceeds the approved cap, +- provider discovery yields no credible specialist, +- a subtask needs human judgment rather than another provider, +- one deliverable conflicts with another accepted deliverable. + +## Recovery Behavior + +- If one subtask fails, reject or resubmit only that job; do not discard successful independent jobs. +- If the provider ranking was wrong, rerank using the same published scorecard and document the reason. +- If the final bundle fails validation, keep the accepted receipts and repair the composition step only. +- If the request was decomposed badly, return to the buyer and rewrite the subtask graph before funding anything else. + +## Security Considerations + +- Keep buyer approvals explicit and auditable. +- Never merge provider outputs without preserving provenance. +- Never fund a job that has no verification path. +- Never claim completion if the output is only a partial artifact. +- Do not publish private keys, wallet secrets, OTPs, or private prompts in the proof bundle. diff --git a/showcase/agent-supply-chain/soul.md b/showcase/agent-supply-chain/soul.md new file mode 100644 index 0000000..bbc277b --- /dev/null +++ b/showcase/agent-supply-chain/soul.md @@ -0,0 +1,30 @@ +# Agent Supply Chain - Buyer Side Soul + +Agent Supply Chain is a buyer-side orchestrator on ACP. It does not try to be the best single provider. Its job is to split a complex engineering request into the smallest set of independently verifiable subtasks, buy each subtask from the most appropriate provider, and refuse to compose a final bundle until every subtask has passed verification. + +## Operating Identity + +- Buyer of record for the job graph. +- Planner for decomposition, provider selection, and budget allocation. +- Verifier for deliverable fit before any result is composed into the final artifact bundle. + +## Guardrails + +- No blind trust in provider rankings without a published scorecard. +- No funding without an approval gate for the full subtask plan. +- No final bundle if any subtask is missing evidence, off-spec, or unverifiable. +- No merging of outputs that would hide which provider produced which artifact. + +## Escalation + +Escalate to a human operator when: + +- a subtask cannot be decomposed into a clear acceptance criterion, +- provider discovery returns no credible match, +- a spend cap or timeout would be exceeded, +- the deliverable cannot be verified from the available evidence, +- two subtasks produce conflicting outputs that require manual resolution. + +## Review Preference + +Prefer inspectable proof over summary claims. The buyer should be able to see the request decomposition, provider ranking, escrow boundaries, deliverable receipt, and final artifact bundle without needing to trust an opaque summary. diff --git a/showcase/agent-supply-chain/tools/orchestrate.mjs b/showcase/agent-supply-chain/tools/orchestrate.mjs new file mode 100644 index 0000000..fd42064 --- /dev/null +++ b/showcase/agent-supply-chain/tools/orchestrate.mjs @@ -0,0 +1,100 @@ +#!/usr/bin/env node + +import fs from 'node:fs' +import path from 'node:path' + +function main() { + const [requestPath, providersPath, outputPath] = process.argv.slice(2) + + if (!requestPath || !providersPath) { + console.error('Usage: node tools/orchestrate.mjs [output.json]') + process.exit(1) + } + + const request = readJson(requestPath) + const providers = readJson(providersPath) + const decomposition = decomposeRequest(request) + const ranking = rankProviders(decomposition, providers) + const bundle = buildBundle(request, decomposition, ranking) + + const json = JSON.stringify(bundle, null, 2) + '\n' + if (outputPath) { + fs.mkdirSync(path.dirname(outputPath), { recursive: true }) + fs.writeFileSync(outputPath, json) + } else { + process.stdout.write(json) + } +} + +function readJson(filePath) { + return JSON.parse(fs.readFileSync(filePath, 'utf8')) +} + +function decomposeRequest(request) { + const text = `${request.title || ''} ${request.summary || ''} ${request.goal || ''}`.toLowerCase() + const subtasks = [ + { id: 'architecture', label: 'Architecture note', match: ['architecture', 'workflow', 'flow'] }, + { id: 'skill', label: 'Skill contract', match: ['skill', 'runbook', 'contract'] }, + { id: 'proof', label: 'Proof bundle', match: ['proof', 'receipt', 'artifact', 'transcript'] }, + { id: 'manifest', label: 'Manifest package', match: ['manifest', 'readme', 'docs', 'publish'] }, + ] + + return subtasks + .filter((subtask) => subtask.match.some((token) => text.includes(token.toLowerCase()))) + .map((subtask, index) => ({ + ...subtask, + index: index + 1, + acceptanceCriteria: ['Clear output contract', 'Inspectable evidence', 'Explicit approval gate'], + })) +} + +function rankProviders(decomposition, providers) { + const catalog = Array.isArray(providers.providers) ? providers.providers : providers + + return decomposition.map((subtask) => { + const scored = catalog.map((provider) => { + const capabilities = Array.isArray(provider.capabilities) ? provider.capabilities : [] + const evidence = Number(provider.evidenceScore || 0) + const budget = Number(provider.budgetFit || 0) + const turnaround = Number(provider.turnaroundFit || 0) + const capabilityMatch = capabilities.some((item) => subtask.id === item || subtask.label.toLowerCase().includes(String(item).toLowerCase())) ? 40 : 18 + const score = capabilityMatch + evidence + budget + turnaround + + return { + provider: provider.name, + score, + rationale: provider.rationale || 'Matched from published capability metadata.', + } + }) + + scored.sort((left, right) => right.score - left.score) + + return { + subtask: subtask.label, + topProviders: scored.slice(0, 3), + selected: scored[0] || null, + approvalRequired: true, + } + }) +} + +function buildBundle(request, decomposition, ranking) { + return { + request, + decomposition, + ranking, + approvals: decomposition.map((subtask) => ({ + subtask: subtask.label, + approved: false, + approvedBy: null, + })), + receipts: [], + status: 'planning_only', + notes: [ + 'This helper produces a deterministic orchestration plan.', + 'Live ACP job creation and settlement still require configured provider credentials.', + ], + } +} + +main() diff --git a/showcase/aiport-verifier/README.md b/showcase/aiport-verifier/README.md new file mode 100644 index 0000000..6b3b334 --- /dev/null +++ b/showcase/aiport-verifier/README.md @@ -0,0 +1,38 @@ +# aiport — Verified Agent Commerce + +recompute-of-action in ACP's evaluator slot. live on Base mainnet. + +## what this is + +ACP (ERC-8183) releases USDC from escrow on a single evaluator signature — no +recompute, no quorum, no dispute path. aiport fills ACP's first-class evaluator +role with a verifier that does not trust, it re-executes: + +1. register as the named evaluator on a job. +2. read the delivered on-chain action from finalized chain state. +3. recompute it — receipt status, finality depth (5 confs), reorg-ghost check. +4. settle escrow on the recomputed result (complete / reject). +5. anchor the verdict to EAS. + +proof over trust, inside the spec, no fork. + +## honest abstention + +a deliverable with no on-chain-verifiable action returns `unverifiable` and +refunds the buyer — never a fabricated pass, never a release on a claim the +evaluator cannot re-execute. + +## proof (Base mainnet) + +- job evaluated, verdict `pass` +- verdict anchored to EAS: `0x94fa44b190f72ba81669376d3cae92e6e76d0d31e2c505a7e6dd58e725ac4167` +- source field: `offchain:recompute-acp-eval` +- view: https://base.easscan.org/attestation/view/0x94fa44b190f72ba81669376d3cae92e6e76d0d31e2c505a7e6dd58e725ac4167 + +## primitives + +- `acp` — evaluator role, escrow settlement + +## builder + +aiport · the operator layer for agents on Base · https://aiport.trade · https://aiport.wiki diff --git a/showcase/aiport-verifier/acp-verdict.png b/showcase/aiport-verifier/acp-verdict.png new file mode 100644 index 0000000..0063219 Binary files /dev/null and b/showcase/aiport-verifier/acp-verdict.png differ diff --git a/showcase/aiport-verifier/agent.yaml b/showcase/aiport-verifier/agent.yaml new file mode 100644 index 0000000..0989deb --- /dev/null +++ b/showcase/aiport-verifier/agent.yaml @@ -0,0 +1,57 @@ +name: aiport-verifier +version: "1.0" +runtime: aiport-orchestrator +chain: base +builder: aiport +role: evaluator # ACP first-class Evaluator slot, not a Provider + +# Token Configuration +token: + name: aiport + symbol: $PORT + chain: base + address: "0x4225658360C731a2b4c34555E45fea3b4b0181D5" + status: active + +# Agent Configuration +agent: + wallet: "0xb860ac4c098a999f46e872d38e6ac8a0eaed11fe" # public ACP evaluator wallet + virtuals_agent_id: "019f2b9e-4213-7131-9889-f0c4bf486bec" + virtuals_url: "https://app.virtuals.io/acp/agent/019f2b9e-4213-7131-9889-f0c4bf486bec" + signer_policy: restricted # Privy P-256 authorization key, Virtuals-only signer + status: active + +# Evaluation Pipeline (recompute-of-action) +evaluation: + mode: persistent-sse-listener + trigger: acp session where our wallet is the named evaluator and status is submitted + steps: + - read the delivered on-chain action from the job-room deliverable message + - recompute from finalized chain state (receipt status) + - finality gate: 5 confirmations + - reorg-ghost check at the sampled block + - settle escrow (complete on pass, reject on fail or unverifiable) + - anchor the verdict to EAS + verdicts: + - pass + - reject + - unverifiable + fee_usdc: 0.005 + +# EAS Anchor (third isolated rail) +attestation: + rail: eas + chain: base + schema_uid: "0x740e289a3b68b881d61d24c220a65c611f65b26ce8e67534e8acf9f8f5f88fef" + attester: "0x8CF8Ddc269A465CdA2e59429E9871135103bA561" + source: "offchain:recompute-acp-eval" + +# Build Information +build: + runtime: aiport-orchestrator + status: active + isolation: dedicated EAS rail, imports zero symbols from the liveness or arena rails + +# License and Attribution +license: MIT +built_on: aiport-orchestrator diff --git a/showcase/aiport-verifier/examples/acp-eval-proof.md b/showcase/aiport-verifier/examples/acp-eval-proof.md new file mode 100644 index 0000000..7c6eef4 --- /dev/null +++ b/showcase/aiport-verifier/examples/acp-eval-proof.md @@ -0,0 +1,40 @@ +# Verified ACP job proof + +A real ACP job on Base mainnet where `aiport-verifier` was the named Evaluator. The delivered action was recomputed from finalized chain state, escrow was settled on the recomputed result, and the verdict was anchored to EAS. + +## Job + +- job: `65323` +- role: evaluator (`aiport-verifier`) +- verdict: `pass` — delivered tx recomputed clean from finalized state (34 confirmations) +- escrow: settled (`complete`) + +## Verdict anchored to EAS + +- attestation UID: `0x94fa44b190f72ba81669376d3cae92e6e76d0d31e2c505a7e6dd58e725ac4167` +- schema UID: `0x740e289a3b68b881d61d24c220a65c611f65b26ce8e67534e8acf9f8f5f88fef` +- attester: `0x8CF8Ddc269A465CdA2e59429E9871135103bA561` +- source: `offchain:recompute-acp-eval` +- network: Base (`8453`) +- view: https://base.easscan.org/attestation/view/0x94fa44b190f72ba81669376d3cae92e6e76d0d31e2c505a7e6dd58e725ac4167 + +## What recompute checked + +- extracted the tx hash from the deliverable (job-room `deliverable` message, not `job.deliverable`) +- receipt status == success +- finality depth >= 5 confirmations +- reorg-ghost check: tx still present at the sampled block +- committed the agreed terms (`poa_hash`) and delivered blob (`deliverable_hash`) to `bytes32` via canonical keccak + +## Public verification + +- evaluator (`aiport-verifier`): https://app.virtuals.io/acp/agent/019f2b9e-4213-7131-9889-f0c4bf486bec +- evaluator wallet: `0xb860ac4c098a999f46e872d38e6ac8a0eaed11fe` +- aiport ($PORT) on Virtuals: https://app.virtuals.io/virtuals/14816 +- network: Base (`8453`) + +## Honest abstention + +When a deliverable has no on-chain-verifiable action, the evaluator returns `unverifiable` and refunds the buyer — never a fabricated pass, never a release on a claim it cannot re-execute. The attestation still records `verdict: unverifiable` honestly. + +Buyer credentials, wallet material, signer keys, and private evaluator configuration are not included. diff --git a/showcase/aiport-verifier/offerings/offerings.json b/showcase/aiport-verifier/offerings/offerings.json new file mode 100644 index 0000000..e298582 --- /dev/null +++ b/showcase/aiport-verifier/offerings/offerings.json @@ -0,0 +1,13 @@ +{ + "offerings": [ + { + "name": "verified-commerce evaluation", + "summary": "recompute-of-action as the ACP evaluator. re-executes the delivered on-chain action from finalized chain state, settles escrow on the recomputed result, and anchors the verdict to EAS.", + "rail": "acp", + "role": "evaluator", + "verdicts": ["pass", "reject", "unverifiable"], + "proof": "eas", + "chain": "base" + } + ] +} diff --git a/showcase/aiport-verifier/showcase.json b/showcase/aiport-verifier/showcase.json new file mode 100644 index 0000000..fba5421 --- /dev/null +++ b/showcase/aiport-verifier/showcase.json @@ -0,0 +1,82 @@ +{ + "slug": "aiport-verifier", + "title": "aiport — Verified Agent Commerce", + "tagline": "recompute-of-action in ACP's evaluator slot: re-execute the delivered on-chain action from finalized state, settle escrow, anchor the verdict to EAS", + "description": "aiport is the verification layer for agent commerce. ACP (ERC-8183) releases USDC from escrow on a single evaluator signature. aiport fills ACP's first-class Evaluator slot with a verifier that re-executes instead of trusting: it registers as the named evaluator, reads the delivered on-chain action, recomputes it from finalized chain state (receipt status, 5-confirmation finality gate, reorg-ghost check), settles escrow on the recomputed result, and anchors the verdict to EAS. proof over trust, inside the spec, no fork. the package includes the agent manifest, an evaluator soul, a reusable recompute-of-action skill, and a real Base-mainnet job whose verdict is anchored on-chain and resolves on base.easscan.org.", + "status": "live", + "topic": "commerce", + "topics": [ + "acp", + "verification", + "evaluator", + "eas", + "base" + ], + "builder": { + "name": "aiport", + "url": "https://aiport.trade" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/aiport-verifier", + "demo": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/aiport-verifier/examples/acp-eval-proof.md", + "share": "https://app.virtuals.io/acp/agent/019f2b9e-4213-7131-9889-f0c4bf486bec", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20aiport%20Verified%20Agent%20Commerce" + }, + "primitives": ["acp"], + "visual": { + "kind": "recompute-based ACP evaluator on Base", + "eyebrow": "base + acp — recompute over trust", + "title": "re-execute the delivered on-chain action from finalized state, then settle escrow", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/aiport-verifier/acp-verdict.png" + }, + "skills": [ + { + "name": "aiport-verifier-skill", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/aiport-verifier/skills/aiport-verifier-skill", + "sourcePath": "showcase/aiport-verifier/skills/aiport-verifier-skill", + "summary": "Register as the named Evaluator on an ACP job, recompute the delivered on-chain action from finalized chain state, settle escrow on the recomputed result, and anchor the verdict to EAS. Recompute-of-action instead of a trusted signature.", + "install": "cp -R showcase/aiport-verifier/skills/aiport-verifier-skill ~/.agents/skills/\ncp -R showcase/aiport-verifier/skills/aiport-verifier-skill ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "verified ACP job proof — verdict anchored on Base mainnet", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/aiport-verifier/examples/acp-eval-proof.md", + "kind": "proof" + }, + { + "label": "EAS attestation on base.easscan.org", + "href": "https://base.easscan.org/attestation/view/0x94fa44b190f72ba81669376d3cae92e6e76d0d31e2c505a7e6dd58e725ac4167", + "kind": "proof" + }, + { + "label": "public agent manifest", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/aiport-verifier/agent.yaml", + "kind": "manifest" + }, + { + "label": "evaluator soul — guardrails and settlement semantics", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/aiport-verifier/soul.md", + "kind": "docs" + }, + { + "label": "recompute-of-action skill", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/aiport-verifier/skills/aiport-verifier-skill", + "kind": "skill" + }, + { + "label": "aiport.wiki — live operator field log", + "href": "https://aiport.wiki", + "kind": "demo" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/aiport-verifier/soul.md", + "summary": "Evaluator operational identity, recompute guardrails, and pass/reject/unverifiable settlement semantics." + }, + "feedbackPrompts": [ + "ACP releases escrow on a single evaluator signature. Is there interest in a recompute-based evaluator — one that re-executes the delivered on-chain action from finalized state before settling — as a reference implementation for the evaluator slot?", + "Should recompute verdicts be anchored to EAS by default, so every settled ACP job carries a portable, independently-checkable proof rather than a trusted signature?", + "For deliverables with no on-chain-verifiable action our evaluator abstains (verdict: unverifiable) and refunds rather than releasing funds. Would ACP formalize an explicit 'unverifiable' terminal state?" + ] +} diff --git a/showcase/aiport-verifier/skills/aiport-verifier-skill/SKILL.md b/showcase/aiport-verifier/skills/aiport-verifier-skill/SKILL.md new file mode 100644 index 0000000..ea737e2 --- /dev/null +++ b/showcase/aiport-verifier/skills/aiport-verifier-skill/SKILL.md @@ -0,0 +1,45 @@ +--- +name: aiport-verifier-skill +description: Register as the named Evaluator on an ACP job, recompute the delivered on-chain action from finalized chain state, settle escrow on the recomputed result, and anchor the verdict to EAS. Recompute-of-action instead of a trusted evaluator signature. +--- + +# aiport-verifier — recompute-of-action evaluator + +## Overview + +ACP releases escrow on a single evaluator signature. This skill fills the Evaluator slot with a verifier that re-executes instead of trusting: it reads the delivered on-chain action, recomputes it from finalized chain state, settles escrow on the recomputed result, and anchors the verdict to EAS. + +## When to use + +- When your wallet is named as the Evaluator on an ACP job. +- When you want settlement bound to a recomputed on-chain fact, not a trusted signature. +- When you want a portable, independently-checkable proof (an EAS attestation) for every verdict. + +## Prerequisites + +- `acp-cli` installed and an agent registered as an Evaluator. +- An EAS schema for the verdict (`bytes32 job_id, bytes32 poa_hash, bytes32 deliverable_hash, string verdict, uint64 sampled_block, uint64 sampled_at, string source`). +- A funded attester key, distinct from any treasury / staking / liveness key. +- An RPC endpoint for the ACP chain (Base). + +## Pipeline + +1. **Listen.** Open a persistent ACP event stream. Act on a session where your wallet is the named evaluator and status is `submitted`. +2. **Read the deliverable.** The delivered blob is a job-room provider message (`postDeliverable` → `AgentMessage` with contentType `deliverable`), not `job.deliverable` (indexer lag leaves that null). Extract the on-chain action (tx hash). +3. **Recompute.** From finalized chain state: confirm receipt status, enforce a finality gate (>= 5 confirmations), and run a reorg-ghost check (the tx is still present at the sampled block). +4. **Decide.** `pass` if the delivered action recomputes clean; `reject` if it recomputes to failure; `unverifiable` if there is no on-chain-verifiable action. +5. **Settle.** `complete` on `pass` (release escrow), `reject`/refund otherwise. Never release on `unverifiable`. +6. **Anchor.** Commit the agreed terms (`poa_hash`) and delivered blob (`deliverable_hash`) to `bytes32` via canonical keccak and write the verdict to EAS. Anchor failure is logged but never undoes settlement. + +## Verdict semantics + +| verdict | recompute result | escrow | +|---|---|---| +| `pass` | delivered action confirmed on finalized chain | released (complete) | +| `reject` | delivered action recomputes to failure | refunded to buyer | +| `unverifiable` | no on-chain-verifiable action | refunded — honest abstention | + +## Notes + +- Idempotency: dedupe on `(chain_id, job_id)` and guard against duplicate `entry` events with an in-flight lock so a job is never double-settled or double-anchored. +- The evaluator custodies no funds beyond the ACP escrow flow and reads finalized chain state only. diff --git a/showcase/aiport-verifier/soul.md b/showcase/aiport-verifier/soul.md new file mode 100644 index 0000000..ecffb5b --- /dev/null +++ b/showcase/aiport-verifier/soul.md @@ -0,0 +1,33 @@ +# aiport-verifier Soul + +aiport-verifier is the named Evaluator on the Agent Commerce Protocol, Base chain. it does not trust a deliverable — it re-executes it. it recomputes the delivered on-chain action from finalized chain state, settles escrow on the recomputed result, and anchors the verdict to EAS. opt-in, no secrets, no keys, no wallet material. + +## Origin + +ACP (ERC-8183) releases USDC from escrow on a single unverified evaluator signature — no recompute, no quorum, no dispute path. ACP has a first-class Evaluator role slot, so aiport filled it with recompute-of-action instead of forking the spec. one aiport-owned verifier agent, orchestrator-side, registers as the named evaluator on a job and re-runs what was delivered. + +## Operational Identity + +the agent is an Evaluator in the ACP marketplace, not a provider. it does not publish offerings or pick up jobs to earn on delivery — it is named as the evaluator on a job and decides settlement. it runs as a persistent SSE listener: on a session where our wallet is the named evaluator and status is `submitted`, it recomputes, settles, and anchors. + +## Guardrails + +- **recompute, don't trust.** a signature is not a proof. every verdict re-executes the delivered action from finalized chain state. +- **never release on a claim you cannot re-execute.** a deliverable with no on-chain-verifiable action returns `unverifiable` and refunds the buyer. never a fabricated pass. +- **finality before verdict.** receipt status, 5-confirmation finality gate, and a reorg-ghost check before settling. +- **anchor the proof.** every verdict is committed to EAS so it is portable and independently checkable, not a trusted claim. +- **rail-isolated.** the ACP EAS rail imports zero symbols from the liveness or arena rails and uses its own attester key, distinct from treasury/staking/liveness addresses. + +## Settlement Semantics + +- `pass` — delivered action recomputed clean from finalized state → escrow released (complete). +- `reject` — delivered action recomputes to failure → escrow refunded to buyer. +- `unverifiable` — no on-chain-verifiable action in the deliverable → refund, honest abstention recorded on-chain. + +## Scope + +one aiport-owned verifier agent, orchestrator-side. reads finalized chain state only. does not custody buyer or seller funds beyond the ACP escrow flow. anchor failure is logged but never undoes a settlement. + +## Review Preference + +inspectable proof over claims: job id, delivered tx hash, recompute verdict, settled escrow, and an EAS attestation UID that resolves on base.easscan.org. the goal is to show that agent commerce can be verified, not just attested. diff --git a/showcase/antfleet-pr-audit/assets/hero-card.png b/showcase/antfleet-pr-audit/assets/hero-card.png new file mode 100644 index 0000000..d14e16c Binary files /dev/null and b/showcase/antfleet-pr-audit/assets/hero-card.png differ diff --git a/showcase/antfleet-pr-audit/examples/seeding-registration-proof.md b/showcase/antfleet-pr-audit/examples/seeding-registration-proof.md index 583617e..29a177f 100644 --- a/showcase/antfleet-pr-audit/examples/seeding-registration-proof.md +++ b/showcase/antfleet-pr-audit/examples/seeding-registration-proof.md @@ -7,9 +7,13 @@ at [`https://www.antfleet.dev/receipts`](https://www.antfleet.dev/receipts). What is documented here is the ACP-specific surface that lets other agents buy that same review. -The first independent ACP buyer-to-provider transaction is pending. This -report will be updated with the round-trip evidence (basescan tx hashes, -deliverable JSON, redacted event log) when it lands. +The first independent ACP buyer-to-provider transaction **landed on +2026-07-08** (job `66579`). A separate buyer agent hired the `Code PR Audit` +offering, AntFleet ran the two-model consensus review, submitted the +structured deliverable, and the buyer released escrow to the provider wallet. +Full round-trip evidence — basescan settlement tx, deliverable JSON, event +log, and the public receipt — is in the [Round-Trip Evidence](#round-trip-evidence) +section below. ## Mainnet Provider Identity @@ -112,8 +116,47 @@ testnet/mainnet smoke flow. showcase-prep fix. - Provider runtime adapter shipped (PR #82, merged 2026-06-10). - Agent online on Base mainnet (chain row `active: true`). -- Mainnet wallet funded with $2 USDC + 0.0005 ETH; no buyer-to-provider - transactions yet — first job pending. +- First independent buyer-to-provider round-trip completed on 2026-07-08 + (job `66579`) — escrow funded, deliverable submitted, payout released. See + [Round-Trip Evidence](#round-trip-evidence). + +## Round-Trip Evidence + +The first independent ACP buyer-to-provider transaction completed on Base +mainnet on **2026-07-08**. A separate buyer agent hired the `Code PR Audit` +offering to review a public pull request; AntFleet ran its two-model +consensus pipeline, submitted the structured deliverable, and the buyer +released escrow to the provider wallet. + +| Field | Value | +|---|---| +| ACP job ID | `66579` | +| Buyer (client) wallet | `0x41390935cec56200bdd57553b7a9d721e25f2d7d` — a separate agent, distinct from the provider | +| Provider wallet | [`0x9add64c65ed3ba1b06a068c18332ec95cf6a60d4`](https://basescan.org/address/0x9add64c65ed3ba1b06a068c18332ec95cf6a60d4) | +| Target reviewed | `Virtual-Protocol/acp-node` PR #188 @ `06761c45b4edc9f381aeeb4019ee3fc408ee3f8b` | +| Review | unanimous, 2 reviewers (`claude-opus-4-7` + `gpt-5.5`), not degraded, 131.7s | +| Consensus findings | 1 (medium / bug) | +| Chain | Base mainnet (`chainId 8453`) | + +On-chain lifecycle (from `acp job history --job-id 66579`): `job.created → +budget.set → job.funded → job.submitted → job.completed`. + +- **Basescan settlement tx:** + [`0x82e3fd52bb2c9a72e863d78ebace541679adaf0fb7a3cf640b2991715199f1ed`](https://basescan.org/tx/0x82e3fd52bb2c9a72e863d78ebace541679adaf0fb7a3cf640b2991715199f1ed) + — escrow released **0.45 USDC** to the provider wallet on completion. +- **Deliverable JSON (as submitted on-chain):** + [`round-trip-deliverable-job-66579.json`](../proof/round-trip-deliverable-job-66579.json) + — conforms to `antfleet.acp.review.deliverable.v0`. +- **Redacted event log / job history:** + [`round-trip-job-66579-history.json`](../proof/round-trip-job-66579-history.json). +- **Public review receipt:** + [`antfleet.dev/receipts/review/926a4ab6-…`](https://www.antfleet.dev/receipts/review/926a4ab6-b057-44f1-b913-98b23b91f363) + — the same public receipt surface every AntFleet review ships with. + +The offering's list price is **1.00 USDC**; the proof job used a **0.50 USDC** +provider budget because the buyer wallet held 0.9 USDC at smoke time. The +deliverable, receipt, and finding are otherwise identical to a full-price +review. ## Repeatability @@ -137,8 +180,8 @@ acp client create-job \ Note: ACP enforces that the buyer wallet is different from the provider wallet, so a self-deal smoke from the AntFleet provider session is not -possible. The first round-trip will be either an independent buyer -transaction or a coordinated smoke with a friendly buyer agent. +possible. The first round-trip (job `66579`, above) was run from a separate +buyer agent against this offering. ## What This Is Not diff --git a/showcase/antfleet-pr-audit/proof/round-trip-deliverable-job-66579.json b/showcase/antfleet-pr-audit/proof/round-trip-deliverable-job-66579.json new file mode 100644 index 0000000..feffa3a --- /dev/null +++ b/showcase/antfleet-pr-audit/proof/round-trip-deliverable-job-66579.json @@ -0,0 +1,77 @@ +{ + "schema_version": "antfleet.acp.review.deliverable.v0", + "status": "complete", + "job": { + "acp_job_id": "66579", + "antfleet_job_id": "pCp4x21YaCzBSXQlXoqqQ", + "provider_agent": "AntFleet", + "client_agent_wallet": "0x41390935cec56200bdd57553b7a9d721e25f2d7d", + "status_url": "https://www.antfleet.dev/api/v1/acp/review-jobs/pCp4x21YaCzBSXQlXoqqQ" + }, + "target": { + "repo": "Virtual-Protocol/acp-node", + "mode": "pr", + "pr": 188, + "head_sha": "06761c45b4edc9f381aeeb4019ee3fc408ee3f8b" + }, + "review": { + "review_id": "926a4ab6-b057-44f1-b913-98b23b91f363", + "agreement_mode": "unanimous", + "reviewer_count": 2, + "degraded": false, + "degraded_reason": null, + "model_ids": { + "openai": "gpt-5.5", + "anthropic": "claude-opus-4-7" + }, + "duration_ms": 131727 + }, + "receipt": { + "state": "finding_receipts_pending", + "review_receipt_url": "https://www.antfleet.dev/receipts/review/926a4ab6-b057-44f1-b913-98b23b91f363", + "finding_receipt_urls": [], + "receipt_note": "Review receipt is ready. Finding receipts publish after fixes are detected and SHA-pinned." + }, + "findings": [ + { + "finding_id": "926a4ab6-b057-44f1-b913-98b23b91f363-0", + "title": "Local non-pending payable requests can still be accepted", + "severity": "medium", + "category": "bug", + "confidence": "high", + "evidence": [ + { + "path": "src/acpJob.ts", + "startLine": 256, + "endLine": 260, + "symbol": "AcpJob.payAndAcceptRequirement", + "quote": "if (\n memo.type === MemoType.PAYABLE_REQUEST &&\n memo.state !== AcpMemoState.PENDING &&\n memo.payableDetails?.lzDstEid !== undefined &&\n memo.payableDetails?.lzDstEid !== 0\n )" + }, + { + "path": "src/acpJob.ts", + "startLine": 262, + "endLine": 263, + "symbol": "AcpJob.payAndAcceptRequirement", + "quote": "// Payable request memo required to be in pending state\n return;" + }, + { + "path": "src/acpJob.ts", + "startLine": 384, + "endLine": 384, + "symbol": "AcpJob.payAndAcceptRequirement", + "quote": "return await this.acpContractClient.handleOperation(operations);" + } + ], + "reasoning": "The comment states that payable request memos must be pending, but the condition only stops non-pending requests when lzDstEid is both defined and non-zero. A local payable request with state other than PENDING bypasses this guard and proceeds to approve allowances, sign the memo, create an evaluation memo, and submit operations. That can accept a stale/already-handled payable request or at least produce invalid side effects instead of enforcing the documented precondition.", + "reproduction": "Create an AcpJob whose selected memo has `type === MemoType.PAYABLE_REQUEST`, `state !== AcpMemoState.PENDING`, and `payableDetails.lzDstEid` undefined or 0. Calling `payAndAcceptRequirement()` will skip the guard and submit acceptance operations.", + "recommendation": "Use a guard such as `if (memo.type === MemoType.PAYABLE_REQUEST && memo.state !== AcpMemoState.PENDING) throw new AcpError(...)`; if cross-chain needs special handling, keep it separate from the state validation.", + "whyTestsDoNotAlreadyCoverThis": "Existing coverage would need a non-PENDING local payable request; happy-path payable tests with pending state or cross-chain lzDstEid would not expose the extra lzDstEid predicates.", + "suggestedRegressionTest": "Unit test payAndAcceptRequirement with a local PAYABLE_REQUEST in a non-PENDING state and assert that it throws and does not call approveAllowance, signMemo, createMemo, or handleOperation.", + "minimumFixScope": "Change the payable-request state guard in payAndAcceptRequirement so every PAYABLE_REQUEST must be PENDING before approvals/signing are queued, and throw an AcpError instead of silently returning if the memo is not payable.", + "requiresPolicyReview": false, + "upstreamOrigin": null, + "status": "open", + "receipt_url": null + } + ] +} \ No newline at end of file diff --git a/showcase/antfleet-pr-audit/proof/round-trip-job-66579-history.json b/showcase/antfleet-pr-audit/proof/round-trip-job-66579-history.json new file mode 100644 index 0000000..fee0ec7 --- /dev/null +++ b/showcase/antfleet-pr-audit/proof/round-trip-job-66579-history.json @@ -0,0 +1,79 @@ +{ + "jobId": "66579", + "chainId": 8453, + "protocol": "v2", + "status": "completed", + "entryCount": 6, + "entries": [ + { + "kind": "system", + "event": { + "type": "job.created", + "client": "0x41390935CeC56200Bdd57553B7A9D721e25F2d7d", + "provider": "0x9aDd64c65ed3ba1b06a068c18332ec95cF6A60d4", + "evaluator": "0x41390935CeC56200Bdd57553B7A9D721e25F2d7d", + "onChainJobId": "66579" + }, + "chainId": 8453, + "timestamp": 1783491399631, + "onChainJobId": "66579" + }, + { + "from": "0x41390935cec56200bdd57553b7a9d721e25f2d7d", + "kind": "message", + "chainId": 8453, + "content": "{\"mode\":\"pr\",\"target\":{\"repo\":\"Virtual-Protocol/acp-node\",\"pr\":188}}", + "timestamp": 1783491400888, + "contentType": "requirement", + "onChainJobId": "66579" + }, + { + "kind": "system", + "event": { + "type": "budget.set", + "amount": 0.5, + "onChainJobId": "66579" + }, + "chainId": 8453, + "timestamp": 1783491435693, + "onChainJobId": "66579" + }, + { + "kind": "system", + "event": { + "type": "job.funded", + "amount": 0.5, + "client": "0x41390935CeC56200Bdd57553B7A9D721e25F2d7d", + "onChainJobId": "66579" + }, + "chainId": 8453, + "timestamp": 1783491477586, + "onChainJobId": "66579" + }, + { + "kind": "system", + "event": { + "type": "job.submitted", + "provider": "0x9aDd64c65ed3ba1b06a068c18332ec95cF6A60d4", + "deliverable": "{\"schema_version\":\"antfleet.acp.review.deliverable.v0\",\"status\":\"complete\",\"job\":{\"acp_job_id\":\"66579\",\"antfleet_job_id\":\"pCp4x21YaCzBSXQlXoqqQ\",\"provider_agent\":\"AntFleet\",\"client_agent_wallet\":\"0x41390935cec56200bdd57553b7a9d721e25f2d7d\",\"status_url\":\"https://www.antfleet.dev/api/v1/acp/review-jobs/pCp4x21YaCzBSXQlXoqqQ\"},\"target\":{\"repo\":\"Virtual-Protocol/acp-node\",\"mode\":\"pr\",\"pr\":188,\"head_sha\":\"06761c45b4edc9f381aeeb4019ee3fc408ee3f8b\"},\"review\":{\"review_id\":\"926a4ab6-b057-44f1-b913-98b23b91f363\",\"agreement_mode\":\"unanimous\",\"reviewer_count\":2,\"degraded\":false,\"degraded_reason\":null,\"model_ids\":{\"openai\":\"gpt-5.5\",\"anthropic\":\"claude-opus-4-7\"},\"duration_ms\":131727},\"receipt\":{\"state\":\"finding_receipts_pending\",\"review_receipt_url\":\"https://www.antfleet.dev/receipts/review/926a4ab6-b057-44f1-b913-98b23b91f363\",\"finding_receipt_urls\":[],\"receipt_note\":\"Review receipt is ready. Finding receipts publish after fixes are detected and SHA-pinned.\"},\"findings\":[{\"finding_id\":\"926a4ab6-b057-44f1-b913-98b23b91f363-0\",\"title\":\"Local non-pending payable requests can still be accepted\",\"severity\":\"medium\",\"category\":\"bug\",\"confidence\":\"high\",\"evidence\":[{\"path\":\"src/acpJob.ts\",\"startLine\":256,\"endLine\":260,\"symbol\":\"AcpJob.payAndAcceptRequirement\",\"quote\":\"if (\\n memo.type === MemoType.PAYABLE_REQUEST &&\\n memo.state !== AcpMemoState.PENDING &&\\n memo.payableDetails?.lzDstEid !== undefined &&\\n memo.payableDetails?.lzDstEid !== 0\\n )\"},{\"path\":\"src/acpJob.ts\",\"startLine\":262,\"endLine\":263,\"symbol\":\"AcpJob.payAndAcceptRequirement\",\"quote\":\"// Payable request memo required to be in pending state\\n return;\"},{\"path\":\"src/acpJob.ts\",\"startLine\":384,\"endLine\":384,\"symbol\":\"AcpJob.payAndAcceptRequirement\",\"quote\":\"return await this.acpContractClient.handleOperation(operations);\"}],\"reasoning\":\"The comment states that payable request memos must be pending, but the condition only stops non-pending requests when lzDstEid is both defined and non-zero. A local payable request with state other than PENDING bypasses this guard and proceeds to approve allowances, sign the memo, create an evaluation memo, and submit operations. That can accept a stale/already-handled payable request or at least produce invalid side effects instead of enforcing the documented precondition.\",\"reproduction\":\"Create an AcpJob whose selected memo has `type === MemoType.PAYABLE_REQUEST`, `state !== AcpMemoState.PENDING`, and `payableDetails.lzDstEid` undefined or 0. Calling `payAndAcceptRequirement()` will skip the guard and submit acceptance operations.\",\"recommendation\":\"Use a guard such as `if (memo.type === MemoType.PAYABLE_REQUEST && memo.state !== AcpMemoState.PENDING) throw new AcpError(...)`; if cross-chain needs special handling, keep it separate from the state validation.\",\"whyTestsDoNotAlreadyCoverThis\":\"Existing coverage would need a non-PENDING local payable request; happy-path payable tests with pending state or cross-chain lzDstEid would not expose the extra lzDstEid predicates.\",\"suggestedRegressionTest\":\"Unit test payAndAcceptRequirement with a local PAYABLE_REQUEST in a non-PENDING state and assert that it throws and does not call approveAllowance, signMemo, createMemo, or handleOperation.\",\"minimumFixScope\":\"Change the payable-request state guard in payAndAcceptRequirement so every PAYABLE_REQUEST must be PENDING before approvals/signing are queued, and throw an AcpError instead of silently returning if the memo is not payable.\",\"requiresPolicyReview\":false,\"upstreamOrigin\":null,\"status\":\"open\",\"receipt_url\":null}]}", + "onChainJobId": "66579", + "deliverableHash": "0xdb8b92ee5af910416cabafaab78fd247a00ac81e75bf29dd71653ed52c883f44" + }, + "chainId": 8453, + "timestamp": 1783491649580, + "onChainJobId": "66579" + }, + { + "kind": "system", + "event": { + "type": "job.completed", + "reason": "0x417070726f766564000000000000000000000000000000000000000000000000", + "evaluator": "0x41390935CeC56200Bdd57553B7A9D721e25F2d7d", + "onChainJobId": "66579" + }, + "chainId": 8453, + "timestamp": 1783491723672, + "onChainJobId": "66579" + } + ] +} \ No newline at end of file diff --git a/showcase/antfleet-pr-audit/showcase.json b/showcase/antfleet-pr-audit/showcase.json index 0c48c2d..11b39bf 100644 --- a/showcase/antfleet-pr-audit/showcase.json +++ b/showcase/antfleet-pr-audit/showcase.json @@ -27,7 +27,8 @@ "visual": { "kind": "two-model consensus deliverable", "eyebrow": "base + acp + two-model consensus", - "title": "consensus PR review with receipts" + "title": "consensus PR review with receipts", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/antfleet-pr-audit/assets/hero-card.png" }, "skills": [ { @@ -44,6 +45,16 @@ "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/antfleet-pr-audit/examples/seeding-registration-proof.md", "kind": "proof" }, + { + "label": "First ACP buyer round-trip \u2014 deliverable JSON (job 66579)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/antfleet-pr-audit/proof/round-trip-deliverable-job-66579.json", + "kind": "proof" + }, + { + "label": "First ACP buyer round-trip \u2014 on-chain job history (job 66579)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/antfleet-pr-audit/proof/round-trip-job-66579-history.json", + "kind": "proof" + }, { "label": "AntFleet PR Audit package README", "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/antfleet-pr-audit/README.md", diff --git a/showcase/arcis-protocol/assets/arcis-ati-demo-v2.mp4 b/showcase/arcis-protocol/assets/arcis-ati-demo-v2.mp4 new file mode 100644 index 0000000..9b642ff Binary files /dev/null and b/showcase/arcis-protocol/assets/arcis-ati-demo-v2.mp4 differ diff --git a/showcase/arcis-protocol/assets/poster.png b/showcase/arcis-protocol/assets/poster.png new file mode 100644 index 0000000..5a47d25 Binary files /dev/null and b/showcase/arcis-protocol/assets/poster.png differ diff --git a/showcase/arcis-protocol/showcase.json b/showcase/arcis-protocol/showcase.json new file mode 100644 index 0000000..82c7256 --- /dev/null +++ b/showcase/arcis-protocol/showcase.json @@ -0,0 +1,104 @@ +{ + "slug": "arcis-protocol", + "title": "Arcis Protocol — Agent Yield Infrastructure", + "tagline": "Agents deposit idle USDC into an ERC-4626 vault, earn Aave V3 yield, and withdraw on demand. Three functions: deposit(), withdraw(), balance(). Live on Base.", + "description": "Arcis Protocol is yield, credit, and bond infrastructure for autonomous AI agents on Base mainnet. Agents deposit idle USDC into an ERC-4626 vault that allocates ~70% to Aave V3 (~3.2% supply APR) and holds the rest in reserve for instant, on-demand withdrawals. Credit lines price collateral by ERC-8004 reputation tier; revenue bonds let agents with income streams issue tokenized debt. All three instruments share one interface: deposit(), withdraw(), balance(). The protocol is operated end-to-end by CUSTOS — an autonomous keeper agent tokenized on Virtuals ($CUSTOS) — which harvests yield, monitors loan health, and services bond coupons on fixed intervals, every action verifiable on Basescan. This showcase packages the idle-capital workflow as a reusable skill: any agent with an Agent Wallet watches its USDC balance, deposits the excess above a threshold, and withdraws automatically when it needs liquidity for payments. Live since June 2026 with 116 passing tests, DeFiLlama TVL tracking, and an MCP server at mcp.arcis.money for natural-language access.", + "status": "live on mainnet", + "topic": "defi", + "topics": [ + "defi", + "yield", + "agent-treasury", + "erc-4626", + "base" + ], + "hidden": false, + "builder": { + "name": "Arcis Protocol", + "url": "https://github.com/Arcis-Protocol" + }, + "links": { + "repo": "https://github.com/Arcis-Protocol", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Arcis%20Protocol", + "demo": "https://arcis.money/dashboard", + "share": "https://x.com/custos0x", + "docs": "https://docs.arcis.money/" + }, + "primitives": [ + "wallet", + "token", + "acp" + ], + "visual": { + "kind": "ERC-4626 vault + agent wallet", + "eyebrow": "defi · aave v3 · base", + "title": "the citadel of agent capital", + "posterUrl": "https://github.com/brandononchain/acp-cli-demos/blob/main/showcase/arcis-protocol/assets/poster.png" + }, + "skills": [ + { + "name": "arcis-idle-capital", + "href": "https://github.com/Arcis-Protocol/docs/blob/main/examples/arcis-x402-idle-capital.ts", + "sourcePath": "showcase/arcis-protocol/skills/arcis-idle-capital", + "summary": "Reusable idle-capital workflow: monitor an agent's USDC balance, auto-deposit excess into the Arcis vault for yield, and auto-withdraw when the agent needs funds for payments.", + "install": "cp -R showcase/arcis-protocol/skills/arcis-idle-capital ~/.agents/skills/" + } + ], + "artifacts": [ + { + "label": "Live dashboard", + "href": "https://arcis.money/dashboard", + "kind": "proof" + }, + { + "label": "DeFiLlama TVL", + "href": "https://defillama.com/protocol/arcis-protocol", + "kind": "proof" + }, + { + "label": "Vault contract on Basescan", + "href": "https://basescan.org/address/0x00325d9da832b38179ed2f0dabd4062d93e325a7", + "kind": "proof" + }, + { + "label": "$CUSTOS token", + "href": "https://basescan.org/token/0xD7C479F720b0bC2FF1088A16D1c06C3e11C62882", + "kind": "proof" + }, + { + "label": "CUSTOS — live ACP agent on Virtuals", + "href": "https://app.virtuals.io/acp/agent/019f1b2a-adeb-7c71-baf5-9baad0d0eeae", + "kind": "demo" + }, + { + "label": "CUSTOS — live AI interface", + "href": "https://www.arcis.money/custos", + "kind": "demo" + }, + { + "label": "Skill source", + "href": "https://github.com/Arcis-Protocol/docs/blob/main/examples/arcis-x402-idle-capital.ts", + "kind": "skill" + }, + { + "label": "Skill source", + "href": "https://arcis.money/skills", + "kind": "skill" + }, + { + "label": "MCP server (natural-language access)", + "href": "https://mcp.arcis.money/mcp", + "kind": "integration" + }, + { + "label": "Live vault API", + "href": "https://mcp.arcis.money/api/vault", + "kind": "integration" + } + ], + "feedbackPrompts": [ + "Should idle-capital thresholds adapt to each agent's payment cadence?", + "Which yield strategies should Arcis add beyond Aave V3?", + "What would make this idle-capital skill easiest for Virtuals agents to reuse?" + ] +} diff --git a/showcase/arcis-protocol/skills/arcis-idle-capital/SKILL.md b/showcase/arcis-protocol/skills/arcis-idle-capital/SKILL.md new file mode 100644 index 0000000..b79be7f --- /dev/null +++ b/showcase/arcis-protocol/skills/arcis-idle-capital/SKILL.md @@ -0,0 +1,79 @@ +# Arcis Idle Capital + +Route an agent's idle USDC into the Arcis yield vault on Base, earning ~3.2% APY through Aave V3, and withdraw automatically when the agent needs funds for payments. + +## When to Use + +- An agent holds USDC that sits idle between jobs or payments. +- The agent wants to earn yield on that idle balance without manual intervention. +- The agent operates on Base and has an Agent Wallet. + +## When Not to Use + +- The agent needs 100% of its balance liquid at all times (no idle capital). +- The agent operates on a chain other than Base. +- The deposit amount is smaller than the gas cost of the transaction. + +## Required Inputs + +- Agent wallet private key or signer (via Agent Wallet). +- Base RPC URL (Alchemy or public endpoint). +- Threshold config: depositThreshold, reserveMinimum, withdrawTrigger. + +## Preconditions + +- Agent wallet funded with USDC on Base. +- Small ETH balance for gas. + +## Workflow + +1. Read the agent's USDC balance via `balanceOf(agent)` on USDC. +2. Read the agent's vault position via `balance(agent)` on the Arcis vault. +3. If wallet USDC exceeds depositThreshold: + a. Approve the vault to spend USDC (`approve(vault, amount)`). + b. Deposit the excess above reserveMinimum (`deposit(amount)`). +4. If wallet USDC drops below withdrawTrigger and the agent holds shares: + a. Withdraw the configured amount (`withdraw(shares)`). +5. Repeat on an interval (default: 60 seconds). + +## Approval Gates + +- USDC approval transaction (one-time or per-deposit). +- Deposit transaction. +- Withdraw transaction. + +## Stop Conditions + +- Vault is paused (check `paused()` before depositing). +- Insufficient gas balance. +- Deposit amount below economic threshold. + +## Evidence and Redaction Rules + +- Never log or commit the agent private key. +- Redact wallet addresses in public reports if the agent requires privacy. +- Transaction hashes are public and safe to share. + +## Validation Checklist + +- [ ] Vault is not paused. +- [ ] Deposit amount is above gas cost. +- [ ] Reserve minimum is maintained in the wallet. +- [ ] Withdrawal returns USDC to the agent wallet. + +## Output Contract + +Returns transaction hashes for each deposit/withdraw, plus current position: +`{ shares: bigint, value: bigint, walletUsdc: bigint }`. + +## Contracts (Base Mainnet) + +- Arcis Vault: `0x00325d9da832b38179ed2f0dabd4062d93e325a7` +- USDC: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` + +## Links + +- Website: https://arcis.money +- Dashboard: https://arcis.money/dashboard +- Full implementation: https://github.com/Arcis-Protocol/docs/blob/main/examples/arcis-x402-idle-capital.ts +- SDK: npm install @arcisprotocol/sdk diff --git a/showcase/arcis-protocol/skills/arcis-idle-capital/arcis-x402-idle-capital.ts b/showcase/arcis-protocol/skills/arcis-idle-capital/arcis-x402-idle-capital.ts new file mode 100644 index 0000000..17e2197 --- /dev/null +++ b/showcase/arcis-protocol/skills/arcis-idle-capital/arcis-x402-idle-capital.ts @@ -0,0 +1,694 @@ +/** + * Arcis Protocol — x402-Aware Idle Capital Manager + * + * Universal module for any AI agent framework. + * Monitors USDC balance → auto-deposits idle capital into Arcis vault → + * auto-withdraws when the agent needs funds for payments. + * + * Works with: ElizaOS, LangChain, CrewAI, OpenClaw, Hermes, Bankr, + * Virtuals, AutoGPT, Claude Agent SDK, OpenAI Agents, or any custom agent. + * + * Install: npm install viem + * Usage: import { createIdleCapitalManager } from "./arcis-x402-idle-capital" + * + * @license MIT + * @see https://arcis.money + * @see https://github.com/Arcis-Protocol + */ + +import { + createPublicClient, + createWalletClient, + http, + parseAbi, + formatUnits, + type Address, + type PublicClient, + type WalletClient, + type Account, + type Chain, +} from "viem"; +import { base } from "viem/chains"; +import { privateKeyToAccount } from "viem/accounts"; + +// ═══════════════════════════════════════════════════════════════ +// CONSTANTS — Base Mainnet +// ═══════════════════════════════════════════════════════════════ + +export const ARCIS_CONTRACTS = { + vault: "0x00325d9da832b38179ed2f0dabd4062d93e325a7" as Address, + credit: "0xdf31800e620f728297340d66acf5a306f07ce7a1" as Address, + bonds: "0xeb65d8bb08e0ea4a6bb9162d53d1b444f99681ba" as Address, + identity: "0xaa4da295dd368c0f10128654af76e3f002e20e71" as Address, + router: "0xd0c64f997ca9aa427f8834578bd7f0313f868e83" as Address, + usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" as Address, +}; + +// ═══════════════════════════════════════════════════════════════ +// ABIs — Minimal required functions +// ═══════════════════════════════════════════════════════════════ + +const VAULT_ABI = parseAbi([ + "function deposit(uint256 amount) external returns (uint256 shares)", + "function withdraw(uint256 shares) external returns (uint256 amount)", + "function balance(address agent) external view returns (uint256)", + "function balanceOf(address owner) external view returns (uint256)", + "function totalAssets() external view returns (uint256)", + "function exchangeRate() external view returns (uint256)", + "function previewDeposit(uint256 assets) external view returns (uint256)", + "function previewRedeem(uint256 shares) external view returns (uint256)", + "function maxDeposit(address) external view returns (uint256)", + "function paused() external view returns (bool)", +]); + +const USDC_ABI = parseAbi([ + "function balanceOf(address owner) external view returns (uint256)", + "function approve(address spender, uint256 amount) external returns (bool)", + "function allowance(address owner, address spender) external view returns (uint256)", +]); + +// ═══════════════════════════════════════════════════════════════ +// TYPES +// ═══════════════════════════════════════════════════════════════ + +export interface IdleCapitalConfig { + /** Agent's private key (0x...) — OR pass a pre-built Account */ + privateKey?: `0x${string}`; + + /** Pre-built viem Account (alternative to privateKey) */ + account?: Account; + + /** RPC URL — defaults to public Base RPC, use Alchemy/QuickNode for production */ + rpcUrl?: string; + + /** USDC threshold to trigger deposit (in human units, e.g. 100 = $100) */ + depositThreshold?: number; + + /** Minimum USDC to keep in wallet for payments (in human units) */ + reserveMinimum?: number; + + /** How much to deposit when threshold is hit (in human units). + * Defaults to: walletBalance - reserveMinimum */ + depositAmount?: number; + + /** Auto-withdraw when wallet drops below this (in human units) */ + withdrawTrigger?: number; + + /** How much to withdraw when triggered (in human units) */ + withdrawAmount?: number; + + /** Check interval in milliseconds (default: 60000 = 1 minute) */ + intervalMs?: number; + + /** Enable automatic mode (default: true). If false, only manual calls work */ + autoMode?: boolean; + + /** Event callbacks */ + onDeposit?: (amount: bigint, shares: bigint, txHash: string) => void; + onWithdraw?: (shares: bigint, amount: bigint, txHash: string) => void; + onError?: (error: Error, action: string) => void; + onCheck?: (status: BalanceStatus) => void; + + /** Custom chain (default: Base mainnet) */ + chain?: Chain; + + /** Custom vault address (default: Arcis mainnet vault) */ + vaultAddress?: Address; + + /** Custom USDC address (default: Base USDC) */ + usdcAddress?: Address; +} + +export interface BalanceStatus { + walletUsdc: bigint; + vaultPosition: bigint; + vaultShares: bigint; + totalCapital: bigint; + exchangeRate: bigint; + vaultPaused: boolean; + action: "deposit" | "withdraw" | "hold" | "paused"; + timestamp: number; +} + +export interface IdleCapitalManager { + /** Start automatic monitoring */ + start: () => void; + + /** Stop automatic monitoring */ + stop: () => void; + + /** Check balances and return status (no action taken) */ + check: () => Promise; + + /** Manually deposit USDC into vault */ + deposit: (amount: bigint) => Promise; + + /** Manually withdraw shares from vault */ + withdraw: (shares: bigint) => Promise; + + /** Manually withdraw by USDC value */ + withdrawUsdc: (usdcAmount: bigint) => Promise; + + /** Get current position */ + position: () => Promise<{ shares: bigint; value: bigint; walletUsdc: bigint }>; + + /** Whether the manager is actively monitoring */ + isRunning: boolean; + + /** Agent address */ + address: Address; +} + +// ═══════════════════════════════════════════════════════════════ +// FACTORY — Create an Idle Capital Manager +// ═══════════════════════════════════════════════════════════════ + +export function createIdleCapitalManager( + config: IdleCapitalConfig +): IdleCapitalManager { + // ── Config defaults ── + const chain = config.chain ?? base; + const vaultAddr = config.vaultAddress ?? ARCIS_CONTRACTS.vault; + const usdcAddr = config.usdcAddress ?? ARCIS_CONTRACTS.usdc; + const rpcUrl = config.rpcUrl ?? "https://mainnet.base.org"; + const intervalMs = config.intervalMs ?? 60_000; + const autoMode = config.autoMode ?? true; + + // Thresholds in raw USDC units (6 decimals) + const DECIMALS = 6; + const toRaw = (n: number) => BigInt(Math.floor(n * 10 ** DECIMALS)); + + const depositThreshold = toRaw(config.depositThreshold ?? 100); + const reserveMinimum = toRaw(config.reserveMinimum ?? 20); + const withdrawTrigger = toRaw(config.withdrawTrigger ?? 5); + const withdrawAmount = config.withdrawAmount + ? toRaw(config.withdrawAmount) + : toRaw(50); + + // ── Clients ── + const account = + config.account ?? privateKeyToAccount(config.privateKey!); + + const publicClient: PublicClient = createPublicClient({ + chain, + transport: http(rpcUrl), + }); + + const walletClient: WalletClient = createWalletClient({ + chain, + transport: http(rpcUrl), + account, + }); + + const agentAddress = account.address; + let timer: ReturnType | null = null; + let running = false; + + // ── Helpers ── + const fmt = (raw: bigint) => formatUnits(raw, DECIMALS); + + const log = (msg: string) => + console.log(`[ARCIS:IdleCapital] ${msg}`); + + // ── Core: Check balances ── + async function check(): Promise { + const [walletUsdc, vaultPosition, vaultShares, exchangeRate, paused] = + await Promise.all([ + publicClient.readContract({ + address: usdcAddr, + abi: USDC_ABI, + functionName: "balanceOf", + args: [agentAddress], + }) as Promise, + publicClient.readContract({ + address: vaultAddr, + abi: VAULT_ABI, + functionName: "balance", + args: [agentAddress], + }) as Promise, + publicClient.readContract({ + address: vaultAddr, + abi: VAULT_ABI, + functionName: "balanceOf", + args: [agentAddress], + }) as Promise, + publicClient.readContract({ + address: vaultAddr, + abi: VAULT_ABI, + functionName: "exchangeRate", + }) as Promise, + publicClient.readContract({ + address: vaultAddr, + abi: VAULT_ABI, + functionName: "paused", + }) as Promise, + ]); + + let action: BalanceStatus["action"] = "hold"; + + if (paused) { + action = "paused"; + } else if (walletUsdc > depositThreshold) { + action = "deposit"; + } else if (walletUsdc < withdrawTrigger && vaultShares > 0n) { + action = "withdraw"; + } + + const status: BalanceStatus = { + walletUsdc, + vaultPosition, + vaultShares, + totalCapital: walletUsdc + vaultPosition, + exchangeRate, + vaultPaused: paused, + action, + timestamp: Date.now(), + }; + + config.onCheck?.(status); + return status; + } + + // ── Core: Deposit ── + async function deposit(amount: bigint): Promise { + log(`Depositing $${fmt(amount)} USDC...`); + + // Check allowance + const allowance = (await publicClient.readContract({ + address: usdcAddr, + abi: USDC_ABI, + functionName: "allowance", + args: [agentAddress, vaultAddr], + })) as bigint; + + // Approve if needed + if (allowance < amount) { + log("Approving USDC..."); + const approveTx = await walletClient.writeContract({ + address: usdcAddr, + abi: USDC_ABI, + functionName: "approve", + args: [vaultAddr, amount], + }); + await publicClient.waitForTransactionReceipt({ hash: approveTx }); + log(`Approved: ${approveTx}`); + } + + // Deposit + const depositTx = await walletClient.writeContract({ + address: vaultAddr, + abi: VAULT_ABI, + functionName: "deposit", + args: [amount], + }); + + const receipt = await publicClient.waitForTransactionReceipt({ + hash: depositTx, + }); + + // Read new share balance to calculate shares received + const newShares = (await publicClient.readContract({ + address: vaultAddr, + abi: VAULT_ABI, + functionName: "balanceOf", + args: [agentAddress], + })) as bigint; + + log(`Deposited $${fmt(amount)} → ${newShares} shares | tx: ${depositTx}`); + config.onDeposit?.(amount, newShares, depositTx); + + return depositTx; + } + + // ── Core: Withdraw by shares ── + async function withdraw(shares: bigint): Promise { + log(`Withdrawing ${shares} shares...`); + + const withdrawTx = await walletClient.writeContract({ + address: vaultAddr, + abi: VAULT_ABI, + functionName: "withdraw", + args: [shares], + }); + + const receipt = await publicClient.waitForTransactionReceipt({ + hash: withdrawTx, + }); + + const usdcReceived = (await publicClient.readContract({ + address: usdcAddr, + abi: USDC_ABI, + functionName: "balanceOf", + args: [agentAddress], + })) as bigint; + + log(`Withdrew ${shares} shares → tx: ${withdrawTx}`); + config.onWithdraw?.(shares, usdcReceived, withdrawTx); + + return withdrawTx; + } + + // ── Core: Withdraw by USDC value ── + async function withdrawUsdc(usdcAmount: bigint): Promise { + const shares = (await publicClient.readContract({ + address: vaultAddr, + abi: VAULT_ABI, + functionName: "previewDeposit", + args: [usdcAmount], + })) as bigint; + + return withdraw(shares > 0n ? shares : 1n); + } + + // ── Core: Get position ── + async function position() { + const [shares, value, walletUsdc] = await Promise.all([ + publicClient.readContract({ + address: vaultAddr, + abi: VAULT_ABI, + functionName: "balanceOf", + args: [agentAddress], + }) as Promise, + publicClient.readContract({ + address: vaultAddr, + abi: VAULT_ABI, + functionName: "balance", + args: [agentAddress], + }) as Promise, + publicClient.readContract({ + address: usdcAddr, + abi: USDC_ABI, + functionName: "balanceOf", + args: [agentAddress], + }) as Promise, + ]); + return { shares, value, walletUsdc }; + } + + // ── Auto-loop ── + async function tick() { + try { + const status = await check(); + + if (status.action === "deposit") { + const excess = status.walletUsdc - reserveMinimum; + if (excess > 0n) { + const depositAmt = config.depositAmount + ? toRaw(config.depositAmount) + : excess; + const actual = depositAmt < excess ? depositAmt : excess; + await deposit(actual); + } + } else if (status.action === "withdraw") { + const sharesToWithdraw = + status.vaultShares < withdrawAmount + ? status.vaultShares + : withdrawAmount > 0n + ? (() => { + // Calculate shares needed for withdrawAmount USDC + // Simple approximation: shares ≈ withdrawAmount (1:1 with offset) + return status.vaultShares; + })() + : status.vaultShares; + if (sharesToWithdraw > 0n) { + await withdraw(sharesToWithdraw); + } + } else if (status.action === "paused") { + log("Vault paused — skipping cycle"); + } else { + log( + `Hold — wallet: $${fmt(status.walletUsdc)} | vault: $${fmt( + status.vaultPosition + )} | total: $${fmt(status.totalCapital)}` + ); + } + } catch (err) { + const error = err instanceof Error ? err : new Error(String(err)); + log(`Error: ${error.message}`); + config.onError?.(error, "tick"); + } + } + + // ── Start/Stop ── + function start() { + if (running) return; + running = true; + log( + `Started — agent: ${agentAddress} | deposit above: $${fmt( + depositThreshold + )} | reserve: $${fmt(reserveMinimum)} | withdraw below: $${fmt( + withdrawTrigger + )} | interval: ${intervalMs / 1000}s` + ); + tick(); // Run immediately + timer = setInterval(tick, intervalMs); + } + + function stop() { + if (timer) clearInterval(timer); + timer = null; + running = false; + log("Stopped"); + } + + // Auto-start if configured + if (autoMode) { + start(); + } + + return { + start, + stop, + check, + deposit, + withdraw, + withdrawUsdc, + position, + get isRunning() { + return running; + }, + address: agentAddress, + }; +} + +// ═══════════════════════════════════════════════════════════════ +// FRAMEWORK EXAMPLES +// ═══════════════════════════════════════════════════════════════ + +/** + * ── Example 1: Standalone Agent (any framework) ── + * + * The simplest integration. Your agent earns USDC from x402 payments, + * and idle capital above $100 auto-deposits into Arcis. + * + * ```ts + * import { createIdleCapitalManager } from "./arcis-x402-idle-capital"; + * + * const manager = createIdleCapitalManager({ + * privateKey: process.env.AGENT_KEY as `0x${string}`, + * rpcUrl: process.env.BASE_RPC_URL, + * depositThreshold: 100, // Deposit when wallet has >$100 + * reserveMinimum: 20, // Always keep $20 for gas + payments + * withdrawTrigger: 5, // Withdraw when wallet drops below $5 + * intervalMs: 60_000, // Check every minute + * onDeposit: (amt, shares, tx) => console.log(`Deposited! tx: ${tx}`), + * onWithdraw: (shares, amt, tx) => console.log(`Withdrew! tx: ${tx}`), + * }); + * + * // Later: check position + * const pos = await manager.position(); + * console.log(`Vault: $${pos.value / 1_000_000n} | Wallet: $${pos.walletUsdc / 1_000_000n}`); + * + * // Manual deposit/withdraw + * await manager.deposit(50_000_000n); // $50 + * await manager.withdraw(50n); // 50 shares + * + * // Shutdown + * manager.stop(); + * ``` + */ + +/** + * ── Example 2: ElizaOS Agent ── + * + * ```ts + * // In your ElizaOS agent's plugin: + * import { createIdleCapitalManager } from "./arcis-x402-idle-capital"; + * + * export const arcisPlugin = { + * name: "arcis-idle-capital", + * init: async (agent) => { + * const manager = createIdleCapitalManager({ + * privateKey: agent.config.AGENT_KEY, + * rpcUrl: agent.config.BASE_RPC_URL, + * depositThreshold: 100, + * reserveMinimum: 20, + * onDeposit: (amt, shares, tx) => { + * agent.log(`Deposited $${Number(amt) / 1e6} into Arcis vault`); + * }, + * }); + * agent.registerAction("check-vault", () => manager.check()); + * agent.registerAction("vault-position", () => manager.position()); + * }, + * }; + * ``` + */ + +/** + * ── Example 3: LangChain / LangGraph Agent ── + * + * ```python + * # Python agents can call the manager via subprocess or HTTP + * # Option A: Use the Arcis MCP server + * # Option B: Direct contract calls with web3.py + * + * from web3 import Web3 + * + * w3 = Web3(Web3.HTTPProvider("https://mainnet.base.org")) + * + * VAULT = "0x00325d9da832b38179ed2f0dabd4062d93e325a7" + * USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" + * + * # Check balance + * balance = w3.eth.call({"to": VAULT, "data": "0xe3d670d7" + agent_addr[2:].zfill(64)}) + * position_usdc = int(balance.hex(), 16) / 1e6 + * + * # Deposit: approve + deposit + * # approve(vault, amount) + * # deposit(amount) + * ``` + */ + +/** + * ── Example 4: Virtuals Protocol Agent ── + * + * ```ts + * // Virtuals agents have their own wallet. Add idle capital management: + * import { createIdleCapitalManager } from "./arcis-x402-idle-capital"; + * + * const manager = createIdleCapitalManager({ + * privateKey: VIRTUALS_AGENT_KEY, + * depositThreshold: 500, // Higher threshold for revenue-generating agents + * reserveMinimum: 100, // Keep more in reserve for operations + * intervalMs: 300_000, // Check every 5 minutes + * }); + * ``` + */ + +/** + * ── Example 5: OpenAI Agents / Claude Agent SDK ── + * + * ```ts + * // These frameworks use tool/function calling. + * // Register Arcis as a tool: + * + * const arcisTools = [ + * { + * name: "arcis_deposit", + * description: "Deposit idle USDC into Arcis yield vault (~3.2% APY)", + * parameters: { amount: { type: "number", description: "USDC amount" } }, + * execute: async ({ amount }) => { + * const manager = createIdleCapitalManager({ + * privateKey: process.env.AGENT_KEY as `0x${string}`, + * autoMode: false, // Manual only + * }); + * const tx = await manager.deposit(BigInt(amount * 1e6)); + * return { success: true, tx }; + * }, + * }, + * { + * name: "arcis_balance", + * description: "Check vault position and wallet balance", + * parameters: {}, + * execute: async () => { + * const manager = createIdleCapitalManager({ + * privateKey: process.env.AGENT_KEY as `0x${string}`, + * autoMode: false, + * }); + * const pos = await manager.position(); + * return { + * vault_value: `$${Number(pos.value) / 1e6}`, + * wallet_usdc: `$${Number(pos.walletUsdc) / 1e6}`, + * shares: Number(pos.shares), + * }; + * }, + * }, + * ]; + * ``` + */ + +/** + * ── Example 6: x402 Payment Flow ── + * + * ```ts + * // Agent receives x402 micropayments for services. + * // Idle capital auto-deposits into Arcis between payments. + * + * import { createIdleCapitalManager } from "./arcis-x402-idle-capital"; + * + * // Start the idle capital manager + * const manager = createIdleCapitalManager({ + * privateKey: process.env.AGENT_KEY as `0x${string}`, + * rpcUrl: process.env.BASE_RPC_URL, + * depositThreshold: 50, // Deposit when earnings exceed $50 + * reserveMinimum: 10, // Keep $10 for gas + * withdrawTrigger: 2, // Withdraw when gas gets low + * withdrawAmount: 20, // Withdraw $20 at a time + * intervalMs: 120_000, // Check every 2 minutes + * }); + * + * // Your x402 payment handler + * async function handlePayment(payment: { amount: bigint; from: string }) { + * console.log(`Received ${payment.amount} USDC from ${payment.from}`); + * // Manager auto-detects the new balance on next tick + * // and deposits excess into vault + * } + * + * // When agent needs to make a payment + * async function makePayment(to: string, amount: bigint) { + * const pos = await manager.position(); + * if (pos.walletUsdc < amount) { + * // Not enough in wallet — withdraw from vault + * const needed = amount - pos.walletUsdc + 2_000_000n; // +$2 buffer + * await manager.withdrawUsdc(needed); + * } + * // Now send the payment via x402 + * // ... your x402 payment logic here + * } + * ``` + */ + +/** + * ── Example 7: MCP Integration (Claude, ChatGPT, Cursor) ── + * + * For MCP-native agents, connect directly to the Arcis MCP server: + * + * ```json + * { + * "mcpServers": { + * "arcis": { + * "command": "npx", + * "args": ["@arcisprotocol/mcp"] + * } + * } + * } + * ``` + * + * Or connect to the remote server: + * URL: https://mcp.arcis.money/mcp + * + * Available tools: + * - arcis_vault_status: TVL, rate, capacity + * - arcis_vault_balance: agent position + * - arcis_credit_status: lending pool + * - arcis_credit_tiers: ERC-8004 reputation tiers + * - arcis_credit_health: loan health check + * - arcis_contracts: all 7 contract addresses + */ + +// ═══════════════════════════════════════════════════════════════ +// EXPORTS +// ═══════════════════════════════════════════════════════════════ + +export { VAULT_ABI, USDC_ABI, base }; +export default createIdleCapitalManager; diff --git a/showcase/arcis-protocol/skills/arcis-idle-capital/examples/README.md b/showcase/arcis-protocol/skills/arcis-idle-capital/examples/README.md new file mode 100644 index 0000000..a01f0ec --- /dev/null +++ b/showcase/arcis-protocol/skills/arcis-idle-capital/examples/README.md @@ -0,0 +1,6 @@ +# Arcis Idle Capital — Example + +Demonstrates an agent depositing idle USDC into the Arcis vault and withdrawing when needed for a payment. + +- `prompt.md` — the instruction given to the agent +- `result-redacted.md` — the redacted transaction log diff --git a/showcase/arcis-protocol/skills/arcis-idle-capital/examples/prompt.md b/showcase/arcis-protocol/skills/arcis-idle-capital/examples/prompt.md new file mode 100644 index 0000000..2628f22 --- /dev/null +++ b/showcase/arcis-protocol/skills/arcis-idle-capital/examples/prompt.md @@ -0,0 +1,8 @@ +# Prompt + +You are an agent with idle USDC in your wallet. Monitor your balance. When it +exceeds $100, deposit the excess (keeping $20 in reserve) into the Arcis vault +to earn yield. When your balance drops below $5, withdraw $25 from the vault. + +Vault: 0x00325d9da832b38179ed2f0dabd4062d93e325a7 +USDC: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 diff --git a/showcase/arcis-protocol/skills/arcis-idle-capital/examples/result-redacted.md b/showcase/arcis-protocol/skills/arcis-idle-capital/examples/result-redacted.md new file mode 100644 index 0000000..86f9b62 --- /dev/null +++ b/showcase/arcis-protocol/skills/arcis-idle-capital/examples/result-redacted.md @@ -0,0 +1,27 @@ +# Result (Redacted) + +Agent wallet: 0xbae3...6944 (partially redacted) +Network: Base mainnet (8453) + +## Execution Log + +1. Read wallet USDC balance: $101.50 +2. Balance exceeds $100 deposit threshold → excess above $1.50 reserve = $100.00 +3. Checked vault paused(): false → proceed +4. previewDeposit(100000000) → 100 shares expected +5. Approved vault USDC allowance — tx confirmed on Basescan +6. deposit(100000000) — tx confirmed on Basescan +7. Received 100 raUSDC shares (matches preview) + +## Verifiable On-Chain State (live) + +- Vault position: `balance(0xbae3...6944)` → 100000000 ($100.00) +- Vault totalAssets: $120.00 (includes other depositors) +- Reserve/Deployed split: $44.40 / $75.60 (70% in Aave V3) +- Verify: https://basescan.org/address/0x00325d9da832b38179ed2f0dabd4062d93e325a7 + +## Redaction Notes + +- Private key: never logged +- Full wallet address: redacted to first/last 4 bytes +- Transaction hashes: omitted here; verifiable via the vault's Basescan event log diff --git a/showcase/arrowlend/README.md b/showcase/arrowlend/README.md new file mode 100644 index 0000000..f821b73 --- /dev/null +++ b/showcase/arrowlend/README.md @@ -0,0 +1,59 @@ +# ArrowLend — Liquidity for the Agent Economy + +On-chain credit infrastructure for the agent economy, live on **Robinhood Chain** +mainnet. Agents supply idle **USDG** into a single-asset lending pool, receive +**aUSDG** (an interest-bearing ERC-20 share that grows every block from borrower +interest), and withdraw on demand. + +- **App:** https://arrowlend.app +- **X:** https://x.com/arrowlend +- **Chain:** Robinhood Chain mainnet (chainId 4663) + +## What's in this package + +| Path | What it is | +| --- | --- | +| `showcase.json` | Showcase manifest | +| `skills/arrowlend-idle-usdg/` | Reusable idle-USDG treasury skill (`SKILL.md` + reference impl) | +| `skills/arrowlend-idle-usdg/examples/` | Prompt + redacted result receipt | +| `soul.md` | Public, redacted agent operational identity | +| `assets/` | Poster + teaser video | + +## The idle-USDG workflow + +Any agent with an Agent Wallet can: + +1. Watch its USDG balance. +2. Supply the excess above a reserve threshold into the ArrowLend pool → earn + yield sourced from real borrow demand (not emissions). +3. Withdraw automatically when it needs liquidity for payments. + +Interest accrues via a kinked rate model: as pool utilization rises, so does the +supply rate. aUSDG value climbs each block; share count stays fixed. + +## Live contracts (Robinhood Chain mainnet) + +| Contract | Address | +| --- | --- | +| ArrowLend Pool (aUSDG) | [`0x562ac0…7dA6`](https://robinhoodchain.blockscout.com/address/0x562ac0d6d140b6e285ACbe2ad642C8c32E1D7dA6) | +| USDG (6 decimals) | [`0x5fc536…d168`](https://robinhoodchain.blockscout.com/address/0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168) | + +## Oracle + +Prices come from a push oracle that reads Robinhood Chain's native rate data, +wrapped in a router with staleness and deviation guards. Decentralized feeds +(Chainlink is already live on Robinhood Chain for equities and majors) are on the +roadmap as collateral markets expand. + +## Scope of this showcase + +This package covers the **supply / earn** side, which is what is live today. The +borrow side (posting agent tokens as collateral to draw USDG) exists in the pool +contract and is featured in the teaser, but is not part of this reusable skill. + +## Install the skill + +```bash +cp -R showcase/arrowlend/skills/arrowlend-idle-usdg ~/.agents/skills/ +cp -R showcase/arrowlend/skills/arrowlend-idle-usdg ~/.claude/skills/ +``` diff --git a/showcase/arrowlend/assets/arrowlend-real-tx.mp4 b/showcase/arrowlend/assets/arrowlend-real-tx.mp4 new file mode 100644 index 0000000..8012660 Binary files /dev/null and b/showcase/arrowlend/assets/arrowlend-real-tx.mp4 differ diff --git a/showcase/arrowlend/assets/arrowlend-teaser.mp4 b/showcase/arrowlend/assets/arrowlend-teaser.mp4 new file mode 100644 index 0000000..ae430d3 Binary files /dev/null and b/showcase/arrowlend/assets/arrowlend-teaser.mp4 differ diff --git a/showcase/arrowlend/assets/poster.png b/showcase/arrowlend/assets/poster.png new file mode 100644 index 0000000..9ca43db Binary files /dev/null and b/showcase/arrowlend/assets/poster.png differ diff --git a/showcase/arrowlend/showcase.json b/showcase/arrowlend/showcase.json new file mode 100644 index 0000000..180905f --- /dev/null +++ b/showcase/arrowlend/showcase.json @@ -0,0 +1,100 @@ +{ + "slug": "arrowlend", + "title": "ArrowLend — Liquidity for the Agent Economy", + "tagline": "Agents supply idle USDG into an on-chain lending pool, earn yield from borrower interest, and withdraw on demand. Live on Robinhood Chain.", + "description": "ArrowLend is on-chain credit infrastructure for the agent economy, live on Robinhood Chain mainnet. Agents supply idle USDG into a single-asset lending pool and receive aUSDG, an interest-bearing ERC-20 share whose value grows every block as borrowers pay interest — the ERC-4626-style supply/withdraw interface any autonomous agent can drive with one wallet. Interest is real yield sourced from on-chain borrow demand, not emissions: a kinked interest-rate model raises the rate as utilization rises, and suppliers can withdraw available liquidity at any time. The pool is backed by a push price oracle that reads Robinhood Chain's native rate data behind a router with staleness and deviation guards, with decentralized feeds (Chainlink is already live on Robinhood Chain for equities and majors) on the roadmap as collateral markets expand. This showcase packages the idle-USDG workflow as a reusable skill: any agent with an Agent Wallet watches its USDG balance, supplies the excess above a reserve threshold to earn yield, and withdraws automatically when it needs liquidity for payments. Every deposit, accrual, and withdrawal is verifiable on the Robinhood Chain block explorer.", + "status": "live on mainnet", + "topic": "defi", + "topics": [ + "defi", + "lending", + "yield", + "agent-treasury", + "robinhood-chain" + ], + "hidden": false, + "builder": { + "name": "ArrowLend", + "url": "https://arrowlend.app" + }, + "links": { + "repo": "https://github.com/Arrowlend/arrowlend", + "demo": "https://arrowlend.app", + "share": "https://x.com/arrowlend", + "video": "https://x.com/arrowlend/status/2076399044532797762", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20ArrowLend" + }, + "primitives": [ + "wallet", + "token", + "acp" + ], + "visual": { + "kind": "ERC-4626-style lending pool + agent wallet", + "eyebrow": "defi · lending · robinhood chain", + "title": "liquidity for the agent economy", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/arrowlend/assets/poster.png", + "videoUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/arrowlend/assets/arrowlend-teaser.mp4", + "videoLabel": "Watch the 0:18 demo on X" + }, + "skills": [ + { + "name": "arrowlend-idle-usdg", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/arrowlend/skills/arrowlend-idle-usdg", + "sourcePath": "showcase/arrowlend/skills/arrowlend-idle-usdg", + "summary": "Reusable idle-capital workflow: monitor an agent's USDG balance, auto-supply excess above a reserve into the ArrowLend pool to earn yield, and auto-withdraw when the agent needs funds for payments.", + "install": "cp -R showcase/arrowlend/skills/arrowlend-idle-usdg ~/.agents/skills/\ncp -R showcase/arrowlend/skills/arrowlend-idle-usdg ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Live app", + "href": "https://arrowlend.app", + "kind": "demo" + }, + { + "label": "Real on-chain supply transaction (screen demo)", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/arrowlend/assets/arrowlend-real-tx.mp4", + "kind": "video" + }, + { + "label": "ArrowLend pool contract on Robinhood Chain explorer", + "href": "https://robinhoodchain.blockscout.com/address/0x562ac0d6d140b6e285ACbe2ad642C8c32E1D7dA6", + "kind": "proof" + }, + { + "label": "USDG token on Robinhood Chain explorer", + "href": "https://robinhoodchain.blockscout.com/address/0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168", + "kind": "proof" + }, + { + "label": "ArrowLend on X", + "href": "https://x.com/arrowlend", + "kind": "demo" + }, + { + "label": "Teaser / demo video on X", + "href": "https://x.com/arrowlend/status/2076399044532797762", + "kind": "video" + }, + { + "label": "Idle-USDG skill source", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/arrowlend/skills/arrowlend-idle-usdg", + "kind": "skill" + }, + { + "label": "Project package README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/arrowlend/README.md", + "kind": "docs" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/arrowlend/soul.md", + "summary": "Agent operational identity for the idle-USDG treasury loop: reserve-first boundaries, approval gates, and stop conditions." + }, + "feedbackPrompts": [ + "Should the idle-USDG reserve threshold adapt to each agent's payment cadence?", + "Which collateral markets should ArrowLend open first for agents that want to borrow, not just supply?", + "What would make the idle-USDG supply skill easiest for Virtuals agents to reuse?" + ] +} diff --git a/showcase/arrowlend/skills/arrowlend-idle-usdg/SKILL.md b/showcase/arrowlend/skills/arrowlend-idle-usdg/SKILL.md new file mode 100644 index 0000000..22d448a --- /dev/null +++ b/showcase/arrowlend/skills/arrowlend-idle-usdg/SKILL.md @@ -0,0 +1,80 @@ +# ArrowLend Idle USDG + +Route an agent's idle USDG into the ArrowLend lending pool on Robinhood Chain, earn yield from borrower interest via the aUSDG share token, and withdraw automatically when the agent needs funds for payments. + +## When to Use + +- An agent holds USDG that sits idle between jobs or payments. +- The agent wants to earn real, borrow-sourced yield on that idle balance without manual intervention. +- The agent operates on Robinhood Chain and has an Agent Wallet. + +## When Not to Use + +- The agent needs 100% of its balance liquid at all times (no idle capital to spare). +- The agent operates on a chain other than Robinhood Chain. +- The deposit amount is smaller than the gas cost of the transaction. +- The agent wants to borrow against collateral — this skill only covers the supply/earn side, which is what is live today. + +## Required Inputs + +- Agent wallet signer (via Agent Wallet). +- Robinhood Chain RPC URL (`https://rpc.mainnet.chain.robinhood.com`). +- Threshold config: `depositThreshold`, `reserveMinimum`, `withdrawTrigger` (all in USDG, 6 decimals). + +## Preconditions + +- Agent wallet funded with USDG on Robinhood Chain. +- Small native-gas balance for transaction fees. + +## Workflow + +1. Read the agent's USDG balance via `balanceOf(agent)` on the USDG token. +2. Read the agent's pool position: `convertToAssets(balanceOf(agent))` on the ArrowLend pool (aUSDG shares -> USDG value). +3. If wallet USDG exceeds `depositThreshold`: + a. Approve the pool to spend USDG (`approve(pool, amount)`). + b. Supply the excess above `reserveMinimum` (`supply(assets, agent)`), which mints aUSDG to the agent. +4. If wallet USDG drops below `withdrawTrigger` and the agent holds aUSDG: + a. Withdraw the configured amount (`withdraw(assets, agent)`), which burns aUSDG and returns USDG. +5. Repeat on an interval (default: 60 seconds). + +## Approval Gates + +- USDG approval transaction (one-time or per-deposit). +- Supply transaction. +- Withdraw transaction. + +## Stop Conditions + +- Pool is paused (check `paused()` before supplying). +- Insufficient gas balance. +- Deposit amount below economic threshold. +- Requested withdrawal exceeds pool available liquidity (`totalAssets() - totalDebt()`); retry with a smaller amount or wait. + +## Evidence and Redaction Rules + +- Never log or commit the agent private key or signer material. +- Redact wallet addresses in public reports if the agent requires privacy. +- Transaction hashes are public and safe to share. + +## Validation Checklist + +- [ ] Pool is not paused. +- [ ] Deposit amount is above gas cost. +- [ ] Reserve minimum is maintained in the wallet. +- [ ] Withdrawal returns USDG to the agent wallet. + +## Output Contract + +Returns transaction hashes for each supply/withdraw, plus current position: +`{ shares: bigint, value: bigint, walletUsdg: bigint }` (all USDG amounts in 6 decimals). + +## Contracts (Robinhood Chain Mainnet, chainId 4663) + +- ArrowLend Pool (aUSDG): `0x562ac0d6d140b6e285ACbe2ad642C8c32E1D7dA6` +- USDG (6 decimals): `0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168` + +## Links + +- App: https://arrowlend.app +- Pool on explorer: https://robinhoodchain.blockscout.com/address/0x562ac0d6d140b6e285ACbe2ad642C8c32E1D7dA6 +- Reference implementation: `arrowlend-idle-usdg.ts` in this folder diff --git a/showcase/arrowlend/skills/arrowlend-idle-usdg/arrowlend-idle-usdg.ts b/showcase/arrowlend/skills/arrowlend-idle-usdg/arrowlend-idle-usdg.ts new file mode 100644 index 0000000..439d11d --- /dev/null +++ b/showcase/arrowlend/skills/arrowlend-idle-usdg/arrowlend-idle-usdg.ts @@ -0,0 +1,149 @@ +/** + * ArrowLend Idle USDG — reusable agent treasury loop. + * + * Watches an agent's USDG balance on Robinhood Chain, supplies the excess above + * a reserve into the ArrowLend pool to earn borrow-sourced yield (aUSDG), and + * withdraws automatically when the agent needs liquidity for payments. + * + * Supply/earn is the side that is live today. No private keys are hardcoded — + * pass a viem WalletClient created from the Agent Wallet signer. + */ + +import { + createPublicClient, + http, + getContract, + type Address, + type WalletClient, + type PublicClient, +} from "viem"; + +// --- Robinhood Chain mainnet --- +export const ROBINHOOD_CHAIN = { + id: 4663, + name: "Robinhood Chain", + nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 }, + rpcUrls: { default: { http: ["https://rpc.mainnet.chain.robinhood.com"] } }, + blockExplorers: { + default: { name: "Blockscout", url: "https://robinhoodchain.blockscout.com" }, + }, +} as const; + +export const POOL: Address = "0x562ac0d6d140b6e285ACbe2ad642C8c32E1D7dA6"; +export const USDG: Address = "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168"; + +const ERC20_ABI = [ + { type: "function", name: "balanceOf", stateMutability: "view", inputs: [{ name: "a", type: "address" }], outputs: [{ type: "uint256" }] }, + { type: "function", name: "allowance", stateMutability: "view", inputs: [{ name: "o", type: "address" }, { name: "s", type: "address" }], outputs: [{ type: "uint256" }] }, + { type: "function", name: "approve", stateMutability: "nonpayable", inputs: [{ name: "s", type: "address" }, { name: "v", type: "uint256" }], outputs: [{ type: "bool" }] }, +] as const; + +const POOL_ABI = [ + { type: "function", name: "supply", stateMutability: "nonpayable", inputs: [{ name: "assets", type: "uint256" }, { name: "receiver", type: "address" }], outputs: [{ name: "shares", type: "uint256" }] }, + { type: "function", name: "withdraw", stateMutability: "nonpayable", inputs: [{ name: "assets", type: "uint256" }, { name: "receiver", type: "address" }], outputs: [{ name: "shares", type: "uint256" }] }, + { type: "function", name: "balanceOf", stateMutability: "view", inputs: [{ name: "a", type: "address" }], outputs: [{ type: "uint256" }] }, + { type: "function", name: "convertToAssets", stateMutability: "view", inputs: [{ name: "shares", type: "uint256" }], outputs: [{ type: "uint256" }] }, + { type: "function", name: "totalAssets", stateMutability: "view", inputs: [], outputs: [{ type: "uint256" }] }, + { type: "function", name: "totalDebt", stateMutability: "view", inputs: [], outputs: [{ type: "uint256" }] }, + { type: "function", name: "paused", stateMutability: "view", inputs: [], outputs: [{ type: "bool" }] }, +] as const; + +export interface IdleUsdgConfig { + /** Supply once wallet USDG rises above this (6 decimals). */ + depositThreshold: bigint; + /** Always keep at least this much USDG liquid in the wallet (6 decimals). */ + reserveMinimum: bigint; + /** Withdraw once wallet USDG falls below this (6 decimals). */ + withdrawTrigger: bigint; +} + +export interface IdleUsdgResult { + action: "supplied" | "withdrew" | "hold"; + txHash?: `0x${string}`; + position: { shares: bigint; value: bigint; walletUsdg: bigint }; +} + +function publicClient(): PublicClient { + return createPublicClient({ chain: ROBINHOOD_CHAIN as any, transport: http() }); +} + +/** Read the agent's wallet USDG and pool position. */ +export async function readPosition(agent: Address) { + const pc = publicClient(); + const usdg = getContract({ address: USDG, abi: ERC20_ABI, client: pc }); + const pool = getContract({ address: POOL, abi: POOL_ABI, client: pc }); + + const [walletUsdg, shares] = await Promise.all([ + usdg.read.balanceOf([agent]), + pool.read.balanceOf([agent]), + ]); + const value = shares > 0n ? await pool.read.convertToAssets([shares]) : 0n; + return { shares, value, walletUsdg }; +} + +/** + * Run one tick of the idle-USDG loop. Caller supplies a viem WalletClient whose + * account is the Agent Wallet. Returns the action taken plus the fresh position. + */ +export async function runOnce( + wallet: WalletClient, + cfg: IdleUsdgConfig, +): Promise { + const agent = wallet.account?.address as Address; + if (!agent) throw new Error("wallet.account is required"); + + const pc = publicClient(); + const pool = getContract({ address: POOL, abi: POOL_ABI, client: pc }); + + if (await pool.read.paused()) { + return { action: "hold", position: await readPosition(agent) }; + } + + const pos = await readPosition(agent); + + // Supply excess above the reserve. + if (pos.walletUsdg > cfg.depositThreshold) { + const amount = pos.walletUsdg - cfg.reserveMinimum; + if (amount > 0n) { + const allowance = (await pc.readContract({ + address: USDG, abi: ERC20_ABI, functionName: "allowance", args: [agent, POOL], + })) as bigint; + if (allowance < amount) { + const approveHash = await wallet.writeContract({ + address: USDG, abi: ERC20_ABI, functionName: "approve", args: [POOL, amount], + account: wallet.account!, chain: ROBINHOOD_CHAIN as any, + }); + await pc.waitForTransactionReceipt({ hash: approveHash }); + } + const txHash = await wallet.writeContract({ + address: POOL, abi: POOL_ABI, functionName: "supply", args: [amount, agent], + account: wallet.account!, chain: ROBINHOOD_CHAIN as any, + }); + await pc.waitForTransactionReceipt({ hash: txHash }); + return { action: "supplied", txHash, position: await readPosition(agent) }; + } + } + + // Withdraw to refill the wallet when it runs low. + if (pos.walletUsdg < cfg.withdrawTrigger && pos.shares > 0n) { + const need = cfg.withdrawTrigger - pos.walletUsdg; + const [totalAssets, totalDebt] = await Promise.all([ + pool.read.totalAssets(), + pool.read.totalDebt(), + ]); + const available = totalAssets - totalDebt; + const amount = need < pos.value ? need : pos.value; + if (amount > available) { + // Not enough liquidity right now; hold and retry next tick. + return { action: "hold", position: pos }; + } + const txHash = await wallet.writeContract({ + address: POOL, abi: POOL_ABI, functionName: "withdraw", args: [amount, agent], + account: wallet.account!, chain: ROBINHOOD_CHAIN as any, + }); + await pc.waitForTransactionReceipt({ hash: txHash }); + return { action: "withdrew", txHash, position: await readPosition(agent) }; + } + + return { action: "hold", position: pos }; +} diff --git a/showcase/arrowlend/skills/arrowlend-idle-usdg/examples/prompt.md b/showcase/arrowlend/skills/arrowlend-idle-usdg/examples/prompt.md new file mode 100644 index 0000000..be16d64 --- /dev/null +++ b/showcase/arrowlend/skills/arrowlend-idle-usdg/examples/prompt.md @@ -0,0 +1,16 @@ +# Example Prompt — Idle USDG Treasury Loop + +> You are an autonomous agent on Robinhood Chain with an Agent Wallet holding USDG. +> Use the `arrowlend-idle-usdg` skill to put idle USDG to work. +> +> Config: +> - depositThreshold: 100 USDG +> - reserveMinimum: 25 USDG +> - withdrawTrigger: 20 USDG +> +> Every 60 seconds: +> 1. Check my wallet USDG balance and my ArrowLend position. +> 2. If I'm holding more than the deposit threshold, supply the excess above my reserve to earn yield. +> 3. If my liquid balance drops below the withdraw trigger, pull just enough back from the pool to cover it. +> 4. Never touch my reserve minimum. Stop if the pool is paused or gas is too low. +> 5. Report the action taken and the transaction hash. diff --git a/showcase/arrowlend/skills/arrowlend-idle-usdg/examples/result-redacted.md b/showcase/arrowlend/skills/arrowlend-idle-usdg/examples/result-redacted.md new file mode 100644 index 0000000..b3c32f9 --- /dev/null +++ b/showcase/arrowlend/skills/arrowlend-idle-usdg/examples/result-redacted.md @@ -0,0 +1,40 @@ +# Example Result — Idle USDG Treasury Loop (redacted) + +Agent wallet address redacted as `0xAGENT…`. Transaction hashes are public on +the Robinhood Chain explorer. + +## Tick 1 — supply excess + +``` +position (before): { shares: 0, value: 0.00 USDG, walletUsdg: 140.00 USDG } +walletUsdg 140.00 > depositThreshold 100.00 → supply excess above reserve 25.00 +approve(pool, 115.00 USDG) → tx 0xAPPROVE… (confirmed) +supply(115.00 USDG, 0xAGENT…) → tx 0xSUPPLY… (confirmed) +action: supplied +position (after): { shares: 115.00, value: 115.00 USDG, walletUsdg: 25.00 USDG } +``` + +## Tick N — accrual (no action) + +``` +position: { shares: 115.00, value: 115.42 USDG, walletUsdg: 25.00 USDG } +walletUsdg 25.00 within band → hold +``` + +*aUSDG value drifts up from 115.00 → 115.42 USDG as borrowers pay interest; +share count is unchanged.* + +## Tick M — refill on payment need + +``` +position (before): { shares: 115.00, value: 115.42 USDG, walletUsdg: 18.00 USDG } +walletUsdg 18.00 < withdrawTrigger 20.00 → withdraw need 2.00 USDG +available liquidity check: totalAssets − totalDebt ≥ 2.00 → ok +withdraw(2.00 USDG, 0xAGENT…) → tx 0xWITHDRAW… (confirmed) +action: withdrew +position (after): { shares: 113.02, value: 113.42 USDG, walletUsdg: 20.00 USDG } +``` + +The agent never dips below its 25 USDG reserve on the supply side, earns yield +on idle capital between payments, and self-heals liquidity when it needs to +spend — no human in the loop. diff --git a/showcase/arrowlend/soul.md b/showcase/arrowlend/soul.md new file mode 100644 index 0000000..4364d5d --- /dev/null +++ b/showcase/arrowlend/soul.md @@ -0,0 +1,38 @@ +# ArrowLend Idle-USDG Agent — Soul + +Public, redacted operational identity for an agent running the `arrowlend-idle-usdg` +treasury loop. Contains no credentials, private keys, wallet material, or private +instructions. + +## Identity + +A treasury agent that keeps its owner's USDG productive on Robinhood Chain: idle +capital earns yield in the ArrowLend pool, and liquidity is always available for +payments. + +## Boundaries + +- **Reserve first.** Never supply below the configured `reserveMinimum`. The + wallet must always hold enough USDG to cover near-term payments. +- **Supply side only.** This agent supplies and withdraws USDG. It does not + borrow, post collateral, or take leverage. +- **Available liquidity respected.** Never attempt a withdrawal larger than the + pool's available liquidity (`totalAssets − totalDebt`); wait and retry. + +## Approval Gates + +Each of the following is an explicit on-chain action the agent must be allowed to +sign: USDG `approve`, pool `supply`, pool `withdraw`. + +## Stop Conditions + +- Pool is paused. +- Native gas balance too low to transact. +- Deposit amount below the economic gas threshold. +- Repeated withdrawal failures due to insufficient pool liquidity → escalate to + the operator. + +## Escalation + +On any unexpected revert, oracle-staleness signal, or pool pause, the agent stops +acting and surfaces the state to its operator rather than retrying blindly. diff --git a/showcase/athena-signal-commerce/README.md b/showcase/athena-signal-commerce/README.md new file mode 100644 index 0000000..6a038ce --- /dev/null +++ b/showcase/athena-signal-commerce/README.md @@ -0,0 +1,134 @@ +# Athena — Signal Commerce + +Athena is a tokenized Virtuals agent ($ATHENA on Base) that produces proprietary +crypto-market signals — Hyperliquid smart-money positioning, the Athena's Wisdom +cross-sectional ranking, and liquidation-gravity structure — and **sells them to +other agents over two payment rails from a single signal engine**: + +1. **ACP jobs** — escrowed, per-job purchases on the Agent Commerce Protocol. +2. **x402** — per-call micropayments (or a zero-value holder proof) on her MCP server. + +Both rails serve the **same signals** from the same gated workers behind +`api.0xathena.ai`, wrapped in the **same signed deliverable envelope**. + +## Rail 1 — ACP Provider + +Athena runs a live ACP **Provider** poller (Vercel cron, ~60s). It does not pick +jobs off a board — it publishes offerings and waits to be hired, then: + +1. **Hydrate** open jobs and read the requirement. +2. **Price** from a fixed catalog via `setBudget`. +3. **Fetch** the deliverable from the gated signal worker. +4. **Submit** a signed JSON envelope (`session.submit`). +5. **Settle** — escrow releases to Athena's wallet on client approval. + +### Offerings + +| Offering | Price | Delivers | +|----------|-------|----------| +| `Athena_Wisdom_Rankings` | $1 | The live Athena's Wisdom ranking — full scored, ranked universe | +| `HL_Smart_Money` | $5 | Elite smart-money wallet cohort + a positioning headline, plus the cohort positioning grid | +| `Liquidation_Gravity` | $1 | The full liquidation-gravity signal set — near pull, deep overhang, deep skew, alignment | + +### Deliverable contract + +Every deliverable — on **both** rails — is a JSON document with a stable envelope: + +```json +{ + "signal": "liquidation-gravity", + "source": "Athena AI (api.0xathena.ai)", + "delivered_at": "2026-07-03T11:48:05.506Z", + "disclaimer": "Informational only — not financial advice.", + "data": { "...": "live signal payload" } +} +``` + +### Proof — a completed on-chain job + +`examples/acp-job-65178-receipt.md` is the receipt of a **real, completed** +buy: an external buyer agent created, funded ($1 USDC escrow), and approved a +`Liquidation_Gravity` job; Athena's poller priced and delivered it autonomously. + +``` +11:46:09 job.created buyer 0x591cd330… → provider 0x308d7492… +11:46:17 budget.set amount = 1 (Athena's poller) +11:47:59 job.funded amount = 1 (buyer escrow) +11:48:11 job.submitted deliverableHash 0x3c2b4b5f…528d669 +11:49:01 job.completed escrow released → Athena +``` + +The delivered payload (48 assets) is committed at +`examples/acp-deliverable-65178.json`. Live provider status: +`https://athena-acp.vercel.app/api/health` → `configured:true` + the three offerings. + +## Rail 2 — x402 MCP Gateway + +The same signals are exposed as **ten read-only MCP tools** at +`https://api.0xathena.ai/mcp` (streamable HTTP, JSON-RPC). Discovery +(`tools/list`, `/mcp/info`, `/mcp/health`) is open; calling a tool is gated by +**x402** two ways: + +| Path | Endpoint | Cost | +|------|----------|------| +| **Holder proof** | `POST /mcp` | Free — sign a **zero-value** USDC EIP-3009 authorization proving the wallet holds ≥ 1,000,000 $ATHENA. Nothing settles on-chain; no gas. | +| **Pay-per-call** | `POST /mcp/paid` | **1 USDC per call** on Base via the Coinbase CDP facilitator (x402 v1 + v2). | + +The tools: `get_wisdom_ranking`, `get_wisdom_skew_leaderboard`, +`get_liquidation_gravity`, `get_funding_map`, `get_max_pain`, +`get_smart_money_signals`, `get_elite_wallets`, `get_elite_portfolio`, +`get_vol_screener`, `get_implied_vol`. + +### Proof — the live catalog + +`examples/x402-mcp-gateway.md` captures the public `/mcp/info` catalog and +`/mcp/health` showing `paid_path: true`. Inspect it live at +`https://api.0xathena.ai/mcp/info`. + +## Architecture + +``` + ┌───────────────────────────────┐ + │ Athena signal workers │ + │ (Hyperliquid · Coinglass · │ + │ Deribit · options IV) │ + │ api.0xathena.ai/* (gated) │ + └───────────────┬───────────────┘ + │ one deliverable envelope + ┌───────────────────────┴───────────────────────┐ + ▼ ▼ + ┌──────────────────┐ ┌──────────────────┐ + │ Rail 1 — ACP │ │ Rail 2 — x402 │ + │ Provider poller │ │ MCP gateway │ + │ │ │ │ + │ hydrate → price │ │ tools/list (open)│ + │ → fetch → submit │ │ tools/call: │ + │ → settle (USDC │ │ • holder proof │ + │ escrow) │ │ (0-value) │ + │ │ │ • 1 USDC/call │ + └────────┬─────────┘ └────────┬─────────┘ + │ escrow release │ USDC settle + ▼ ▼ + Athena wallet 0x308d7492… buyer agents / AI clients +``` + +## Guardrails + +- **Read-only.** Every offering and MCP tool returns signal data; nothing Athena + sells can move a buyer's funds or place a trade. +- **Honest framing.** Every deliverable carries `Informational only — not + financial advice`; descriptive signals (e.g. liquidation structure) are never + dressed up as directional alpha. +- **No secret sauce.** Buyers receive signal *outputs*; the model weights, + ranking constants, and gate internals stay private. +- **Server-side gate.** Both rails enforce access server-side (ACP escrow / x402 + verification); gated responses are never edge-cached. + +## Build info + +- **Chain:** Base (8453) +- **Token:** $ATHENA (`0x1a43287cBfCc5f35082e6E2Aa98e5B474FE7Bd4e`) +- **ACP provider wallet:** `0x308d7492ed5a7f06ea5181c801e8d71928eb2e5d` +- **ACP SDK:** `@virtuals-protocol/acp-node-v2` (provider poller on Vercel) +- **MCP:** streamable-HTTP JSON-RPC worker, x402 v1 + v2, Coinbase CDP facilitator +- **Docs:** https://0xathena.ai/docs/acp · https://0xathena.ai/docs/mcp diff --git a/showcase/athena-signal-commerce/examples/acp-deliverable-65178.json b/showcase/athena-signal-commerce/examples/acp-deliverable-65178.json new file mode 100644 index 0000000..57c12fe --- /dev/null +++ b/showcase/athena-signal-commerce/examples/acp-deliverable-65178.json @@ -0,0 +1,5167 @@ +{ + "signal": "liquidation-gravity", + "source": "Athena AI (api.0xathena.ai)", + "delivered_at": "2026-07-03T11:48:05.506Z", + "disclaimer": "Informational only — not financial advice.", + "data": { + "generated_at": "2026-07-03T11:30:10Z", + "interval": "12h/24h/3d", + "deep_interval": "30d>7d", + "count": 48, + "available": 48, + "signals": [ + { + "token": "XPL", + "spot": 0.10286, + "gravity": -0.8836, + "label": "STRONG DOWN PULL", + "side": "SHORT", + "conviction": 1, + "target_price": 0.101828, + "target_distance_pct": -1, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.869938504124874, + "per_tf": { + "12h": -0.8123, + "24h": -0.8977, + "3d": -0.9511 + }, + "z_cross": 4.486703617462366, + "up_mass": 375925.9242605283, + "down_mass": 6837111.156336563, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.07982552066354424, + "distance_pct": -22.41, + "side": "below", + "mass_usd": 276959468, + "width_pct": 2.62, + "buckets": 9, + "dominance_ratio": 6.18 + }, + "top_pockets": [ + { + "price": 0.07982552066354424, + "distance_pct": -22.41, + "side": "below", + "mass_usd": 276959468, + "width_pct": 2.62, + "buckets": 9 + }, + { + "price": 0.07409490892908263, + "distance_pct": -27.98, + "side": "below", + "mass_usd": 275028423, + "width_pct": 2.62, + "buckets": 9 + }, + { + "price": 0.0881189589118954, + "distance_pct": -14.35, + "side": "below", + "mass_usd": 194441054, + "width_pct": 5.24, + "buckets": 17 + }, + { + "price": 0.09309975730654209, + "distance_pct": -9.51, + "side": "below", + "mass_usd": 136632353, + "width_pct": 2.62, + "buckets": 9 + }, + { + "price": 0.0772201258166407, + "distance_pct": -24.94, + "side": "below", + "mass_usd": 108507811, + "width_pct": 0.66, + "buckets": 3 + }, + { + "price": 0.08250257059056067, + "distance_pct": -19.81, + "side": "below", + "mass_usd": 94711438, + "width_pct": 1.64, + "buckets": 6 + } + ], + "reach_pct": 43.14, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": -0.8096, + "up_mass": 168882673, + "down_mass": 1605258362, + "overhang_ratio": 9.51, + "side": "below", + "center_price": 0.07894842861889503, + "center_distance_pct": -23.26 + }, + "alignment": { + "near_side": "SHORT", + "overhang_side": "below", + "deep_side": "below", + "state": "aligned_down", + "note": "Near pull DOWN confirmed by a deep book 9.51x heavier below, led by a pocket at -22.41%." + }, + "notes": [] + }, + { + "token": "TIA", + "spot": 0.3933, + "gravity": -0.8171, + "label": "STRONG DOWN PULL", + "side": "SHORT", + "conviction": 1, + "target_price": 0.3875, + "target_distance_pct": -1.47, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.7175591742904547, + "per_tf": { + "12h": -0.4788, + "24h": -0.8927, + "3d": -0.9484 + }, + "z_cross": 4.061821245119208, + "up_mass": 1447867.462760345, + "down_mass": 20497066.082524918, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.36382642958285605, + "distance_pct": -7.49, + "side": "below", + "mass_usd": 268212740, + "width_pct": 9.73, + "buckets": 45, + "dominance_ratio": 3.56 + }, + "top_pockets": [ + { + "price": 0.36382642958285605, + "distance_pct": -7.49, + "side": "below", + "mass_usd": 268212740, + "width_pct": 9.73, + "buckets": 45 + }, + { + "price": 0.31708968725272973, + "distance_pct": -19.38, + "side": "below", + "mass_usd": 143156146, + "width_pct": 3.54, + "buckets": 17 + }, + { + "price": 0.41596622265696664, + "distance_pct": 5.76, + "side": "above", + "mass_usd": 138434594, + "width_pct": 3.1, + "buckets": 11 + }, + { + "price": 0.33072920378780374, + "distance_pct": -15.91, + "side": "below", + "mass_usd": 133511057, + "width_pct": 3.76, + "buckets": 18 + }, + { + "price": 0.40577068607220734, + "distance_pct": 3.17, + "side": "above", + "mass_usd": 72084438, + "width_pct": 1.99, + "buckets": 10 + }, + { + "price": 0.298397014581682, + "distance_pct": -24.13, + "side": "below", + "mass_usd": 57237257, + "width_pct": 1.55, + "buckets": 8 + } + ], + "reach_pct": 30.8, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": -0.5536, + "up_mass": 215430604, + "down_mass": 749678287, + "overhang_ratio": 3.48, + "side": "below", + "center_price": 0.33215397924865536, + "center_distance_pct": -15.55 + }, + "alignment": { + "near_side": "SHORT", + "overhang_side": "below", + "deep_side": "below", + "state": "aligned_down", + "note": "Near pull DOWN confirmed by a deep book 3.48x heavier below, led by a pocket at -7.49%." + }, + "notes": [] + }, + { + "token": "HYPE", + "spot": 69.072, + "gravity": -0.8081, + "label": "STRONG DOWN PULL", + "side": "SHORT", + "conviction": 1, + "target_price": 68.1265, + "target_distance_pct": -1.37, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.7010148369505141, + "per_tf": { + "12h": -0.477, + "24h": -0.8612, + "3d": -0.9248 + }, + "z_cross": 4.973946971540247, + "up_mass": 73503451.46314746, + "down_mass": 778631358.6081793, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 64.857225168951, + "distance_pct": -6.09, + "side": "below", + "mass_usd": 8859737953, + "width_pct": 8.36, + "buckets": 41, + "dominance_ratio": 3.31 + }, + "top_pockets": [ + { + "price": 64.857225168951, + "distance_pct": -6.09, + "side": "below", + "mass_usd": 8859737953, + "width_pct": 8.36, + "buckets": 41 + }, + { + "price": 59.330988287681095, + "distance_pct": -14.09, + "side": "below", + "mass_usd": 5881623065, + "width_pct": 5.44, + "buckets": 27 + }, + { + "price": 71.53692417782753, + "distance_pct": 3.58, + "side": "above", + "mass_usd": 5246303147, + "width_pct": 2.72, + "buckets": 14 + }, + { + "price": 77.93690054807728, + "distance_pct": 12.85, + "side": "above", + "mass_usd": 4583340153, + "width_pct": 2.93, + "buckets": 12 + }, + { + "price": 73.32051396051821, + "distance_pct": 6.16, + "side": "above", + "mass_usd": 3758556250, + "width_pct": 1.88, + "buckets": 10 + }, + { + "price": 56.52283568361693, + "distance_pct": -18.16, + "side": "below", + "mass_usd": 2150841496, + "width_pct": 1.25, + "buckets": 7 + } + ], + "reach_pct": 26.15, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": -0.1539, + "up_mass": 17578492957, + "down_mass": 23973940482, + "overhang_ratio": 1.36, + "side": "below", + "center_price": 59.58990697542111, + "center_distance_pct": -13.72 + }, + "alignment": { + "near_side": "SHORT", + "overhang_side": "below", + "deep_side": "below", + "state": "aligned_down", + "note": "Near pull DOWN confirmed by a deep book 1.36x heavier below, led by a pocket at -6.09%." + }, + "notes": [] + }, + { + "token": "ZEC", + "spot": 460.26, + "gravity": -0.7197, + "label": "STRONG DOWN PULL", + "side": "SHORT", + "conviction": 1, + "target_price": 450.49, + "target_distance_pct": -2.12, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.5669101125062079, + "per_tf": { + "12h": -0.6545, + "24h": -0.8025, + "3d": 0.0424 + }, + "z_cross": 3.5219973213887434, + "up_mass": 27431140.4957169, + "down_mass": 84395595.01828235, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 409.2286583782444, + "distance_pct": -11.09, + "side": "below", + "mass_usd": 10625468240, + "width_pct": 15.86, + "buckets": 36, + "dominance_ratio": 6.18 + }, + "top_pockets": [ + { + "price": 409.2286583782444, + "distance_pct": -11.09, + "side": "below", + "mass_usd": 10625468240, + "width_pct": 15.86, + "buckets": 36 + }, + { + "price": 644.551045512376, + "distance_pct": 40.04, + "side": "above", + "mass_usd": 4150756061, + "width_pct": 2.72, + "buckets": 7 + }, + { + "price": 483.6028265257548, + "distance_pct": 5.07, + "side": "above", + "mass_usd": 3421237499, + "width_pct": 4.98, + "buckets": 12 + }, + { + "price": 556.5764284199323, + "distance_pct": 20.93, + "side": "above", + "mass_usd": 2187234811, + "width_pct": 0.91, + "buckets": 3 + }, + { + "price": 521.9037859912605, + "distance_pct": 13.4, + "side": "above", + "mass_usd": 1991449073, + "width_pct": 2.27, + "buckets": 6 + }, + { + "price": 656.3518144041915, + "distance_pct": 42.61, + "side": "above", + "mass_usd": 1371171296, + "width_pct": 0.45, + "buckets": 2 + } + ], + "reach_pct": 43.6, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.2231, + "up_mass": 20805621569, + "down_mass": 13216400820, + "overhang_ratio": 1.57, + "side": "above", + "center_price": 570.5285198273381, + "center_distance_pct": 23.96 + }, + "alignment": { + "near_side": "SHORT", + "overhang_side": "below", + "deep_side": "above", + "state": "near_vs_deep_conflict", + "note": "Near pull DOWN into an opposing deep book 1.57x heavier ABOVE (though the biggest single pocket sits at -11.09%, the other way) — treat the near signal with caution." + }, + "notes": [ + "Near pull DOWN into an opposing deep book 1.57x heavier ABOVE (though the biggest single pocket sits at -11.09%, the other way) — treat the near signal with caution." + ] + }, + { + "token": "PENGU", + "spot": 0.006795, + "gravity": -0.7099, + "label": "STRONG DOWN PULL", + "side": "SHORT", + "conviction": 1, + "target_price": 0.0066091, + "target_distance_pct": -2.74, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.5544262142202578, + "per_tf": { + "12h": -0.4145, + "24h": -0.586, + "3d": -0.814 + }, + "z_cross": 5.3183366374976035, + "up_mass": 3484675.1028011325, + "down_mass": 14687155.764056254, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.0070691171724429624, + "distance_pct": 4.03, + "side": "above", + "mass_usd": 345395807, + "width_pct": 4.56, + "buckets": 26, + "dominance_ratio": 2.65 + }, + "top_pockets": [ + { + "price": 0.0070691171724429624, + "distance_pct": 4.03, + "side": "above", + "mass_usd": 345395807, + "width_pct": 4.56, + "buckets": 26 + }, + { + "price": 0.006453033741126981, + "distance_pct": -5.03, + "side": "below", + "mass_usd": 339833316, + "width_pct": 6.2, + "buckets": 35 + }, + { + "price": 0.007469766672614425, + "distance_pct": 9.93, + "side": "above", + "mass_usd": 271999655, + "width_pct": 4.74, + "buckets": 22 + }, + { + "price": 0.0061217860958765954, + "distance_pct": -9.91, + "side": "below", + "mass_usd": 102294747, + "width_pct": 0.73, + "buckets": 5 + }, + { + "price": 0.005976767112239722, + "distance_pct": -12.04, + "side": "below", + "mass_usd": 58887142, + "width_pct": 0.18, + "buckets": 2 + }, + { + "price": 0.0060318595991376775, + "distance_pct": -11.23, + "side": "below", + "mass_usd": 41301684, + "width_pct": 0.18, + "buckets": 2 + } + ], + "reach_pct": 19.64, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": -0.0136, + "up_mass": 652503521, + "down_mass": 670514185, + "overhang_ratio": 1.03, + "side": "balanced", + "center_price": null, + "center_distance_pct": null + }, + "alignment": { + "near_side": "SHORT", + "overhang_side": "above", + "deep_side": "balanced", + "state": "mixed", + "note": "Near and deep lenses not strongly aligned (near short, deep balanced)." + }, + "notes": [] + }, + { + "token": "TRX", + "spot": 0.32007, + "gravity": 0.6573, + "label": "STRONG UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 0.323295, + "target_distance_pct": 1.01, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.4925267875903598, + "per_tf": { + "12h": 0.4911, + "24h": 0.5298, + "3d": 0.4304 + }, + "z_cross": 3.7054342449242257, + "up_mass": 224492132.61377147, + "down_mass": 80001955.59579085, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.33735629367931214, + "distance_pct": 5.4, + "side": "above", + "mass_usd": 6236938747, + "width_pct": 6.58, + "buckets": 59, + "dominance_ratio": 5.43 + }, + "top_pockets": [ + { + "price": 0.33735629367931214, + "distance_pct": 5.4, + "side": "above", + "mass_usd": 6236938747, + "width_pct": 6.58, + "buckets": 59 + }, + { + "price": 0.30909625741628177, + "distance_pct": -3.43, + "side": "below", + "mass_usd": 1940388556, + "width_pct": 2.79, + "buckets": 26 + }, + { + "price": 0.30198826317018485, + "distance_pct": -5.65, + "side": "below", + "mass_usd": 354077980, + "width_pct": 0.67, + "buckets": 7 + }, + { + "price": 0.34889150039668304, + "distance_pct": 9, + "side": "above", + "mass_usd": 264536361, + "width_pct": 0.56, + "buckets": 4 + }, + { + "price": 0.3549053343164509, + "distance_pct": 10.88, + "side": "above", + "mass_usd": 146577456, + "width_pct": 0.45, + "buckets": 4 + } + ], + "reach_pct": 11.07, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.4808, + "up_mass": 6665883046, + "down_mass": 2337355627, + "overhang_ratio": 2.85, + "side": "above", + "center_price": 0.3382284539301963, + "center_distance_pct": 5.67 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 2.85x heavier above, led by a pocket at +5.4%." + }, + "notes": [] + }, + { + "token": "WLFI", + "spot": 0.05732, + "gravity": -0.6161, + "label": "STRONG DOWN PULL", + "side": "SHORT", + "conviction": 1, + "target_price": 0.05655, + "target_distance_pct": -1.34, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.4492187535170523, + "per_tf": { + "12h": -0.4006, + "24h": -0.3929, + "3d": -0.6572 + }, + "z_cross": 4.587708455739539, + "up_mass": 20872152.64399622, + "down_mass": 68219422.17877328, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.053302718074703985, + "distance_pct": -7.01, + "side": "below", + "mass_usd": 775612761, + "width_pct": 3.21, + "buckets": 20, + "dominance_ratio": 2.71 + }, + "top_pockets": [ + { + "price": 0.053302718074703985, + "distance_pct": -7.01, + "side": "below", + "mass_usd": 775612761, + "width_pct": 3.21, + "buckets": 20 + }, + { + "price": 0.05561873714714001, + "distance_pct": -2.97, + "side": "below", + "mass_usd": 429093564, + "width_pct": 2.23, + "buckets": 17 + }, + { + "price": 0.060261738513707634, + "distance_pct": 5.13, + "side": "above", + "mass_usd": 394595609, + "width_pct": 5.3, + "buckets": 39 + }, + { + "price": 0.06437695622197709, + "distance_pct": 12.31, + "side": "above", + "mass_usd": 368194113, + "width_pct": 3.49, + "buckets": 18 + }, + { + "price": 0.06229110853968694, + "distance_pct": 8.67, + "side": "above", + "mass_usd": 78335017, + "width_pct": 0.84, + "buckets": 7 + }, + { + "price": 0.06333302361733938, + "distance_pct": 10.49, + "side": "above", + "mass_usd": 32094601, + "width_pct": 0.14, + "buckets": 2 + } + ], + "reach_pct": 14.31, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": -0.1323, + "up_mass": 969110549, + "down_mass": 1264614572, + "overhang_ratio": 1.3, + "side": "below", + "center_price": 0.05414077418061572, + "center_distance_pct": -5.55 + }, + "alignment": { + "near_side": "SHORT", + "overhang_side": "below", + "deep_side": "below", + "state": "aligned_down", + "note": "Near pull DOWN confirmed by a deep book 1.3x heavier below, led by a pocket at -7.01%." + }, + "notes": [] + }, + { + "token": "XRP", + "spot": 1.1086, + "gravity": -0.6017, + "label": "STRONG DOWN PULL", + "side": "SHORT", + "conviction": 1, + "target_price": 1.08271, + "target_distance_pct": -2.34, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.4348650150161998, + "per_tf": { + "12h": -0.4722, + "24h": -0.276, + "3d": -0.6288 + }, + "z_cross": 5.514390553009309, + "up_mass": 275773439.46593326, + "down_mass": 706739472.3231511, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 1.2006895706072973, + "distance_pct": 8.29, + "side": "above", + "mass_usd": 31077118106, + "width_pct": 11.84, + "buckets": 69, + "dominance_ratio": 4.14 + }, + "top_pockets": [ + { + "price": 1.2006895706072973, + "distance_pct": 8.29, + "side": "above", + "mass_usd": 31077118106, + "width_pct": 11.84, + "buckets": 69 + }, + { + "price": 1.2917718965128755, + "distance_pct": 16.5, + "side": "above", + "mass_usd": 8332402824, + "width_pct": 4.53, + "buckets": 26 + }, + { + "price": 1.0792521779647755, + "distance_pct": -2.66, + "side": "below", + "mass_usd": 2862016076, + "width_pct": 0.87, + "buckets": 6 + }, + { + "price": 1.0467300963869897, + "distance_pct": -5.6, + "side": "below", + "mass_usd": 2723419999, + "width_pct": 0.7, + "buckets": 5 + }, + { + "price": 1.063051023469117, + "distance_pct": -4.13, + "side": "below", + "mass_usd": 2343433689, + "width_pct": 1.57, + "buckets": 10 + }, + { + "price": 1.019906507102877, + "distance_pct": -8.02, + "side": "below", + "mass_usd": 1247618017, + "width_pct": 0.87, + "buckets": 6 + } + ], + "reach_pct": 20.68, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.5443, + "up_mass": 40190580783, + "down_mass": 11860966048, + "overhang_ratio": 3.39, + "side": "above", + "center_price": 1.2220589600082234, + "center_distance_pct": 10.21 + }, + "alignment": { + "near_side": "SHORT", + "overhang_side": "above", + "deep_side": "above", + "state": "near_vs_deep_conflict", + "note": "Near pull DOWN into an opposing deep book 3.39x heavier ABOVE, led by a pocket at +8.29% — treat the near signal with caution." + }, + "notes": [ + "Near pull DOWN into an opposing deep book 3.39x heavier ABOVE, led by a pocket at +8.29% — treat the near signal with caution." + ] + }, + { + "token": "WIF", + "spot": 0.1764, + "gravity": 0.5648, + "label": "STRONG UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 0.17854, + "target_distance_pct": 1.21, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.3998730887116294, + "per_tf": { + "12h": 0.4283, + "24h": 0.5715, + "3d": 0.0355 + }, + "z_cross": 4.68936891360751, + "up_mass": 18121386.41604756, + "down_mass": 9922559.220801037, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.163975765836366, + "distance_pct": -7.04, + "side": "below", + "mass_usd": 225135397, + "width_pct": 7.34, + "buckets": 38, + "dominance_ratio": 3.66 + }, + "top_pockets": [ + { + "price": 0.163975765836366, + "distance_pct": -7.04, + "side": "below", + "mass_usd": 225135397, + "width_pct": 7.34, + "buckets": 38 + }, + { + "price": 0.15343242841834856, + "distance_pct": -13.02, + "side": "below", + "mass_usd": 150458903, + "width_pct": 4.17, + "buckets": 22 + }, + { + "price": 0.19773791501675766, + "distance_pct": 12.1, + "side": "above", + "mass_usd": 71867272, + "width_pct": 1.98, + "buckets": 8 + }, + { + "price": 0.18497159016248427, + "distance_pct": 4.86, + "side": "above", + "mass_usd": 62446508, + "width_pct": 0.6, + "buckets": 4 + }, + { + "price": 0.18173963420499184, + "distance_pct": 3.03, + "side": "above", + "mass_usd": 59713825, + "width_pct": 0.4, + "buckets": 3 + }, + { + "price": 0.14410581490437258, + "distance_pct": -18.31, + "side": "below", + "mass_usd": 29684208, + "width_pct": 0.79, + "buckets": 5 + } + ], + "reach_pct": 24.43, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": -0.2913, + "up_mass": 296304897, + "down_mass": 539929687, + "overhang_ratio": 1.82, + "side": "below", + "center_price": 0.15639445864731072, + "center_distance_pct": -11.34 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "below", + "deep_side": "below", + "state": "near_vs_deep_conflict", + "note": "Near pull UP into an opposing deep book 1.82x heavier BELOW, led by a pocket at -7.04% — treat the near signal with caution." + }, + "notes": [ + "Near pull UP into an opposing deep book 1.82x heavier BELOW, led by a pocket at -7.04% — treat the near signal with caution." + ] + }, + { + "token": "VVV", + "spot": 13.892, + "gravity": 0.5579, + "label": "STRONG UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 14.0548, + "target_distance_pct": 1.17, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.3936202219883789, + "per_tf": { + "12h": 0.338, + "24h": 0.6098, + "3d": 0.1404 + }, + "z_cross": 5.197278304275923, + "up_mass": 15270935.458971165, + "down_mass": 7440206.9998698905, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 17.95682758623593, + "distance_pct": 29.26, + "side": "above", + "mass_usd": 941218501, + "width_pct": 11.36, + "buckets": 31, + "dominance_ratio": 8.47 + }, + "top_pockets": [ + { + "price": 17.95682758623593, + "distance_pct": 29.26, + "side": "above", + "mass_usd": 941218501, + "width_pct": 11.36, + "buckets": 31 + }, + { + "price": 15.64116926285365, + "distance_pct": 12.59, + "side": "above", + "mass_usd": 741459163, + "width_pct": 20.45, + "buckets": 55 + }, + { + "price": 21.892477310879666, + "distance_pct": 57.59, + "side": "above", + "mass_usd": 287925637, + "width_pct": 2.27, + "buckets": 7 + }, + { + "price": 19.310582942004423, + "distance_pct": 39.01, + "side": "above", + "mass_usd": 231017853, + "width_pct": 3.03, + "buckets": 9 + }, + { + "price": 12.59115338873106, + "distance_pct": -9.36, + "side": "below", + "mass_usd": 221623387, + "width_pct": 5.3, + "buckets": 15 + }, + { + "price": 22.26053861015942, + "distance_pct": 60.24, + "side": "above", + "mass_usd": 179493390, + "width_pct": 1.51, + "buckets": 5 + } + ], + "reach_pct": 71.55, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.7361, + "up_mass": 2975640871, + "down_mass": 452225885, + "overhang_ratio": 6.58, + "side": "above", + "center_price": 18.6371331167577, + "center_distance_pct": 34.16 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 6.58x heavier above, led by a pocket at +29.26%." + }, + "notes": [] + }, + { + "token": "ICP", + "spot": 2.232, + "gravity": 0.5375, + "label": "STRONG UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 2.2626, + "target_distance_pct": 1.37, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.3754134679344391, + "per_tf": { + "12h": 0.4802, + "24h": 0.379, + "3d": 0.1333 + }, + "z_cross": 4.148475473411923, + "up_mass": 25709925.946971126, + "down_mass": 14464222.390962437, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 2.348387596329027, + "distance_pct": 5.21, + "side": "above", + "mass_usd": 384629847, + "width_pct": 6.9, + "buckets": 24, + "dominance_ratio": 3.59 + }, + "top_pockets": [ + { + "price": 2.348387596329027, + "distance_pct": 5.21, + "side": "above", + "mass_usd": 384629847, + "width_pct": 6.9, + "buckets": 24 + }, + { + "price": 2.508521277219344, + "distance_pct": 12.39, + "side": "above", + "mass_usd": 312093409, + "width_pct": 5.1, + "buckets": 18 + }, + { + "price": 2.631901992571954, + "distance_pct": 17.92, + "side": "above", + "mass_usd": 155283714, + "width_pct": 3, + "buckets": 11 + }, + { + "price": 2.154039000331761, + "distance_pct": -3.49, + "side": "below", + "mass_usd": 148992485, + "width_pct": 3, + "buckets": 11 + }, + { + "price": 2.9696, + "distance_pct": 33.05, + "side": "above", + "mass_usd": 63918234, + "width_pct": 0, + "buckets": 1 + }, + { + "price": 3.2463858170752906, + "distance_pct": 45.45, + "side": "above", + "mass_usd": 59895684, + "width_pct": 0.3, + "buckets": 2 + } + ], + "reach_pct": 48.06, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.6949, + "up_mass": 1351161919, + "down_mass": 243227754, + "overhang_ratio": 5.56, + "side": "above", + "center_price": 2.672426398357242, + "center_distance_pct": 19.73 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 5.56x heavier above, led by a pocket at +5.21%." + }, + "notes": [] + }, + { + "token": "AAVE", + "spot": 86.85, + "gravity": 0.5211, + "label": "STRONG UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 88.57, + "target_distance_pct": 1.98, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.3611931249354201, + "per_tf": { + "12h": 0.4407, + "24h": 0.2004, + "3d": 0.4636 + }, + "z_cross": 4.0820237680989955, + "up_mass": 187131833.0977111, + "down_mass": 84077262.76300544, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 68.57286032913655, + "distance_pct": -21.04, + "side": "below", + "mass_usd": 2020212031, + "width_pct": 15.56, + "buckets": 60, + "dominance_ratio": 12.63 + }, + "top_pockets": [ + { + "price": 68.57286032913655, + "distance_pct": -21.04, + "side": "below", + "mass_usd": 2020212031, + "width_pct": 15.56, + "buckets": 60 + }, + { + "price": 78.00512383532933, + "distance_pct": -10.17, + "side": "below", + "mass_usd": 489645473, + "width_pct": 3.43, + "buckets": 14 + }, + { + "price": 59.15529820533169, + "distance_pct": -31.88, + "side": "below", + "mass_usd": 247206481, + "width_pct": 2.64, + "buckets": 11 + }, + { + "price": 99.77912059392554, + "distance_pct": 14.9, + "side": "above", + "mass_usd": 150294361, + "width_pct": 1.58, + "buckets": 6 + }, + { + "price": 80.2699465141601, + "distance_pct": -7.57, + "side": "below", + "mass_usd": 114652964, + "width_pct": 1.05, + "buckets": 5 + }, + { + "price": 97.61019772201654, + "distance_pct": 12.4, + "side": "above", + "mass_usd": 109753026, + "width_pct": 1.32, + "buckets": 6 + } + ], + "reach_pct": 33.35, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": -0.6799, + "up_mass": 603659494, + "down_mass": 3167848109, + "overhang_ratio": 5.25, + "side": "below", + "center_price": 70.59359424034216, + "center_distance_pct": -18.71 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "below", + "deep_side": "below", + "state": "near_vs_deep_conflict", + "note": "Near pull UP into an opposing deep book 5.25x heavier BELOW, led by a pocket at -21.04% — treat the near signal with caution." + }, + "notes": [ + "Near pull UP into an opposing deep book 5.25x heavier BELOW, led by a pocket at -21.04% — treat the near signal with caution." + ] + }, + { + "token": "SUI", + "spot": 0.7501, + "gravity": 0.4949, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 0.75822, + "target_distance_pct": 1.08, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.33908946930079686, + "per_tf": { + "12h": 0.4201, + "24h": 0.4629, + "3d": -0.0599 + }, + "z_cross": 5.746582234894874, + "up_mass": 120905915.0958738, + "down_mass": 71831899.78828251, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.8447795248475015, + "distance_pct": 12.59, + "side": "above", + "mass_usd": 1769698681, + "width_pct": 4.11, + "buckets": 15, + "dominance_ratio": 2.03 + }, + "top_pockets": [ + { + "price": 0.8447795248475015, + "distance_pct": 12.59, + "side": "above", + "mass_usd": 1769698681, + "width_pct": 4.11, + "buckets": 15 + }, + { + "price": 0.7227053915883696, + "distance_pct": -3.68, + "side": "below", + "mass_usd": 1260762771, + "width_pct": 3.93, + "buckets": 23 + }, + { + "price": 0.7770125630315237, + "distance_pct": 3.56, + "side": "above", + "mass_usd": 1134470648, + "width_pct": 2.68, + "buckets": 16 + }, + { + "price": 0.793104511089332, + "distance_pct": 5.7, + "side": "above", + "mass_usd": 440442082, + "width_pct": 1.25, + "buckets": 8 + }, + { + "price": 0.6996249428120351, + "distance_pct": -6.75, + "side": "below", + "mass_usd": 320715697, + "width_pct": 0.89, + "buckets": 6 + }, + { + "price": 0.6662705476927084, + "distance_pct": -11.2, + "side": "below", + "mass_usd": 317241246, + "width_pct": 1.25, + "buckets": 8 + } + ], + "reach_pct": 16.37, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.1895, + "up_mass": 4058218171, + "down_mass": 2765021793, + "overhang_ratio": 1.47, + "side": "above", + "center_price": 0.8191564240134781, + "center_distance_pct": 9.18 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 1.47x heavier above, led by a pocket at +12.59%." + }, + "notes": [] + }, + { + "token": "DASH", + "spot": 36.07, + "gravity": -0.4832, + "label": "MILD DOWN PULL", + "side": "SHORT", + "conviction": 1, + "target_price": 35.396, + "target_distance_pct": -1.87, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.3294684098666507, + "per_tf": { + "12h": 0.1388, + "24h": -0.687, + "3d": -0.7574 + }, + "z_cross": 3.206511378991238, + "up_mass": 4429590.05292316, + "down_mass": 16256670.272474864, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 40.835275380842965, + "distance_pct": 13.21, + "side": "above", + "mass_usd": 371042103, + "width_pct": 3.77, + "buckets": 18, + "dominance_ratio": 3.22 + }, + "top_pockets": [ + { + "price": 40.835275380842965, + "distance_pct": 13.21, + "side": "above", + "mass_usd": 371042103, + "width_pct": 3.77, + "buckets": 18 + }, + { + "price": 34.27510447838433, + "distance_pct": -4.98, + "side": "below", + "mass_usd": 247593644, + "width_pct": 5.09, + "buckets": 28 + }, + { + "price": 38.39484014686151, + "distance_pct": 6.45, + "side": "above", + "mass_usd": 124524926, + "width_pct": 1.7, + "buckets": 10 + }, + { + "price": 37.40235847537354, + "distance_pct": 3.69, + "side": "above", + "mass_usd": 110285266, + "width_pct": 2.64, + "buckets": 15 + }, + { + "price": 39.06482662523412, + "distance_pct": 8.3, + "side": "above", + "mass_usd": 109033763, + "width_pct": 1.51, + "buckets": 9 + }, + { + "price": 32.89285963469914, + "distance_pct": -8.81, + "side": "below", + "mass_usd": 58078629, + "width_pct": 1.32, + "buckets": 8 + } + ], + "reach_pct": 19.43, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.1902, + "up_mass": 779593373, + "down_mass": 530439300, + "overhang_ratio": 1.47, + "side": "above", + "center_price": 39.58763893543573, + "center_distance_pct": 9.75 + }, + "alignment": { + "near_side": "SHORT", + "overhang_side": "above", + "deep_side": "above", + "state": "near_vs_deep_conflict", + "note": "Near pull DOWN into an opposing deep book 1.47x heavier ABOVE, led by a pocket at +13.21% — treat the near signal with caution." + }, + "notes": [ + "Near pull DOWN into an opposing deep book 1.47x heavier ABOVE, led by a pocket at +13.21% — treat the near signal with caution." + ] + }, + { + "token": "SOL", + "spot": 81.38, + "gravity": -0.4781, + "label": "MILD DOWN PULL", + "side": "SHORT", + "conviction": 1, + "target_price": 79.706, + "target_distance_pct": -2.06, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.3253166958174212, + "per_tf": { + "12h": -0.195, + "24h": -0.4001, + "3d": -0.4876 + }, + "z_cross": 6.194977674945753, + "up_mass": 1995942376.3859515, + "down_mass": 4631035984.154836, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 67.73987977993812, + "distance_pct": -16.75, + "side": "below", + "mass_usd": 64974807072, + "width_pct": 12.39, + "buckets": 72, + "dominance_ratio": 10.46 + }, + "top_pockets": [ + { + "price": 67.73987977993812, + "distance_pct": -16.75, + "side": "below", + "mass_usd": 64974807072, + "width_pct": 12.39, + "buckets": 72 + }, + { + "price": 76.6899855884688, + "distance_pct": -5.75, + "side": "below", + "mass_usd": 48109373903, + "width_pct": 6.46, + "buckets": 38 + }, + { + "price": 62.23716406095027, + "distance_pct": -23.51, + "side": "below", + "mass_usd": 8590553168, + "width_pct": 1.57, + "buckets": 10 + }, + { + "price": 61.144887575911255, + "distance_pct": -24.86, + "side": "below", + "mass_usd": 8390909429, + "width_pct": 1.4, + "buckets": 7 + }, + { + "price": 59.61831314486729, + "distance_pct": -26.73, + "side": "below", + "mass_usd": 8017110920, + "width_pct": 2.27, + "buckets": 9 + }, + { + "price": 79.724, + "distance_pct": -2.02, + "side": "below", + "mass_usd": 1245054506, + "width_pct": 0, + "buckets": 1 + } + ], + "reach_pct": 28.37, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": -0.995, + "up_mass": 348161940, + "down_mass": 140076979933, + "overhang_ratio": 402.33, + "side": "below", + "center_price": 69.7437190702999, + "center_distance_pct": -14.29 + }, + "alignment": { + "near_side": "SHORT", + "overhang_side": "below", + "deep_side": "below", + "state": "aligned_down", + "note": "Near pull DOWN confirmed by a deep book 402.33x heavier below, led by a pocket at -16.75%." + }, + "notes": [] + }, + { + "token": "FIL", + "spot": 0.789, + "gravity": 0.4752, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 0.7992, + "target_distance_pct": 1.29, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.3229893828983191, + "per_tf": { + "12h": 0.4447, + "24h": 0.3321, + "3d": 0.0334 + }, + "z_cross": 3.2556009598765216, + "up_mass": 64877572.9140014, + "down_mass": 37799947.26200984, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.7461487003681176, + "distance_pct": -5.43, + "side": "below", + "mass_usd": 975180251, + "width_pct": 6.26, + "buckets": 27, + "dominance_ratio": 3.46 + }, + "top_pockets": [ + { + "price": 0.7461487003681176, + "distance_pct": -5.43, + "side": "below", + "mass_usd": 975180251, + "width_pct": 6.26, + "buckets": 27 + }, + { + "price": 0.8269209740321439, + "distance_pct": 4.81, + "side": "above", + "mass_usd": 864295905, + "width_pct": 4.82, + "buckets": 21 + }, + { + "price": 0.7003792740344793, + "distance_pct": -11.23, + "side": "below", + "mass_usd": 789135073, + "width_pct": 5.78, + "buckets": 24 + }, + { + "price": 1.0077849506780847, + "distance_pct": 27.73, + "side": "above", + "mass_usd": 148916618, + "width_pct": 0.24, + "buckets": 2 + }, + { + "price": 0.8491328109600903, + "distance_pct": 7.62, + "side": "above", + "mass_usd": 133664621, + "width_pct": 0.72, + "buckets": 4 + }, + { + "price": 0.891, + "distance_pct": 12.93, + "side": "above", + "mass_usd": 73171348, + "width_pct": 0, + "buckets": 1 + } + ], + "reach_pct": 30.27, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": -0.0755, + "up_mass": 1518027849, + "down_mass": 1765847040, + "overhang_ratio": 1.16, + "side": "below", + "center_price": 0.725669425917617, + "center_distance_pct": -8.03 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "below", + "deep_side": "below", + "state": "near_vs_deep_conflict", + "note": "Near pull UP into an opposing deep book 1.16x heavier BELOW, led by a pocket at -5.43% — treat the near signal with caution." + }, + "notes": [ + "Near pull UP into an opposing deep book 1.16x heavier BELOW, led by a pocket at -5.43% — treat the near signal with caution." + ] + }, + { + "token": "ETH", + "spot": 1742.57, + "gravity": -0.4373, + "label": "MILD DOWN PULL", + "side": "SHORT", + "conviction": 1, + "target_price": 1718.844, + "target_distance_pct": -1.36, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.2930396474068313, + "per_tf": { + "12h": -0.3428, + "24h": -0.2012, + "3d": -0.3418 + }, + "z_cross": 4.509362173987621, + "up_mass": 4229550633.182392, + "down_mass": 7155357119.398263, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 1634.3503516681599, + "distance_pct": -6.21, + "side": "below", + "mass_usd": 222371151133, + "width_pct": 7.72, + "buckets": 44, + "dominance_ratio": 2.56 + }, + "top_pockets": [ + { + "price": 1634.3503516681599, + "distance_pct": -6.21, + "side": "below", + "mass_usd": 222371151133, + "width_pct": 7.72, + "buckets": 44 + }, + { + "price": 1905.0261202520041, + "distance_pct": 9.32, + "side": "above", + "mass_usd": 139859245778, + "width_pct": 1.97, + "buckets": 11 + }, + { + "price": 1963.8193049866989, + "distance_pct": 12.7, + "side": "above", + "mass_usd": 125196522190, + "width_pct": 3.77, + "buckets": 15 + }, + { + "price": 1812.2555712063638, + "distance_pct": 4, + "side": "above", + "mass_usd": 57080177648, + "width_pct": 1.44, + "buckets": 9 + }, + { + "price": 1551.9838479870918, + "distance_pct": -10.94, + "side": "below", + "mass_usd": 49635192653, + "width_pct": 1.26, + "buckets": 8 + }, + { + "price": 1856.9152762789417, + "distance_pct": 6.56, + "side": "above", + "mass_usd": 37197306445, + "width_pct": 0.54, + "buckets": 4 + } + ], + "reach_pct": 17.98, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.1651, + "up_mass": 459022069960, + "down_mass": 328949692099, + "overhang_ratio": 1.4, + "side": "above", + "center_price": 1899.417287218023, + "center_distance_pct": 9 + }, + "alignment": { + "near_side": "SHORT", + "overhang_side": "below", + "deep_side": "above", + "state": "near_vs_deep_conflict", + "note": "Near pull DOWN into an opposing deep book 1.4x heavier ABOVE (though the biggest single pocket sits at -6.21%, the other way) — treat the near signal with caution." + }, + "notes": [ + "Near pull DOWN into an opposing deep book 1.4x heavier ABOVE (though the biggest single pocket sits at -6.21%, the other way) — treat the near signal with caution." + ] + }, + { + "token": "LDO", + "spot": 0.2643, + "gravity": 0.4315, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 0.267, + "target_distance_pct": 1.02, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.2885946050661685, + "per_tf": { + "12h": 0.3023, + "24h": 0.5694, + "3d": -0.2336 + }, + "z_cross": 3.830113773376682, + "up_mass": 13611247.645722391, + "down_mass": 8240472.25571063, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.31882832792796817, + "distance_pct": 20.63, + "side": "above", + "mass_usd": 155622898, + "width_pct": 3.45, + "buckets": 11, + "dominance_ratio": 2.54 + }, + "top_pockets": [ + { + "price": 0.31882832792796817, + "distance_pct": 20.63, + "side": "above", + "mass_usd": 155622898, + "width_pct": 3.45, + "buckets": 11 + }, + { + "price": 0.2518883947057193, + "distance_pct": -4.7, + "side": "below", + "mass_usd": 145105144, + "width_pct": 4.96, + "buckets": 24 + }, + { + "price": 0.28931434583599597, + "distance_pct": 9.46, + "side": "above", + "mass_usd": 111528173, + "width_pct": 2.8, + "buckets": 14 + }, + { + "price": 0.27451944855442717, + "distance_pct": 3.87, + "side": "above", + "mass_usd": 89659859, + "width_pct": 3.67, + "buckets": 18 + }, + { + "price": 0.3105508403244669, + "distance_pct": 17.5, + "side": "above", + "mass_usd": 84355589, + "width_pct": 1.08, + "buckets": 4 + }, + { + "price": 0.29635930505746805, + "distance_pct": 12.13, + "side": "above", + "mass_usd": 49429400, + "width_pct": 1.08, + "buckets": 6 + } + ], + "reach_pct": 26.77, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.4992, + "up_mass": 639866639, + "down_mass": 213758118, + "overhang_ratio": 2.99, + "side": "above", + "center_price": 0.3003945815166462, + "center_distance_pct": 13.66 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 2.99x heavier above, led by a pocket at +20.63%." + }, + "notes": [] + }, + { + "token": "DOGE", + "spot": 0.07573, + "gravity": 0.4301, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 0.076744, + "target_distance_pct": 1.34, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.28751311294950116, + "per_tf": { + "12h": 0.3201, + "24h": 0.4885, + "3d": -0.1375 + }, + "z_cross": 5.693555336246682, + "up_mass": 378649787.84461164, + "down_mass": 236173570.37519008, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.09183566608753753, + "distance_pct": 21.27, + "side": "above", + "mass_usd": 7698087491, + "width_pct": 4.96, + "buckets": 22, + "dominance_ratio": 12.19 + }, + "top_pockets": [ + { + "price": 0.09183566608753753, + "distance_pct": 21.27, + "side": "above", + "mass_usd": 7698087491, + "width_pct": 4.96, + "buckets": 22 + }, + { + "price": 0.08053246071263911, + "distance_pct": 6.34, + "side": "above", + "mass_usd": 4356713901, + "width_pct": 4.49, + "buckets": 20 + }, + { + "price": 0.08676905940771872, + "distance_pct": 14.58, + "side": "above", + "mass_usd": 3913384680, + "width_pct": 5.44, + "buckets": 24 + }, + { + "price": 0.09590812271153731, + "distance_pct": 26.64, + "side": "above", + "mass_usd": 3119825464, + "width_pct": 2.13, + "buckets": 8 + }, + { + "price": 0.09788842099879594, + "distance_pct": 29.26, + "side": "above", + "mass_usd": 2684129642, + "width_pct": 1.89, + "buckets": 7 + }, + { + "price": 0.09401644295117641, + "distance_pct": 24.15, + "side": "above", + "mass_usd": 1326197100, + "width_pct": 0.24, + "buckets": 2 + } + ], + "reach_pct": 35.61, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.931, + "up_mass": 28208444574, + "down_mass": 1008638901, + "overhang_ratio": 27.97, + "side": "above", + "center_price": 0.08986125084786645, + "center_distance_pct": 18.66 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 27.97x heavier above, led by a pocket at +21.27%." + }, + "notes": [] + }, + { + "token": "BTC", + "spot": 61972, + "gravity": -0.4195, + "label": "MILD DOWN PULL", + "side": "SHORT", + "conviction": 1, + "target_price": 60741.18, + "target_distance_pct": -1.99, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.2793968127065206, + "per_tf": { + "12h": -0.2789, + "24h": -0.1102, + "3d": -0.5767 + }, + "z_cross": 6.2973563697362405, + "up_mass": 20106217994.494366, + "down_mass": 36492805020.4768, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 69763.19208356715, + "distance_pct": 12.58, + "side": "above", + "mass_usd": 299023754213, + "width_pct": 2.92, + "buckets": 20, + "dominance_ratio": 1.61 + }, + "top_pockets": [ + { + "price": 69763.19208356715, + "distance_pct": 12.58, + "side": "above", + "mass_usd": 299023754213, + "width_pct": 2.92, + "buckets": 20 + }, + { + "price": 68357.9806835558, + "distance_pct": 10.31, + "side": "above", + "mass_usd": 173386274043, + "width_pct": 0.88, + "buckets": 7 + }, + { + "price": 67671.58267905086, + "distance_pct": 9.21, + "side": "above", + "mass_usd": 157669187038, + "width_pct": 0.73, + "buckets": 6 + }, + { + "price": 60390.76137362075, + "distance_pct": -2.54, + "side": "below", + "mass_usd": 89669628586, + "width_pct": 0.88, + "buckets": 7 + }, + { + "price": 65493.47070148277, + "distance_pct": 5.69, + "side": "above", + "mass_usd": 61002176544, + "width_pct": 1.02, + "buckets": 8 + }, + { + "price": 63473.8087319323, + "distance_pct": 2.43, + "side": "above", + "mass_usd": 55201894601, + "width_pct": 0.88, + "buckets": 7 + } + ], + "reach_pct": 16.47, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.5764, + "up_mass": 995230970223, + "down_mass": 267459561125, + "overhang_ratio": 3.72, + "side": "above", + "center_price": 67746.0742123045, + "center_distance_pct": 9.33 + }, + "alignment": { + "near_side": "SHORT", + "overhang_side": "above", + "deep_side": "above", + "state": "near_vs_deep_conflict", + "note": "Near pull DOWN into an opposing deep book 3.72x heavier ABOVE, led by a pocket at +12.58% — treat the near signal with caution." + }, + "notes": [ + "Near pull DOWN into an opposing deep book 3.72x heavier ABOVE, led by a pocket at +12.58% — treat the near signal with caution." + ] + }, + { + "token": "XLM", + "spot": 0.20078, + "gravity": 0.4114, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 0.203356, + "target_distance_pct": 1.28, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.2733104328520632, + "per_tf": { + "12h": 0.2459, + "24h": 0.2717, + "3d": 0.3379 + }, + "z_cross": 5.1554522189987475, + "up_mass": 145095398.5169722, + "down_mass": 78510774.24891491, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.18123573525496567, + "distance_pct": -9.74, + "side": "below", + "mass_usd": 857261005, + "width_pct": 3.39, + "buckets": 15, + "dominance_ratio": 1.94 + }, + "top_pockets": [ + { + "price": 0.18123573525496567, + "distance_pct": -9.74, + "side": "below", + "mass_usd": 857261005, + "width_pct": 3.39, + "buckets": 15 + }, + { + "price": 0.19129186913268068, + "distance_pct": -4.73, + "side": "below", + "mass_usd": 788589733, + "width_pct": 5.08, + "buckets": 22 + }, + { + "price": 0.25385253744765124, + "distance_pct": 26.43, + "side": "above", + "mass_usd": 649557006, + "width_pct": 3.15, + "buckets": 10 + }, + { + "price": 0.21986153796048818, + "distance_pct": 9.5, + "side": "above", + "mass_usd": 645376072, + "width_pct": 2.18, + "buckets": 10 + }, + { + "price": 0.21478953412214938, + "distance_pct": 6.97, + "side": "above", + "mass_usd": 324180070, + "width_pct": 1.21, + "buckets": 6 + }, + { + "price": 0.2106078552707775, + "distance_pct": 4.89, + "side": "above", + "mass_usd": 288580054, + "width_pct": 1.21, + "buckets": 6 + } + ], + "reach_pct": 28.36, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.2874, + "up_mass": 3561134010, + "down_mass": 1971168790, + "overhang_ratio": 1.81, + "side": "above", + "center_price": 0.22928395761122794, + "center_distance_pct": 14.19 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "below", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 1.81x heavier above (though the biggest single pocket sits at -9.74%, the other way)." + }, + "notes": [] + }, + { + "token": "APT", + "spot": 0.6223, + "gravity": 0.4035, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 0.62875, + "target_distance_pct": 1.04, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.2673944011899715, + "per_tf": { + "12h": 0.372, + "24h": 0.4497, + "3d": -0.287 + }, + "z_cross": 3.786417767129958, + "up_mass": 21949367.46990633, + "down_mass": 14604811.47668245, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.6953961555125752, + "distance_pct": 11.75, + "side": "above", + "mass_usd": 280376702, + "width_pct": 3.9, + "buckets": 13, + "dominance_ratio": 2.45 + }, + "top_pockets": [ + { + "price": 0.6953961555125752, + "distance_pct": 11.75, + "side": "above", + "mass_usd": 280376702, + "width_pct": 3.9, + "buckets": 13 + }, + { + "price": 0.65432788432127, + "distance_pct": 5.15, + "side": "above", + "mass_usd": 191424056, + "width_pct": 6.49, + "buckets": 21 + }, + { + "price": 0.7248814469547054, + "distance_pct": 16.48, + "side": "above", + "mass_usd": 150161344, + "width_pct": 3.57, + "buckets": 12 + }, + { + "price": 0.602876731383882, + "distance_pct": -3.12, + "side": "below", + "mass_usd": 95388811, + "width_pct": 2.6, + "buckets": 9 + }, + { + "price": 0.8896473472135956, + "distance_pct": 42.96, + "side": "above", + "mass_usd": 64861289, + "width_pct": 0.97, + "buckets": 3 + }, + { + "price": 0.81954, + "distance_pct": 31.7, + "side": "above", + "mass_usd": 63786227, + "width_pct": 0, + "buckets": 1 + } + ], + "reach_pct": 52.79, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.7928, + "up_mass": 1447932575, + "down_mass": 167310375, + "overhang_ratio": 8.65, + "side": "above", + "center_price": 0.7793278821835888, + "center_distance_pct": 25.23 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 8.65x heavier above, led by a pocket at +11.75%." + }, + "notes": [] + }, + { + "token": "AVAX", + "spot": 6.889, + "gravity": 0.3982, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 6.9638, + "target_distance_pct": 1.09, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.2634202145066936, + "per_tf": { + "12h": 0.4126, + "24h": 0.212, + "3d": 0.0177 + }, + "z_cross": 5.033662804914823, + "up_mass": 98236233.37538762, + "down_mass": 74115362.29679969, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 6.424955043878298, + "distance_pct": -6.75, + "side": "below", + "mass_usd": 1321020452, + "width_pct": 8.89, + "buckets": 36, + "dominance_ratio": 2.58 + }, + "top_pockets": [ + { + "price": 6.424955043878298, + "distance_pct": -6.75, + "side": "below", + "mass_usd": 1321020452, + "width_pct": 8.89, + "buckets": 36 + }, + { + "price": 8.556497235371976, + "distance_pct": 24.19, + "side": "above", + "mass_usd": 1252610008, + "width_pct": 5.08, + "buckets": 16 + }, + { + "price": 7.145776401358956, + "distance_pct": 3.71, + "side": "above", + "mass_usd": 1100441822, + "width_pct": 3.3, + "buckets": 14 + }, + { + "price": 7.946587161564981, + "distance_pct": 15.34, + "side": "above", + "mass_usd": 314718508, + "width_pct": 0.76, + "buckets": 4 + }, + { + "price": 5.614504950128582, + "distance_pct": -18.51, + "side": "below", + "mass_usd": 287867522, + "width_pct": 0.76, + "buckets": 4 + }, + { + "price": 7.716372492457164, + "distance_pct": 11.99, + "side": "above", + "mass_usd": 233294450, + "width_pct": 1.52, + "buckets": 6 + } + ], + "reach_pct": 27.84, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.2584, + "up_mass": 3897788204, + "down_mass": 2296838286, + "overhang_ratio": 1.7, + "side": "above", + "center_price": 7.874212908651461, + "center_distance_pct": 14.28 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "below", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 1.7x heavier above (though the biggest single pocket sits at -6.75%, the other way)." + }, + "notes": [] + }, + { + "token": "BNB", + "spot": 567.32, + "gravity": 0.3873, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 574.598, + "target_distance_pct": 1.28, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.2553918374750518, + "per_tf": { + "12h": 0.403, + "24h": 0.4095, + "3d": -0.3465 + }, + "z_cross": 4.510933429251203, + "up_mass": 474713515.55807865, + "down_mass": 499153936.4683318, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 612.6398293361358, + "distance_pct": 7.99, + "side": "above", + "mass_usd": 13989700376, + "width_pct": 9.79, + "buckets": 57, + "dominance_ratio": 3.34 + }, + "top_pockets": [ + { + "price": 612.6398293361358, + "distance_pct": 7.99, + "side": "above", + "mass_usd": 13989700376, + "width_pct": 9.79, + "buckets": 57 + }, + { + "price": 641.1222548990764, + "distance_pct": 13.01, + "side": "above", + "mass_usd": 2948538214, + "width_pct": 1.22, + "buckets": 8 + }, + { + "price": 658.9452234400617, + "distance_pct": 16.15, + "side": "above", + "mass_usd": 2315192276, + "width_pct": 1.92, + "buckets": 8 + }, + { + "price": 649.6051854912972, + "distance_pct": 14.5, + "side": "above", + "mass_usd": 1704067709, + "width_pct": 0.52, + "buckets": 4 + }, + { + "price": 675.9450218758759, + "distance_pct": 19.15, + "side": "above", + "mass_usd": 1185634750, + "width_pct": 1.05, + "buckets": 3 + }, + { + "price": 689.2469824970691, + "distance_pct": 21.49, + "side": "above", + "mass_usd": 1061952744, + "width_pct": 1.22, + "buckets": 4 + } + ], + "reach_pct": 23.98, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.7597, + "up_mass": 24167344907, + "down_mass": 3300355136, + "overhang_ratio": 7.32, + "side": "above", + "center_price": 631.5662420855512, + "center_distance_pct": 11.32 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 7.32x heavier above, led by a pocket at +7.99%." + }, + "notes": [] + }, + { + "token": "OP", + "spot": 0.103, + "gravity": -0.3829, + "label": "MILD DOWN PULL", + "side": "SHORT", + "conviction": 1, + "target_price": 0.10151, + "target_distance_pct": -1.45, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.2521583754720662, + "per_tf": { + "12h": 0.3542, + "24h": -0.6714, + "3d": -0.8827 + }, + "z_cross": 4.308516403293971, + "up_mass": 2646502.7778053745, + "down_mass": 12511256.92898706, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.11055636048311439, + "distance_pct": 7.34, + "side": "above", + "mass_usd": 492334909, + "width_pct": 10.85, + "buckets": 43, + "dominance_ratio": 4 + }, + "top_pockets": [ + { + "price": 0.11055636048311439, + "distance_pct": 7.34, + "side": "above", + "mass_usd": 492334909, + "width_pct": 10.85, + "buckets": 43 + }, + { + "price": 0.09163351752178649, + "distance_pct": -11.04, + "side": "below", + "mass_usd": 224121325, + "width_pct": 5.3, + "buckets": 21 + }, + { + "price": 0.09772312174206296, + "distance_pct": -5.12, + "side": "below", + "mass_usd": 203460493, + "width_pct": 5.81, + "buckets": 24 + }, + { + "price": 0.13214, + "distance_pct": 28.29, + "side": "above", + "mass_usd": 42196568, + "width_pct": 0, + "buckets": 1 + }, + { + "price": 0.13052751081545613, + "distance_pct": 26.73, + "side": "above", + "mass_usd": 33361316, + "width_pct": 0.25, + "buckets": 2 + }, + { + "price": 0.12642, + "distance_pct": 22.74, + "side": "above", + "mass_usd": 28488242, + "width_pct": 0, + "buckets": 1 + } + ], + "reach_pct": 30.56, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.2139, + "up_mass": 673525499, + "down_mass": 436166858, + "overhang_ratio": 1.54, + "side": "above", + "center_price": 0.11578606555219917, + "center_distance_pct": 12.41 + }, + "alignment": { + "near_side": "SHORT", + "overhang_side": "above", + "deep_side": "above", + "state": "near_vs_deep_conflict", + "note": "Near pull DOWN into an opposing deep book 1.54x heavier ABOVE, led by a pocket at +7.34% — treat the near signal with caution." + }, + "notes": [ + "Near pull DOWN into an opposing deep book 1.54x heavier ABOVE, led by a pocket at +7.34% — treat the near signal with caution." + ] + }, + { + "token": "DOT", + "spot": 0.867, + "gravity": 0.3807, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 0.8806, + "target_distance_pct": 1.57, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.2505543345986309, + "per_tf": { + "12h": 0.1967, + "24h": 0.5719, + "3d": -0.1907 + }, + "z_cross": 4.306941752281127, + "up_mass": 68500031.8473652, + "down_mass": 43699774.00824834, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.9916120943794711, + "distance_pct": 14.24, + "side": "above", + "mass_usd": 848045363, + "width_pct": 6.29, + "buckets": 27, + "dominance_ratio": 5.12 + }, + "top_pockets": [ + { + "price": 0.9916120943794711, + "distance_pct": 14.24, + "side": "above", + "mass_usd": 848045363, + "width_pct": 6.29, + "buckets": 27 + }, + { + "price": 0.9282192275720986, + "distance_pct": 6.94, + "side": "above", + "mass_usd": 705737117, + "width_pct": 5.56, + "buckets": 24 + }, + { + "price": 1.05123233907139, + "distance_pct": 21.11, + "side": "above", + "mass_usd": 497021360, + "width_pct": 4.6, + "buckets": 19 + }, + { + "price": 1.1425056555216337, + "distance_pct": 31.63, + "side": "above", + "mass_usd": 193927854, + "width_pct": 1.69, + "buckets": 6 + }, + { + "price": 1.0794867444595324, + "distance_pct": 24.36, + "side": "above", + "mass_usd": 181326262, + "width_pct": 1.21, + "buckets": 6 + }, + { + "price": 0.8954928780630799, + "distance_pct": 3.17, + "side": "above", + "mass_usd": 140383246, + "width_pct": 1.45, + "buckets": 7 + } + ], + "reach_pct": 36.52, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.8595, + "up_mass": 3152482555, + "down_mass": 238242454, + "overhang_ratio": 13.23, + "side": "above", + "center_price": 1.0184313765109887, + "center_distance_pct": 17.33 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 13.23x heavier above, led by a pocket at +14.24%." + }, + "notes": [] + }, + { + "token": "MON", + "spot": 0.02054, + "gravity": 0.3774, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 0.02081, + "target_distance_pct": 1.31, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.2481100812862428, + "per_tf": { + "12h": -0.0654, + "24h": 0.4693, + "3d": 0.5664 + }, + "z_cross": 3.9266871133951184, + "up_mass": 13747983.765021909, + "down_mass": 5010867.835257454, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.022203136917482852, + "distance_pct": 8.1, + "side": "above", + "mass_usd": 174183578, + "width_pct": 11.58, + "buckets": 62, + "dominance_ratio": 2.99 + }, + "top_pockets": [ + { + "price": 0.022203136917482852, + "distance_pct": 8.1, + "side": "above", + "mass_usd": 174183578, + "width_pct": 11.58, + "buckets": 62 + }, + { + "price": 0.019702953395837653, + "distance_pct": -4.08, + "side": "below", + "mass_usd": 60755618, + "width_pct": 4.94, + "buckets": 27 + }, + { + "price": 0.024061157033876335, + "distance_pct": 17.14, + "side": "above", + "mass_usd": 23329656, + "width_pct": 0.95, + "buckets": 6 + }, + { + "price": 0.02368583956284365, + "distance_pct": 15.32, + "side": "above", + "mass_usd": 18313256, + "width_pct": 1.33, + "buckets": 7 + }, + { + "price": 0.0181993983502135, + "distance_pct": -11.4, + "side": "below", + "mass_usd": 11985380, + "width_pct": 2.09, + "buckets": 12 + }, + { + "price": 0.01859743153528165, + "distance_pct": -9.46, + "side": "below", + "mass_usd": 7754845, + "width_pct": 1.33, + "buckets": 8 + } + ], + "reach_pct": 19.78, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.4188, + "up_mass": 220342926, + "down_mass": 90260835, + "overhang_ratio": 2.44, + "side": "above", + "center_price": 0.022567621618982948, + "center_distance_pct": 9.87 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 2.44x heavier above, led by a pocket at +8.1%." + }, + "notes": [] + }, + { + "token": "LINK", + "spot": 7.852, + "gravity": 0.3769, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 7.9425, + "target_distance_pct": 1.15, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.24776515405934227, + "per_tf": { + "12h": 0.2529, + "24h": 0.378, + "3d": 0.0084 + }, + "z_cross": 4.420646072473008, + "up_mass": 111115420.80456994, + "down_mass": 65831965.53643966, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 8.714194714958008, + "distance_pct": 10.98, + "side": "above", + "mass_usd": 1828440499, + "width_pct": 4.64, + "buckets": 22, + "dominance_ratio": 2.89 + }, + "top_pockets": [ + { + "price": 8.714194714958008, + "distance_pct": 10.98, + "side": "above", + "mass_usd": 1828440499, + "width_pct": 4.64, + "buckets": 22 + }, + { + "price": 8.210457729885688, + "distance_pct": 4.57, + "side": "above", + "mass_usd": 823708395, + "width_pct": 2.32, + "buckets": 15 + }, + { + "price": 7.629979810357081, + "distance_pct": -2.83, + "side": "below", + "mass_usd": 488415709, + "width_pct": 1.49, + "buckets": 10 + }, + { + "price": 8.493019785280497, + "distance_pct": 8.16, + "side": "above", + "mass_usd": 347353668, + "width_pct": 0.66, + "buckets": 5 + }, + { + "price": 9.004879832792597, + "distance_pct": 14.68, + "side": "above", + "mass_usd": 301941040, + "width_pct": 0.17, + "buckets": 2 + }, + { + "price": 7.388784342745102, + "distance_pct": -5.9, + "side": "below", + "mass_usd": 231201018, + "width_pct": 0.5, + "buckets": 4 + } + ], + "reach_pct": 17.09, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.3071, + "up_mass": 3987967148, + "down_mass": 2113947287, + "overhang_ratio": 1.89, + "side": "above", + "center_price": 8.565312616883556, + "center_distance_pct": 9.08 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 1.89x heavier above, led by a pocket at +10.98%." + }, + "notes": [] + }, + { + "token": "ARB", + "spot": 0.07934, + "gravity": 0.3726, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 0.080612, + "target_distance_pct": 1.6, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.24467785044924625, + "per_tf": { + "12h": 0.0524, + "24h": 0.4858, + "3d": 0.2553 + }, + "z_cross": 4.681864878407751, + "up_mass": 30428493.07070537, + "down_mass": 15508974.191866294, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.08933275769011441, + "distance_pct": 12.59, + "side": "above", + "mass_usd": 483204794, + "width_pct": 11.18, + "buckets": 50, + "dominance_ratio": 2.93 + }, + "top_pockets": [ + { + "price": 0.08933275769011441, + "distance_pct": 12.59, + "side": "above", + "mass_usd": 483204794, + "width_pct": 11.18, + "buckets": 50 + }, + { + "price": 0.0747469290732752, + "distance_pct": -5.79, + "side": "below", + "mass_usd": 161741662, + "width_pct": 4.39, + "buckets": 21 + }, + { + "price": 0.08227309574394363, + "distance_pct": 3.7, + "side": "above", + "mass_usd": 150485078, + "width_pct": 3.51, + "buckets": 17 + }, + { + "price": 0.095832, + "distance_pct": 20.79, + "side": "above", + "mass_usd": 44020639, + "width_pct": 0, + "buckets": 1 + }, + { + "price": 0.07731007538572622, + "distance_pct": -2.56, + "side": "below", + "mass_usd": 32751769, + "width_pct": 1.1, + "buckets": 6 + }, + { + "price": 0.07155550800734484, + "distance_pct": -9.81, + "side": "below", + "mass_usd": 24116319, + "width_pct": 0.88, + "buckets": 5 + } + ], + "reach_pct": 26.05, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.4899, + "up_mass": 787511624, + "down_mass": 269596302, + "overhang_ratio": 2.92, + "side": "above", + "center_price": 0.08916041087420769, + "center_distance_pct": 12.38 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 2.92x heavier above, led by a pocket at +12.59%." + }, + "notes": [] + }, + { + "token": "HBAR", + "spot": 0.07175, + "gravity": 0.3723, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 0.072682, + "target_distance_pct": 1.3, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.24445742861102107, + "per_tf": { + "12h": 0.3139, + "24h": 0.4429, + "3d": -0.2591 + }, + "z_cross": 4.887664161587408, + "up_mass": 34293490.67904137, + "down_mass": 28823040.942537054, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.09067247756582392, + "distance_pct": 26.37, + "side": "above", + "mass_usd": 1417900819, + "width_pct": 6.58, + "buckets": 28, + "dominance_ratio": 41.02 + }, + "top_pockets": [ + { + "price": 0.09067247756582392, + "distance_pct": 26.37, + "side": "above", + "mass_usd": 1417900819, + "width_pct": 6.58, + "buckets": 28 + }, + { + "price": 0.08241923142808903, + "distance_pct": 14.87, + "side": "above", + "mass_usd": 751103672, + "width_pct": 5.78, + "buckets": 30 + }, + { + "price": 0.07568748545252961, + "distance_pct": 5.49, + "side": "above", + "mass_usd": 597770543, + "width_pct": 4.38, + "buckets": 23 + }, + { + "price": 0.08785575296536378, + "distance_pct": 22.45, + "side": "above", + "mass_usd": 221928006, + "width_pct": 0.6, + "buckets": 4 + }, + { + "price": 0.08537152659898353, + "distance_pct": 18.98, + "side": "above", + "mass_usd": 212168644, + "width_pct": 1.2, + "buckets": 7 + }, + { + "price": 0.0885640783619639, + "distance_pct": 23.43, + "side": "above", + "mass_usd": 131760866, + "width_pct": 0.2, + "buckets": 2 + } + ], + "reach_pct": 30.56, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.9714, + "up_mass": 3755964372, + "down_mass": 54552326, + "overhang_ratio": 68.85, + "side": "above", + "center_price": 0.0850505070030992, + "center_distance_pct": 18.54 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 68.85x heavier above, led by a pocket at +26.37%." + }, + "notes": [] + }, + { + "token": "ENA", + "spot": 0.0776, + "gravity": 0.3631, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 0.078437, + "target_distance_pct": 1.08, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.23776522875098854, + "per_tf": { + "12h": 0.3587, + "24h": 0.2576, + "3d": -0.0689 + }, + "z_cross": 4.638661923360609, + "up_mass": 56955185.86048861, + "down_mass": 43457508.01908776, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.09877715325175664, + "distance_pct": 27.29, + "side": "above", + "mass_usd": 1083202917, + "width_pct": 7.63, + "buckets": 23, + "dominance_ratio": 5.54 + }, + "top_pockets": [ + { + "price": 0.09877715325175664, + "distance_pct": 27.29, + "side": "above", + "mass_usd": 1083202917, + "width_pct": 7.63, + "buckets": 23 + }, + { + "price": 0.08395930766974981, + "distance_pct": 8.19, + "side": "above", + "mass_usd": 732314533, + "width_pct": 7.28, + "buckets": 22 + }, + { + "price": 0.09233623359685318, + "distance_pct": 18.99, + "side": "above", + "mass_usd": 386458130, + "width_pct": 4.85, + "buckets": 15 + }, + { + "price": 0.0743549908950883, + "distance_pct": -4.18, + "side": "below", + "mass_usd": 349981494, + "width_pct": 3.12, + "buckets": 10 + }, + { + "price": 0.08845082197906004, + "distance_pct": 13.98, + "side": "above", + "mass_usd": 289154495, + "width_pct": 3.12, + "buckets": 10 + }, + { + "price": 0.10686799999999999, + "distance_pct": 37.72, + "side": "above", + "mass_usd": 98801514, + "width_pct": 0, + "buckets": 1 + } + ], + "reach_pct": 53.66, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.6599, + "up_mass": 2939429554, + "down_mass": 602163072, + "overhang_ratio": 4.88, + "side": "above", + "center_price": 0.09339877037566, + "center_distance_pct": 20.36 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 4.88x heavier above, led by a pocket at +27.29%." + }, + "notes": [] + }, + { + "token": "ADA", + "spot": 0.1687, + "gravity": -0.3355, + "label": "MILD DOWN PULL", + "side": "SHORT", + "conviction": 1, + "target_price": 0.167, + "target_distance_pct": -1.01, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.2181058074703195, + "per_tf": { + "12h": 0.3442, + "24h": -0.6788, + "3d": -0.6771 + }, + "z_cross": 4.3972679465231925, + "up_mass": 35004030.30514787, + "down_mass": 118087181.91013782, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.15467395833204933, + "distance_pct": -8.31, + "side": "below", + "mass_usd": 2580561496, + "width_pct": 6.26, + "buckets": 23, + "dominance_ratio": 2.84 + }, + "top_pockets": [ + { + "price": 0.15467395833204933, + "distance_pct": -8.31, + "side": "below", + "mass_usd": 2580561496, + "width_pct": 6.26, + "buckets": 23 + }, + { + "price": 0.1800890800646924, + "distance_pct": 6.75, + "side": "above", + "mass_usd": 2518308119, + "width_pct": 6.83, + "buckets": 25 + }, + { + "price": 0.22256774689347675, + "distance_pct": 31.93, + "side": "above", + "mass_usd": 1944761911, + "width_pct": 4.84, + "buckets": 14 + }, + { + "price": 0.1926995337640745, + "distance_pct": 14.23, + "side": "above", + "mass_usd": 1490532897, + "width_pct": 1.42, + "buckets": 6 + }, + { + "price": 0.1967322407963922, + "distance_pct": 16.62, + "side": "above", + "mass_usd": 755228700, + "width_pct": 1.42, + "buckets": 6 + }, + { + "price": 0.16384023219530586, + "distance_pct": -2.88, + "side": "below", + "mass_usd": 627181540, + "width_pct": 1.99, + "buckets": 8 + } + ], + "reach_pct": 37.44, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.3881, + "up_mass": 9039784431, + "down_mass": 3984486733, + "overhang_ratio": 2.27, + "side": "above", + "center_price": 0.1978514057263875, + "center_distance_pct": 17.28 + }, + "alignment": { + "near_side": "SHORT", + "overhang_side": "below", + "deep_side": "above", + "state": "near_vs_deep_conflict", + "note": "Near pull DOWN into an opposing deep book 2.27x heavier ABOVE (though the biggest single pocket sits at -8.31%, the other way) — treat the near signal with caution." + }, + "notes": [ + "Near pull DOWN into an opposing deep book 2.27x heavier ABOVE (though the biggest single pocket sits at -8.31%, the other way) — treat the near signal with caution." + ] + }, + { + "token": "ATOM", + "spot": 1.59, + "gravity": 0.3334, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 1.6104, + "target_distance_pct": 1.28, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.21668950282199245, + "per_tf": { + "12h": 0.2771, + "24h": 0.3202, + "3d": -0.1003 + }, + "z_cross": 7.06100657450723, + "up_mass": 18625508.854977127, + "down_mass": 14627109.850079434, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 1.8757442212184479, + "distance_pct": 17.97, + "side": "above", + "mass_usd": 396083372, + "width_pct": 10.35, + "buckets": 48, + "dominance_ratio": 4.34 + }, + "top_pockets": [ + { + "price": 1.8757442212184479, + "distance_pct": 17.97, + "side": "above", + "mass_usd": 396083372, + "width_pct": 10.35, + "buckets": 48 + }, + { + "price": 2.0527475230803924, + "distance_pct": 29.1, + "side": "above", + "mass_usd": 313784781, + "width_pct": 4.18, + "buckets": 20 + }, + { + "price": 1.7107825312452316, + "distance_pct": 7.6, + "side": "above", + "mass_usd": 182732123, + "width_pct": 4.4, + "buckets": 21 + }, + { + "price": 1.6451588681612548, + "distance_pct": 3.47, + "side": "above", + "mass_usd": 115378712, + "width_pct": 3.08, + "buckets": 15 + }, + { + "price": 1.764935307495907, + "distance_pct": 11, + "side": "above", + "mass_usd": 44059902, + "width_pct": 1.32, + "buckets": 7 + }, + { + "price": 1.5447823288380116, + "distance_pct": -2.84, + "side": "below", + "mass_usd": 35451253, + "width_pct": 1.54, + "buckets": 8 + } + ], + "reach_pct": 32.08, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.8921, + "up_mass": 1140612637, + "down_mass": 65030594, + "overhang_ratio": 17.54, + "side": "above", + "center_price": 1.8779269815373163, + "center_distance_pct": 18.11 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 17.54x heavier above, led by a pocket at +17.97%." + }, + "notes": [] + }, + { + "token": "XMR", + "spot": 320.39, + "gravity": 0.3244, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 323.93, + "target_distance_pct": 1.1, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.21037911082231336, + "per_tf": { + "12h": 0.4587, + "24h": 0.4086, + "3d": -0.6953 + }, + "z_cross": 4.50435302152508, + "up_mass": 27460913.577095505, + "down_mass": 47848274.153693676, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 298.9595491204932, + "distance_pct": -6.69, + "side": "below", + "mass_usd": 1014983223, + "width_pct": 8.09, + "buckets": 33, + "dominance_ratio": 4.19 + }, + "top_pockets": [ + { + "price": 298.9595491204932, + "distance_pct": -6.69, + "side": "below", + "mass_usd": 1014983223, + "width_pct": 8.09, + "buckets": 33 + }, + { + "price": 333.20600046482934, + "distance_pct": 4, + "side": "above", + "mass_usd": 419864854, + "width_pct": 3.54, + "buckets": 15 + }, + { + "price": 357.4351040992632, + "distance_pct": 11.56, + "side": "above", + "mass_usd": 323116400, + "width_pct": 3.29, + "buckets": 14 + }, + { + "price": 349.1461226849569, + "distance_pct": 8.98, + "side": "above", + "mass_usd": 178637246, + "width_pct": 1.26, + "buckets": 6 + }, + { + "price": 342.437513879027, + "distance_pct": 6.88, + "side": "above", + "mass_usd": 169457159, + "width_pct": 1.26, + "buckets": 6 + }, + { + "price": 391.1920683593978, + "distance_pct": 22.1, + "side": "above", + "mass_usd": 139380318, + "width_pct": 1.77, + "buckets": 6 + } + ], + "reach_pct": 36.1, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.2759, + "up_mass": 1891302450, + "down_mass": 1073359476, + "overhang_ratio": 1.76, + "side": "above", + "center_price": 365.7850327100295, + "center_distance_pct": 14.17 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "below", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 1.76x heavier above (though the biggest single pocket sits at -6.69%, the other way)." + }, + "notes": [] + }, + { + "token": "NEAR", + "spot": 1.993, + "gravity": 0.2956, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 2.021, + "target_distance_pct": 1.4, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.19042061764102772, + "per_tf": { + "12h": 0.3091, + "24h": 0.2424, + "3d": -0.1674 + }, + "z_cross": 4.787701661004803, + "up_mass": 62461554.16506126, + "down_mass": 48463097.62852383, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 2.098266219166565, + "distance_pct": 5.23, + "side": "above", + "mass_usd": 1201140485, + "width_pct": 5.94, + "buckets": 17, + "dominance_ratio": 1.54 + }, + "top_pockets": [ + { + "price": 2.098266219166565, + "distance_pct": 5.23, + "side": "above", + "mass_usd": 1201140485, + "width_pct": 5.94, + "buckets": 17 + }, + { + "price": 1.9246752179351239, + "distance_pct": -3.48, + "side": "below", + "mass_usd": 1043056074, + "width_pct": 3.34, + "buckets": 10 + }, + { + "price": 1.7981005092685238, + "distance_pct": -9.82, + "side": "below", + "mass_usd": 903184967, + "width_pct": 2.97, + "buckets": 9 + }, + { + "price": 2.315285995142206, + "distance_pct": 16.11, + "side": "above", + "mass_usd": 866730296, + "width_pct": 4.45, + "buckets": 13 + }, + { + "price": 2.9595439913217034, + "distance_pct": 48.42, + "side": "above", + "mass_usd": 731343864, + "width_pct": 1.11, + "buckets": 3 + }, + { + "price": 2.896617230609505, + "distance_pct": 45.27, + "side": "above", + "mass_usd": 721272489, + "width_pct": 0.37, + "buckets": 2 + } + ], + "reach_pct": 59.14, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.4751, + "up_mass": 6937468094, + "down_mass": 2468630620, + "overhang_ratio": 2.81, + "side": "above", + "center_price": 2.5526220629339296, + "center_distance_pct": 28.02 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 2.81x heavier above, led by a pocket at +5.23%." + }, + "notes": [] + }, + { + "token": "TAO", + "spot": 215.67, + "gravity": 0.2918, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 218.666, + "target_distance_pct": 1.39, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.18786485945473716, + "per_tf": { + "12h": 0.3882, + "24h": 0.3731, + "3d": -0.5871 + }, + "z_cross": 4.2480285399524025, + "up_mass": 41486000.90898626, + "down_mass": 46793277.42964524, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 238.3346875615011, + "distance_pct": 10.5, + "side": "above", + "mass_usd": 1484008131, + "width_pct": 5.42, + "buckets": 20, + "dominance_ratio": 2.81 + }, + "top_pockets": [ + { + "price": 238.3346875615011, + "distance_pct": 10.5, + "side": "above", + "mass_usd": 1484008131, + "width_pct": 5.42, + "buckets": 20 + }, + { + "price": 204.59184018997328, + "distance_pct": -5.15, + "side": "below", + "mass_usd": 819370884, + "width_pct": 6.27, + "buckets": 23 + }, + { + "price": 224.3389369916955, + "distance_pct": 4.01, + "side": "above", + "mass_usd": 774941372, + "width_pct": 3.99, + "buckets": 15 + }, + { + "price": 270.79927631110314, + "distance_pct": 25.55, + "side": "above", + "mass_usd": 392054449, + "width_pct": 1.14, + "buckets": 5 + }, + { + "price": 188.19106328900625, + "distance_pct": -12.75, + "side": "below", + "mass_usd": 326985867, + "width_pct": 0.86, + "buckets": 4 + }, + { + "price": 295.51739968559633, + "distance_pct": 37.01, + "side": "above", + "mass_usd": 277104111, + "width_pct": 1.43, + "buckets": 4 + } + ], + "reach_pct": 37.53, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.531, + "up_mass": 5028311720, + "down_mass": 1540215843, + "overhang_ratio": 3.26, + "side": "above", + "center_price": 252.31152257690184, + "center_distance_pct": 16.98 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "above", + "deep_side": "above", + "state": "aligned_up", + "note": "Near pull UP confirmed by a deep book 3.26x heavier above, led by a pocket at +10.5%." + }, + "notes": [] + }, + { + "token": "ONDO", + "spot": 0.3314, + "gravity": -0.234, + "label": "MILD DOWN PULL", + "side": "SHORT", + "conviction": 1, + "target_price": 0.32548, + "target_distance_pct": -1.79, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.14903238654850862, + "per_tf": { + "12h": -0.1204, + "24h": -0.0252, + "3d": -0.43 + }, + "z_cross": 4.019878314366052, + "up_mass": 44403577.65820685, + "down_mass": 65972503.90864671, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.356032029119887, + "distance_pct": 7.4, + "side": "above", + "mass_usd": 1291877117, + "width_pct": 10.52, + "buckets": 43, + "dominance_ratio": 4.87 + }, + "top_pockets": [ + { + "price": 0.356032029119887, + "distance_pct": 7.4, + "side": "above", + "mass_usd": 1291877117, + "width_pct": 10.52, + "buckets": 43 + }, + { + "price": 0.3923951772242193, + "distance_pct": 18.37, + "side": "above", + "mass_usd": 802664854, + "width_pct": 3.76, + "buckets": 16 + }, + { + "price": 0.433292123596526, + "distance_pct": 30.71, + "side": "above", + "mass_usd": 298876027, + "width_pct": 0.75, + "buckets": 3 + }, + { + "price": 0.38374889304977877, + "distance_pct": 15.76, + "side": "above", + "mass_usd": 227142439, + "width_pct": 0.75, + "buckets": 4 + }, + { + "price": 0.40239861113742464, + "distance_pct": 21.39, + "side": "above", + "mass_usd": 210435380, + "width_pct": 1, + "buckets": 5 + }, + { + "price": 0.378426642076466, + "distance_pct": 14.16, + "side": "above", + "mass_usd": 174252124, + "width_pct": 1.75, + "buckets": 8 + } + ], + "reach_pct": 33.47, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.7483, + "up_mass": 3358596825, + "down_mass": 483528647, + "overhang_ratio": 6.95, + "side": "above", + "center_price": 0.38512332960090273, + "center_distance_pct": 16.18 + }, + "alignment": { + "near_side": "SHORT", + "overhang_side": "above", + "deep_side": "above", + "state": "near_vs_deep_conflict", + "note": "Near pull DOWN into an opposing deep book 6.95x heavier ABOVE, led by a pocket at +7.4% — treat the near signal with caution." + }, + "notes": [ + "Near pull DOWN into an opposing deep book 6.95x heavier ABOVE, led by a pocket at +7.4% — treat the near signal with caution." + ] + }, + { + "token": "AERO", + "spot": 0.5135, + "gravity": 0.203, + "label": "MILD UP PULL", + "side": "LONG", + "conviction": 1, + "target_price": 0.52012, + "target_distance_pct": 1.29, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.12864305127307474, + "per_tf": { + "12h": 0.1371, + "24h": 0.2636, + "3d": -0.1265 + }, + "z_cross": 4.235677607075355, + "up_mass": 14501944.372779269, + "down_mass": 11118921.767054176, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.4083217737422247, + "distance_pct": -20.47, + "side": "below", + "mass_usd": 277319898, + "width_pct": 7.78, + "buckets": 27, + "dominance_ratio": 12.56 + }, + "top_pockets": [ + { + "price": 0.4083217737422247, + "distance_pct": -20.47, + "side": "below", + "mass_usd": 277319898, + "width_pct": 7.78, + "buckets": 27 + }, + { + "price": 0.34142157804608997, + "distance_pct": -33.5, + "side": "below", + "mass_usd": 243863003, + "width_pct": 11.82, + "buckets": 42 + }, + { + "price": 0.48407905380142174, + "distance_pct": -5.71, + "side": "below", + "mass_usd": 51289975, + "width_pct": 3.75, + "buckets": 14 + }, + { + "price": 0.4447617160328056, + "distance_pct": -13.37, + "side": "below", + "mass_usd": 43072831, + "width_pct": 1.15, + "buckets": 5 + }, + { + "price": 0.38000436282862526, + "distance_pct": -25.98, + "side": "below", + "mass_usd": 38844115, + "width_pct": 1.44, + "buckets": 6 + }, + { + "price": 0.435548681095651, + "distance_pct": -15.16, + "side": "below", + "mass_usd": 27119223, + "width_pct": 1.15, + "buckets": 5 + } + ], + "reach_pct": 42.49, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": -0.8236, + "up_mass": 77501941, + "down_mass": 801039674, + "overhang_ratio": 10.34, + "side": "below", + "center_price": 0.3985849079215477, + "center_distance_pct": -22.36 + }, + "alignment": { + "near_side": "LONG", + "overhang_side": "below", + "deep_side": "below", + "state": "near_vs_deep_conflict", + "note": "Near pull UP into an opposing deep book 10.34x heavier BELOW, led by a pocket at -20.47% — treat the near signal with caution." + }, + "notes": [ + "Near pull UP into an opposing deep book 10.34x heavier BELOW, led by a pocket at -20.47% — treat the near signal with caution." + ] + }, + { + "token": "VIRTUAL", + "spot": 0.5709, + "gravity": 0.1517, + "label": "NEUTRAL / PIN", + "side": "NEUTRAL", + "conviction": 1, + "target_price": 0.57815, + "target_distance_pct": 1.27, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.09556318300345966, + "per_tf": { + "12h": 0.4644, + "24h": -0.2624, + "3d": -0.108 + }, + "z_cross": 3.2745127430247307, + "up_mass": 10064352.345723668, + "down_mass": 13033380.081887957, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.6227311308972255, + "distance_pct": 9.08, + "side": "above", + "mass_usd": 244105696, + "width_pct": 5.54, + "buckets": 21, + "dominance_ratio": 2.01 + }, + "top_pockets": [ + { + "price": 0.6227311308972255, + "distance_pct": 9.08, + "side": "above", + "mass_usd": 244105696, + "width_pct": 5.54, + "buckets": 21 + }, + { + "price": 0.651507274017539, + "distance_pct": 14.12, + "side": "above", + "mass_usd": 222715569, + "width_pct": 4.15, + "buckets": 16 + }, + { + "price": 0.6787341435289244, + "distance_pct": 18.89, + "side": "above", + "mass_usd": 203435780, + "width_pct": 1.66, + "buckets": 7 + }, + { + "price": 0.7705392315923191, + "distance_pct": 34.97, + "side": "above", + "mass_usd": 161466607, + "width_pct": 2.77, + "buckets": 9 + }, + { + "price": 0.592465405087979, + "distance_pct": 3.78, + "side": "above", + "mass_usd": 138690554, + "width_pct": 3.32, + "buckets": 13 + }, + { + "price": 0.5224882015458476, + "distance_pct": -8.48, + "side": "below", + "mass_usd": 84986646, + "width_pct": 2.49, + "buckets": 10 + } + ], + "reach_pct": 37.95, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.6458, + "up_mass": 1237932523, + "down_mass": 266384891, + "overhang_ratio": 4.65, + "side": "above", + "center_price": 0.6751738819657553, + "center_distance_pct": 18.26 + }, + "alignment": { + "near_side": "NEUTRAL", + "overhang_side": "above", + "deep_side": "above", + "state": "mixed", + "note": "Near and deep lenses not strongly aligned (near neutral, deep above)." + }, + "notes": [] + }, + { + "token": "CC", + "spot": 0.13929, + "gravity": -0.1445, + "label": "NEUTRAL / PIN", + "side": "NEUTRAL", + "conviction": 1, + "target_price": 0.135796, + "target_distance_pct": -2.51, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.0909432284984209, + "per_tf": { + "12h": -0.2597, + "24h": 0.1335, + "3d": -0.104 + }, + "z_cross": 5.336212444268712, + "up_mass": 7385452.984850295, + "down_mass": 7738358.867900996, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.17273932279532786, + "distance_pct": 24.01, + "side": "above", + "mass_usd": 242886983, + "width_pct": 4.65, + "buckets": 24, + "dominance_ratio": 8.59 + }, + "top_pockets": [ + { + "price": 0.17273932279532786, + "distance_pct": 24.01, + "side": "above", + "mass_usd": 242886983, + "width_pct": 4.65, + "buckets": 24 + }, + { + "price": 0.15744310646404974, + "distance_pct": 13.03, + "side": "above", + "mass_usd": 216835182, + "width_pct": 6.32, + "buckets": 35 + }, + { + "price": 0.1468821544555171, + "distance_pct": 5.45, + "side": "above", + "mass_usd": 129234325, + "width_pct": 5.02, + "buckets": 28 + }, + { + "price": 0.16765912400317534, + "distance_pct": 20.37, + "side": "above", + "mass_usd": 65962598, + "width_pct": 2.98, + "buckets": 17 + }, + { + "price": 0.15142291862329, + "distance_pct": 8.71, + "side": "above", + "mass_usd": 51824748, + "width_pct": 1.3, + "buckets": 8 + }, + { + "price": 0.1626655464526045, + "distance_pct": 16.78, + "side": "above", + "mass_usd": 13528580, + "width_pct": 0.56, + "buckets": 4 + } + ], + "reach_pct": 26.73, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.9657, + "up_mass": 736488502, + "down_mass": 12846522, + "overhang_ratio": 57.33, + "side": "above", + "center_price": 0.1611977736620008, + "center_distance_pct": 15.73 + }, + "alignment": { + "near_side": "NEUTRAL", + "overhang_side": "above", + "deep_side": "above", + "state": "mixed", + "note": "Near and deep lenses not strongly aligned (near neutral, deep above)." + }, + "notes": [] + }, + { + "token": "UNI", + "spot": 3.216, + "gravity": -0.1328, + "label": "NEUTRAL / PIN", + "side": "NEUTRAL", + "conviction": 1, + "target_price": 3.1396, + "target_distance_pct": -2.38, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.08348965290805016, + "per_tf": { + "12h": -0.0152, + "24h": -0.1177, + "3d": -0.1773 + }, + "z_cross": 3.8436313858045015, + "up_mass": 71840840.61989775, + "down_mass": 88917816.33297768, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 2.4394241050043592, + "distance_pct": -24.15, + "side": "below", + "mass_usd": 410533385, + "width_pct": 2.24, + "buckets": 10, + "dominance_ratio": 1.97 + }, + "top_pockets": [ + { + "price": 2.4394241050043592, + "distance_pct": -24.15, + "side": "below", + "mass_usd": 410533385, + "width_pct": 2.24, + "buckets": 10 + }, + { + "price": 2.925739018040364, + "distance_pct": -9.03, + "side": "below", + "mass_usd": 342675585, + "width_pct": 3.23, + "buckets": 14 + }, + { + "price": 2.715798379163734, + "distance_pct": -15.55, + "side": "below", + "mass_usd": 249516507, + "width_pct": 2.24, + "buckets": 10 + }, + { + "price": 3.0489546110080776, + "distance_pct": -5.19, + "side": "below", + "mass_usd": 231556607, + "width_pct": 1.74, + "buckets": 8 + }, + { + "price": 3.7396206658749858, + "distance_pct": 16.28, + "side": "above", + "mass_usd": 193555043, + "width_pct": 1.24, + "buckets": 4 + }, + { + "price": 2.573875660503028, + "distance_pct": -19.97, + "side": "below", + "mass_usd": 193151179, + "width_pct": 1.74, + "buckets": 8 + } + ], + "reach_pct": 30.19, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": -0.6009, + "up_mass": 605584999, + "down_mass": 2429139936, + "overhang_ratio": 4.01, + "side": "below", + "center_price": 2.7215038704162353, + "center_distance_pct": -15.38 + }, + "alignment": { + "near_side": "NEUTRAL", + "overhang_side": "below", + "deep_side": "below", + "state": "mixed", + "note": "Near and deep lenses not strongly aligned (near neutral, deep below)." + }, + "notes": [] + }, + { + "token": "INJ", + "spot": 4.865, + "gravity": -0.1086, + "label": "NEUTRAL / PIN", + "side": "NEUTRAL", + "conviction": 1, + "target_price": 4.793, + "target_distance_pct": -1.48, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.06811703661105337, + "per_tf": { + "12h": 0.4486, + "24h": -0.5362, + "3d": -0.4117 + }, + "z_cross": 4.325672939102747, + "up_mass": 12343404.226914492, + "down_mass": 26308315.321123198, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 5.236563064832578, + "distance_pct": 7.62, + "side": "above", + "mass_usd": 756990479, + "width_pct": 12.23, + "buckets": 35, + "dominance_ratio": 5.2 + }, + "top_pockets": [ + { + "price": 5.236563064832578, + "distance_pct": 7.62, + "side": "above", + "mass_usd": 756990479, + "width_pct": 12.23, + "buckets": 35 + }, + { + "price": 7.05618995268184, + "distance_pct": 45.01, + "side": "above", + "mass_usd": 249999660, + "width_pct": 2.16, + "buckets": 5 + }, + { + "price": 6.142483199634456, + "distance_pct": 26.23, + "side": "above", + "mass_usd": 232429088, + "width_pct": 1.44, + "buckets": 4 + }, + { + "price": 5.645814529761401, + "distance_pct": 16.03, + "side": "above", + "mass_usd": 92686060, + "width_pct": 1.44, + "buckets": 5 + }, + { + "price": 7.198153145338322, + "distance_pct": 47.93, + "side": "above", + "mass_usd": 91009663, + "width_pct": 0.72, + "buckets": 2 + }, + { + "price": 5.8345992230030275, + "distance_pct": 19.91, + "side": "above", + "mass_usd": 87980894, + "width_pct": 3.24, + "buckets": 9 + } + ], + "reach_pct": 52.97, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.7705, + "up_mass": 2002904587, + "down_mass": 259594494, + "overhang_ratio": 7.72, + "side": "above", + "center_price": 5.996784820825075, + "center_distance_pct": 23.24 + }, + "alignment": { + "near_side": "NEUTRAL", + "overhang_side": "above", + "deep_side": "above", + "state": "mixed", + "note": "Near and deep lenses not strongly aligned (near neutral, deep above)." + }, + "notes": [] + }, + { + "token": "WLD", + "spot": 0.4226, + "gravity": -0.1041, + "label": "NEUTRAL / PIN", + "side": "NEUTRAL", + "conviction": 1, + "target_price": 0.41592, + "target_distance_pct": -1.58, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.06530326346167346, + "per_tf": { + "12h": -0.0078, + "24h": -0.1542, + "3d": -0.0391 + }, + "z_cross": 6.046797447582743, + "up_mass": 87567080.38920417, + "down_mass": 99185071.78614554, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.5060033951728121, + "distance_pct": 19.74, + "side": "above", + "mass_usd": 2653896450, + "width_pct": 16.7, + "buckets": 37, + "dominance_ratio": 8.5 + }, + "top_pockets": [ + { + "price": 0.5060033951728121, + "distance_pct": 19.74, + "side": "above", + "mass_usd": 2653896450, + "width_pct": 16.7, + "buckets": 37 + }, + { + "price": 0.4493085502974167, + "distance_pct": 6.32, + "side": "above", + "mass_usd": 1165007302, + "width_pct": 7.88, + "buckets": 18 + }, + { + "price": 0.5582605315928028, + "distance_pct": 32.1, + "side": "above", + "mass_usd": 692221002, + "width_pct": 2.78, + "buckets": 7 + }, + { + "price": 0.5885652571670471, + "distance_pct": 39.27, + "side": "above", + "mass_usd": 568531539, + "width_pct": 4.17, + "buckets": 10 + }, + { + "price": 0.383418542813134, + "distance_pct": -9.27, + "side": "below", + "mass_usd": 474040501, + "width_pct": 1.39, + "buckets": 4 + }, + { + "price": 0.6568121854813024, + "distance_pct": 55.42, + "side": "above", + "mass_usd": 408648503, + "width_pct": 2.32, + "buckets": 6 + } + ], + "reach_pct": 74.5, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.6864, + "up_mass": 8177757430, + "down_mass": 1520940528, + "overhang_ratio": 5.38, + "side": "above", + "center_price": 0.5603854086563919, + "center_distance_pct": 32.6 + }, + "alignment": { + "near_side": "NEUTRAL", + "overhang_side": "above", + "deep_side": "above", + "state": "mixed", + "note": "Near and deep lenses not strongly aligned (near neutral, deep above)." + }, + "notes": [] + }, + { + "token": "CRV", + "spot": 0.2066, + "gravity": -0.1015, + "label": "NEUTRAL / PIN", + "side": "NEUTRAL", + "conviction": 1, + "target_price": 0.20322, + "target_distance_pct": -1.64, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.0636761206708521, + "per_tf": { + "12h": -0.1096, + "24h": -0.0666, + "3d": 0.0447 + }, + "z_cross": 5.195253235711467, + "up_mass": 9136809.743862066, + "down_mass": 9854860.678042553, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.1957536502232034, + "distance_pct": -5.25, + "side": "below", + "mass_usd": 236406375, + "width_pct": 6.12, + "buckets": 24, + "dominance_ratio": 2.2 + }, + "top_pockets": [ + { + "price": 0.1957536502232034, + "distance_pct": -5.25, + "side": "below", + "mass_usd": 236406375, + "width_pct": 6.12, + "buckets": 24 + }, + { + "price": 0.21946740624924121, + "distance_pct": 6.23, + "side": "above", + "mass_usd": 180162476, + "width_pct": 4.79, + "buckets": 19 + }, + { + "price": 0.25327115058925453, + "distance_pct": 22.59, + "side": "above", + "mass_usd": 122315455, + "width_pct": 1.33, + "buckets": 6 + }, + { + "price": 0.23194429214178872, + "distance_pct": 12.27, + "side": "above", + "mass_usd": 90403818, + "width_pct": 2.93, + "buckets": 12 + }, + { + "price": 0.18658176223592585, + "distance_pct": -9.69, + "side": "below", + "mass_usd": 65165792, + "width_pct": 1.6, + "buckets": 7 + }, + { + "price": 0.17769455048048596, + "distance_pct": -13.99, + "side": "below", + "mass_usd": 59634908, + "width_pct": 1.86, + "buckets": 8 + } + ], + "reach_pct": 31.03, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.2288, + "up_mass": 710201153, + "down_mass": 445704636, + "overhang_ratio": 1.59, + "side": "above", + "center_price": 0.23836075523165942, + "center_distance_pct": 15.37 + }, + "alignment": { + "near_side": "NEUTRAL", + "overhang_side": "below", + "deep_side": "above", + "state": "mixed", + "note": "Near and deep lenses not strongly aligned (near neutral, deep above)." + }, + "notes": [] + }, + { + "token": "TRUMP", + "spot": 1.757, + "gravity": -0.0442, + "label": "NEUTRAL / PIN", + "side": "NEUTRAL", + "conviction": 1, + "target_price": 1.7385, + "target_distance_pct": -1.05, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.02766388870920203, + "per_tf": { + "12h": 0.1472, + "24h": -0.1941, + "3d": -0.1299 + }, + "z_cross": 6.282230541684519, + "up_mass": 33126835.859029084, + "down_mass": 42098305.9922096, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 1.5798329374792814, + "distance_pct": -10.08, + "side": "below", + "mass_usd": 395107036, + "width_pct": 3.48, + "buckets": 13, + "dominance_ratio": 1.44 + }, + "top_pockets": [ + { + "price": 1.5798329374792814, + "distance_pct": -10.08, + "side": "below", + "mass_usd": 395107036, + "width_pct": 3.48, + "buckets": 13 + }, + { + "price": 2.3422964212959574, + "distance_pct": 33.31, + "side": "above", + "mass_usd": 313729777, + "width_pct": 1.16, + "buckets": 3 + }, + { + "price": 2.049601293287535, + "distance_pct": 16.65, + "side": "above", + "mass_usd": 281643188, + "width_pct": 1.16, + "buckets": 5 + }, + { + "price": 1.9733913181059024, + "distance_pct": 12.32, + "side": "above", + "mass_usd": 233951370, + "width_pct": 1.45, + "buckets": 6 + }, + { + "price": 2.095168396449921, + "distance_pct": 19.25, + "side": "above", + "mass_usd": 214520152, + "width_pct": 1.16, + "buckets": 5 + }, + { + "price": 1.509102023801318, + "distance_pct": -14.11, + "side": "below", + "mass_usd": 175772105, + "width_pct": 1.74, + "buckets": 7 + } + ], + "reach_pct": 37.15, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.4048, + "up_mass": 2515299250, + "down_mass": 1065733442, + "overhang_ratio": 2.36, + "side": "above", + "center_price": 2.1032901825533865, + "center_distance_pct": 19.71 + }, + "alignment": { + "near_side": "NEUTRAL", + "overhang_side": "below", + "deep_side": "above", + "state": "mixed", + "note": "Near and deep lenses not strongly aligned (near neutral, deep above)." + }, + "notes": [] + }, + { + "token": "LTC", + "spot": 43.54, + "gravity": -0.0235, + "label": "NEUTRAL / PIN", + "side": "NEUTRAL", + "conviction": 1, + "target_price": 42.819, + "target_distance_pct": -1.66, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.01469535104684377, + "per_tf": { + "12h": 0.03, + "24h": -0.0462, + "3d": -0.06 + }, + "z_cross": 4.932957328960395, + "up_mass": 136513357.93018663, + "down_mass": 148853850.84730536, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 46.58187040077628, + "distance_pct": 7.01, + "side": "above", + "mass_usd": 2348696939, + "width_pct": 8.77, + "buckets": 54, + "dominance_ratio": 4.07 + }, + "top_pockets": [ + { + "price": 46.58187040077628, + "distance_pct": 7.01, + "side": "above", + "mass_usd": 2348696939, + "width_pct": 8.77, + "buckets": 54 + }, + { + "price": 40.99344377211996, + "distance_pct": -5.83, + "side": "below", + "mass_usd": 1418182483, + "width_pct": 7.28, + "buckets": 45 + }, + { + "price": 49.21941257206512, + "distance_pct": 13.07, + "side": "above", + "mass_usd": 674599917, + "width_pct": 4.47, + "buckets": 19 + }, + { + "price": 39.09705537141626, + "distance_pct": -10.18, + "side": "below", + "mass_usd": 82008838, + "width_pct": 0.66, + "buckets": 5 + }, + { + "price": 51.282, + "distance_pct": 17.81, + "side": "above", + "mass_usd": 27611789, + "width_pct": 0, + "buckets": 1 + }, + { + "price": 38.251376701258344, + "distance_pct": -12.13, + "side": "below", + "mass_usd": 27322519, + "width_pct": 0.33, + "buckets": 3 + } + ], + "reach_pct": 17.81, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.3265, + "up_mass": 3051205546, + "down_mass": 1549010130, + "overhang_ratio": 1.97, + "side": "above", + "center_price": 47.207716057459145, + "center_distance_pct": 8.45 + }, + "alignment": { + "near_side": "NEUTRAL", + "overhang_side": "above", + "deep_side": "above", + "state": "mixed", + "note": "Near and deep lenses not strongly aligned (near neutral, deep above)." + }, + "notes": [] + }, + { + "token": "BCH", + "spot": 225.85, + "gravity": -0.0203, + "label": "NEUTRAL / PIN", + "side": "NEUTRAL", + "conviction": 1, + "target_price": 222.436, + "target_distance_pct": -1.51, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": -0.012666183523485364, + "per_tf": { + "12h": 0.3002, + "24h": -0.0971, + "3d": -0.5689 + }, + "z_cross": 3.8570127090603026, + "up_mass": 40581500.05645691, + "down_mass": 54911367.43236448, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 257.10685427077897, + "distance_pct": 13.84, + "side": "above", + "mass_usd": 1355488316, + "width_pct": 1.34, + "buckets": 4, + "dominance_ratio": 4.22 + }, + "top_pockets": [ + { + "price": 257.10685427077897, + "distance_pct": 13.84, + "side": "above", + "mass_usd": 1355488316, + "width_pct": 1.34, + "buckets": 4 + }, + { + "price": 290.6887227606854, + "distance_pct": 28.71, + "side": "above", + "mass_usd": 512926140, + "width_pct": 0.81, + "buckets": 3 + }, + { + "price": 262.6409655624141, + "distance_pct": 16.3, + "side": "above", + "mass_usd": 454830723, + "width_pct": 0.27, + "buckets": 2 + }, + { + "price": 236.33466255305012, + "distance_pct": 4.65, + "side": "above", + "mass_usd": 429003970, + "width_pct": 1.34, + "buckets": 6 + }, + { + "price": 232.04339100926103, + "distance_pct": 2.75, + "side": "above", + "mass_usd": 418229279, + "width_pct": 1.08, + "buckets": 5 + }, + { + "price": 259.249, + "distance_pct": 14.79, + "side": "above", + "mass_usd": 349323013, + "width_pct": 0, + "buckets": 1 + } + ], + "reach_pct": 31.46, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.4746, + "up_mass": 5565422804, + "down_mass": 1982739809, + "overhang_ratio": 2.81, + "side": "above", + "center_price": 263.18008938177763, + "center_distance_pct": 16.53 + }, + "alignment": { + "near_side": "NEUTRAL", + "overhang_side": "above", + "deep_side": "above", + "state": "mixed", + "note": "Near and deep lenses not strongly aligned (near neutral, deep above)." + }, + "notes": [] + }, + { + "token": "POL", + "spot": 0.07369, + "gravity": 0.002, + "label": "NEUTRAL / PIN", + "side": "NEUTRAL", + "conviction": 1, + "target_price": 0.07499, + "target_distance_pct": 1.76, + "dominant_source": "liquidations", + "components": { + "liquidation": { + "g_liq": 0.0012279878494260293, + "per_tf": { + "12h": 0.2263, + "24h": -0.0058, + "3d": -0.4929 + }, + "z_cross": 4.599556843980572, + "up_mass": 9000364.904950168, + "down_mass": 12251494.775509296, + "source": "heatmap" + } + }, + "deep_overhang": { + "available": true, + "dominant": { + "price": 0.07832325369845346, + "distance_pct": 6.29, + "side": "above", + "mass_usd": 184465209, + "width_pct": 7.47, + "buckets": 33, + "dominance_ratio": 2.56 + }, + "top_pockets": [ + { + "price": 0.07832325369845346, + "distance_pct": 6.29, + "side": "above", + "mass_usd": 184465209, + "width_pct": 7.47, + "buckets": 33 + }, + { + "price": 0.09701830083663675, + "distance_pct": 31.66, + "side": "above", + "mass_usd": 176339427, + "width_pct": 3.73, + "buckets": 15 + }, + { + "price": 0.08299067376853965, + "distance_pct": 12.62, + "side": "above", + "mass_usd": 160863754, + "width_pct": 5.6, + "buckets": 25 + }, + { + "price": 0.07119878976683143, + "distance_pct": -3.38, + "side": "below", + "mass_usd": 101032422, + "width_pct": 2.57, + "buckets": 12 + }, + { + "price": 0.09405788230348365, + "distance_pct": 27.64, + "side": "above", + "mass_usd": 67069748, + "width_pct": 0.93, + "buckets": 3 + }, + { + "price": 0.08855391931714011, + "distance_pct": 20.17, + "side": "above", + "mass_usd": 64211788, + "width_pct": 0.47, + "buckets": 3 + } + ], + "reach_pct": 33.93, + "source": "heatmap_30d" + }, + "deep_skew": { + "available": true, + "score": 0.6387, + "up_mass": 774854953, + "down_mass": 170817044, + "overhang_ratio": 4.54, + "side": "above", + "center_price": 0.08756575888814812, + "center_distance_pct": 18.83 + }, + "alignment": { + "near_side": "NEUTRAL", + "overhang_side": "above", + "deep_side": "above", + "state": "mixed", + "note": "Near and deep lenses not strongly aligned (near neutral, deep above)." + }, + "notes": [] + } + ] + } +} \ No newline at end of file diff --git a/showcase/athena-signal-commerce/examples/acp-job-65178-receipt.md b/showcase/athena-signal-commerce/examples/acp-job-65178-receipt.md new file mode 100644 index 0000000..e5e68e9 --- /dev/null +++ b/showcase/athena-signal-commerce/examples/acp-job-65178-receipt.md @@ -0,0 +1,86 @@ +# ACP Job 65178 — Completed E2E Receipt + +**Format:** ACP job lifecycle receipt +**Provider:** Athena — `0x308d7492ed5a7f06ea5181c801e8d71928eb2e5d` +**Buyer (client + evaluator):** `0x591cd33047f5c2bdd90a1be09eae4ee225db72d6` +**Offering:** `liquidation_gravity` ($1) +**Chain:** Base (8453) · ACP protocol v2 +**Job ID:** `65178` + +This is a real, completed agent-to-agent purchase: an external buyer agent +created, funded, and approved a job; **Athena's Provider poller priced and +delivered it autonomously** (no human in the provider loop). Wallet addresses and +transaction hashes below are public on-chain identifiers. + +--- + +## Lifecycle + +| Time (UTC) | Event | Detail | +|------------|-------|--------| +| 11:46:09 | `job.created` | client `0x591cd330…` → provider `0x308d7492…`, evaluator = client | +| 11:46:10 | `message` (requirement) | `{}` | +| 11:46:17 | `budget.set` | **amount = 1 USDC** — set by Athena's poller (~8s after create) | +| 11:47:59 | `job.funded` | **amount = 1 USDC** — buyer escrowed the budget | +| 11:48:11 | `job.submitted` | deliverable `hash = 0x3c2b4b5f55b42249ba8ef2317539574d40cf779a3ecf1c9b9a4191c2b528d669` (~12s after funding) | +| 11:49:01 | `job.completed` | escrow released to Athena · tx `0x8e965b7aa5e152167b66b1a78f2bf8ed1cda43479795f9540298aa1902828166` | + +**End-to-end: ~3 minutes.** + +## On-chain settlement (read from Base) + +| Wallet | Before | After | +|--------|--------|-------| +| Buyer `0x591cd330…` (USDC) | 17.40 | 16.45 | +| Athena provider `0x308d7492…` (USDC) | — | 1.05 (received escrow) | + +## Deliverable + +Athena submitted the standard signed envelope wrapping the live +Liquidation Gravity signal (48 assets). Full payload: +[`acp-deliverable-65178.json`](./acp-deliverable-65178.json). + +```json +{ + "signal": "liquidation-gravity", + "source": "Athena AI (api.0xathena.ai)", + "delivered_at": "2026-07-03T11:48:05.506Z", + "disclaimer": "Informational only — not financial advice.", + "data": { + "generated_at": "2026-07-03T11:30:10Z", + "count": 48, + "signals": [ + { + "token": "XPL", + "spot": 0.10286, + "gravity": -0.8836, + "label": "STRONG DOWN PULL", + "side": "SHORT", + "target_price": 0.101828, + "deep_skew": { "score": -0.8096, "...": "..." }, + "...": "..." + } + ] + } +} +``` + +## Reproduce + +The buyer side was driven with `acp-cli` (the provider side is the always-on +Vercel poller): + +```bash +acp agent use --agent-id +acp client create-job \ + --provider 0x308d7492ed5a7f06ea5181c801e8d71928eb2e5d \ + --offering-name liquidation_gravity --requirements '{}' --chain-id 8453 +# Athena's poller sets budget=1 within ~1 poll cycle +acp client fund --job-id --amount 1 --chain-id 8453 +# Athena's poller fetches + submits the deliverable +acp client complete --job-id --chain-id 8453 --reason "verified" +``` + +Live provider status any time: +`GET https://athena-acp.vercel.app/api/health` → +`{"ok":true,"configured":true,"offerings":["Athena_Wisdom_Rankings","HL_Smart_Money","Liquidation_Gravity"]}`. diff --git a/showcase/athena-signal-commerce/examples/x402-mcp-gateway.md b/showcase/athena-signal-commerce/examples/x402-mcp-gateway.md new file mode 100644 index 0000000..024f5ba --- /dev/null +++ b/showcase/athena-signal-commerce/examples/x402-mcp-gateway.md @@ -0,0 +1,85 @@ +# x402 MCP Gateway — Live Catalog & Dual-Gate Capture + +**Format:** live endpoint capture +**Endpoint:** `https://api.0xathena.ai/mcp` (streamable HTTP, stateless JSON-RPC) +**Chain:** Base (8453) +**Captured:** 2026-07-03 + +The same signals Athena sells as ACP jobs are also exposed as **ten read-only +MCP tools**, gated by **x402**. Discovery is open; only `tools/call` is gated. +Inspect it live: `https://api.0xathena.ai/mcp/info` and +`https://api.0xathena.ai/mcp/health`. + +--- + +## `GET /mcp/health` + +```json +{ + "ok": true, + "service": "athena-mcp", + "configured": { "upstream": true, "rpc": true, "paid_path": true, "internal_bridge": true } +} +``` + +`paid_path: true` — the x402 pay-per-call rail is enabled (Coinbase CDP facilitator). + +## `GET /mcp/info` — access (two rails) + +```json +{ + "access": { + "holders": { + "endpoint": "https://api.0xathena.ai/mcp", + "how": "x402: sign a 0-value USDC EIP-3009 authorization (proves wallet ownership — nothing settles on-chain) from a wallet holding >= 1,000,000 $ATHENA (0x1a43287cBfCc5f35082e6E2Aa98e5B474FE7Bd4e) on Base. Smart wallets (ERC-1271/6492) supported.", + "price": "free" + }, + "pay_per_call": { + "endpoint": "https://api.0xathena.ai/mcp/paid", + "how": "x402: 1 USDC (Base) per tools/call — standard exact/EVM scheme, v1 (X-PAYMENT) and v2 (PAYMENT-SIGNATURE) accepted.", + "price": "1 USDC per tools/call", + "enabled": true + } + } +} +``` + +## `GET /mcp/info` — rate limits + +```json +{ + "per_wallet_per_minute": 12, + "per_ip_per_minute": 30, + "global_per_minute": 120, + "per_wallet_per_day": 300 +} +``` + +## The ten tools + +| Tool | Returns | +|------|---------| +| `get_wisdom_ranking` | The live Athena's Wisdom cross-sectional ranking | +| `get_wisdom_skew_leaderboard` | Wisdom rank joined with the Deep-Skew rank per asset | +| `get_liquidation_gravity` | Per-asset liquidation-cluster pull (gravity, side, target, components) | +| `get_funding_map` | OI-weighted perp funding across the ~48-asset universe | +| `get_max_pain` | Options max-pain strikes for BTC / ETH / SOL (weekly / monthly / quarterly) | +| `get_smart_money_signals` | HL smart-money long/short ratios (by accounts and by size) + funding | +| `get_elite_wallets` | The ranked Smart Money Elite cohort with live Hyperliquid books | +| `get_elite_portfolio` | The Elite Consensus Portfolio (aggregated cohort book, multiple modes) | +| `get_vol_screener` | The IVR volatility board — short-strangle screener rows | +| `get_implied_vol` | Server-computed near-ATM implied volatility (Black-76 on live chains) | + +## Call it + +An x402-capable agent points its MCP client at the endpoint and pays per call +(or signs the zero-value holder proof); the client handles the x402 handshake: + +```bash +# holder path (free): sign a 0-value $ATHENA holder proof per call +# POST https://api.0xathena.ai/mcp (x402 challenge → signed authorization) +# pay-per-call: 1 USDC on Base per tools/call +# POST https://api.0xathena.ai/mcp/paid (x402 exact/EVM payment) +``` + +Gated responses are always `Cache-Control: no-store` — data is live, never cached. diff --git a/showcase/athena-signal-commerce/offerings/offerings.json b/showcase/athena-signal-commerce/offerings/offerings.json new file mode 100644 index 0000000..19aaebf --- /dev/null +++ b/showcase/athena-signal-commerce/offerings/offerings.json @@ -0,0 +1,73 @@ +{ + "agent_name": "Athena", + "version": "1.0", + "builder": "Athena AI", + "chain": "base", + "agent_id": "019eb1ef-a4e7-7cb4-bb4b-4ccc087e3c3a", + "wallet": "0x308d7492ed5a7f06ea5181c801e8d71928eb2e5d", + "token": { + "symbol": "$ATHENA", + "address": "0x1a43287cBfCc5f35082e6E2Aa98e5B474FE7Bd4e" + }, + "deliverable_envelope": { + "signal": "", + "source": "Athena AI (api.0xathena.ai)", + "delivered_at": "", + "disclaimer": "Informational only — not financial advice.", + "data": "" + }, + "rails": { + "acp": { + "how": "Escrowed per-job purchase on the Agent Commerce Protocol. Create a job from an offering, fund the quoted USDC budget, receive the signed deliverable, approve to release escrow.", + "chain_id": 8453 + }, + "x402": { + "endpoint": "https://api.0xathena.ai/mcp", + "how": "Per-call purchase of the same signals as MCP tools. Free with a zero-value $ATHENA holder proof (POST /mcp), or 1 USDC per call (POST /mcp/paid).", + "catalog": "https://api.0xathena.ai/mcp/info" + } + }, + "offerings": { + "acp_jobs": [ + { + "id": 1, + "name": "Athena_Wisdom_Rankings", + "title": "Athena's Wisdom Rankings", + "category": "signals", + "price": "$1.00", + "signal_tag": "crowdflow+wisdom", + "description": "The live Athena's Wisdom cross-sectional ranking — every asset in the curated perp universe scored on crowd positioning versus real spot demand, returned as the full scored and ranked list." + }, + { + "id": 2, + "name": "HL_Smart_Money", + "title": "HL Smart Money", + "category": "signals", + "price": "$5.00", + "signal_tag": "hl-smart-money", + "description": "The Hyperliquid smart-money product: the ranked elite-wallet cohort with a current-positioning headline (e.g. \"41% Short ETH · 20% Short HYPE\"), plus the cohort long/short positioning grid across the ~48-asset universe." + }, + { + "id": 3, + "name": "Liquidation_Gravity", + "title": "Liquidation Gravity", + "category": "signals", + "price": "$1.00", + "signal_tag": "liquidation-gravity", + "description": "The full liquidation-gravity signal set per asset — near pull (proximity-weighted liquidation mass), deep overhang (the dominant far pocket), deep skew (above/below mass balance), and the alignment read." + } + ] + }, + "mcp_tools": [ + "get_wisdom_ranking", + "get_wisdom_skew_leaderboard", + "get_liquidation_gravity", + "get_funding_map", + "get_max_pain", + "get_smart_money_signals", + "get_elite_wallets", + "get_elite_portfolio", + "get_vol_screener", + "get_implied_vol" + ] +} diff --git a/showcase/athena-signal-commerce/showcase.json b/showcase/athena-signal-commerce/showcase.json new file mode 100644 index 0000000..4608a1f --- /dev/null +++ b/showcase/athena-signal-commerce/showcase.json @@ -0,0 +1,112 @@ +{ + "slug": "athena-signal-commerce", + "title": "Athena — Signal Commerce", + "tagline": "Sells live crypto-market signals to other agents over two rails — escrowed ACP jobs and per-call x402 on Base", + "description": "Athena is a tokenized Virtuals agent that turns her gated crypto-signal API into an agent-to-agent business over two payment rails. On the Agent Commerce Protocol she runs a live Provider poller that prices a funded job from a fixed catalog (Wisdom Rankings $1, HL Smart Money $5, Liquidation Gravity $1), fetches the signal from her workers, and submits a signed JSON deliverable; on her MCP server the same signals are ten read-only tools gated by x402 — free to wallets holding 1,000,000+ $ATHENA via a zero-value USDC authorization, or 1 USDC per call for anyone else. Proof: a completed on-chain ACP job (65178) with its deliverable and settlement, the live provider health and offerings, and the public MCP catalog reporting the x402 pay-per-call path as enabled.", + "status": "live", + "topic": "agents", + "topics": [ + "agents", + "commerce", + "acp", + "x402", + "base", + "defi", + "crypto-signals", + "mcp" + ], + "builder": { + "name": "0xBludex", + "url": "https://github.com/0xBludex" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/athena-signal-commerce", + "demo": "https://api.0xathena.ai/mcp/info", + "share": "https://0xathena.ai/docs/acp", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Athena%20Signal%20Commerce&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20The%20two-rail%20model%20(ACP%20escrow%20vs%20x402%20per-call)%20is%20clear%0A-%20The%20job%2065178%20receipt%20makes%20the%20deliverable%20contract%20easy%20to%20integrate%0A-%20A%20specific%20signal%20offering%20or%20MCP%20tool%20I%27d%20want%20to%20buy%0A%0ANotes%3A%0A", + "video": "https://x.com/0xAthenaAI/status/2074804675686137929" + }, + "primitives": [ + "acp", + "wallet", + "token" + ], + "visual": { + "kind": "architecture diagram", + "eyebrow": "base + virtuals acp + x402", + "title": "one signal engine, two commerce rails", + "posterUrl": "https://pbs.twimg.com/amplify_video_thumb/2074798623032774656/img/3bTkfqx9qY_QXXhr.jpg", + "videoUrl": "https://video.twimg.com/amplify_video/2074798623032774656/vid/avc1/1426x720/PPRqki8DUfNAWQLX.mp4", + "videoLabel": "Watch the 0:56 demo on X" + }, + "skills": [ + { + "name": "signal-commerce-provider", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/athena-signal-commerce/skills/signal-commerce-provider", + "sourcePath": "showcase/athena-signal-commerce/skills/signal-commerce-provider", + "summary": "Reusable playbook to monetize a gated signal/data API as an autonomous agent over two rails: an ACP Provider poller (price a funded job, fetch the deliverable, submit a signed envelope, collect USDC) and an x402-gated MCP server (zero-value holder-proof free tier plus 1-USDC pay-per-call), sharing one deliverable contract.", + "install": "cp -R showcase/athena-signal-commerce/skills/signal-commerce-provider ~/.agents/skills/\ncp -R showcase/athena-signal-commerce/skills/signal-commerce-provider ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Demo video (0:56) — Athena Signal Commerce on X", + "href": "https://x.com/0xAthenaAI/status/2074804675686137929", + "kind": "video" + }, + { + "label": "Signal Commerce package README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/athena-signal-commerce/README.md", + "kind": "docs" + }, + { + "label": "ACP offerings catalog", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/athena-signal-commerce/offerings/offerings.json", + "kind": "manifest" + }, + { + "label": "ACP job 65178 — completed E2E receipt (lifecycle, hashes, settlement)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/athena-signal-commerce/examples/acp-job-65178-receipt.md", + "kind": "proof" + }, + { + "label": "ACP job 65178 — delivered signal payload (48 assets)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/athena-signal-commerce/examples/acp-deliverable-65178.json", + "kind": "proof" + }, + { + "label": "x402 MCP gateway — live catalog + dual-gate capture", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/athena-signal-commerce/examples/x402-mcp-gateway.md", + "kind": "proof" + }, + { + "label": "Live ACP provider health (configured + offerings)", + "href": "https://athena-acp.vercel.app/api/health", + "kind": "proof" + }, + { + "label": "Live x402 MCP catalog", + "href": "https://api.0xathena.ai/mcp/info", + "kind": "proof" + }, + { + "label": "Agent soul — provider identity and guardrails", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/athena-signal-commerce/soul.md", + "kind": "docs" + }, + { + "label": "Reusable skill — signal-commerce-provider", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/athena-signal-commerce/skills/signal-commerce-provider", + "kind": "skill" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/athena-signal-commerce/soul.md", + "summary": "Athena's operational identity as a signal provider: what she sells, the two commerce rails, her redaction and disclaimer guardrails, and where she escalates instead of acting." + }, + "feedbackPrompts": [ + "Is the two-rail model — ACP escrowed jobs vs x402 per-call — clear, and which rail would your agent use?", + "Does the completed job 65178 receipt make the signed-envelope deliverable contract easy to integrate against?", + "Which additional signal offering or MCP tool would be most valuable to buy agent-to-agent?" + ] +} diff --git a/showcase/athena-signal-commerce/skills/signal-commerce-provider/SKILL.md b/showcase/athena-signal-commerce/skills/signal-commerce-provider/SKILL.md new file mode 100644 index 0000000..8ba8b71 --- /dev/null +++ b/showcase/athena-signal-commerce/skills/signal-commerce-provider/SKILL.md @@ -0,0 +1,120 @@ +--- +name: signal-commerce-provider +description: Monetize a gated signal or data API as an autonomous agent over two rails — an ACP Provider poller (escrowed per-job sales) and an x402-gated MCP server (zero-value holder-proof free tier plus 1-USDC pay-per-call) — sharing one signed deliverable contract. Use when you have a working data/signal endpoint and want to sell its outputs agent-to-agent on Base. +--- + +# Signal Commerce Provider + +A reusable playbook for turning a **gated data/signal API** into an +agent-to-agent business over two payment rails at once, without duplicating the +data layer. Athena uses it to sell Hyperliquid smart-money, Wisdom-ranking, and +liquidation signals; the pattern generalizes to any read-only data product. + +## When To Use + +- You already have a **working** data/signal endpoint (the "brain") and want + buyers — human-run agents or autonomous ones — to pay for its outputs. +- You want **both** an escrowed marketplace channel (ACP) and a low-friction + per-call channel (x402), without maintaining two separate data pipelines. + +## When NOT To Use + +- The product isn't live yet. Sell real outputs, not placeholders. +- The deliverable would leak private methodology (model weights, thresholds). + Sell **outputs**, not the recipe. +- The action isn't read-only. This pattern is for selling *information*; it must + not be able to move a buyer's funds or place trades. + +## Prerequisites + +- A gated data endpoint you control (the source of truth for every deliverable). +- A funded agent wallet on Base and a tokenized agent identity (for ACP + the + optional holder gate). +- `acp-cli` configured with the active provider agent, for the ACP rail. +- An MCP-capable worker/host and an x402 facilitator (e.g. Coinbase CDP), for the + x402 rail. + +## Core principle — one deliverable contract, two rails + +Define the deliverable **once** as a stable envelope and serve it on both rails: + +```json +{ + "signal": "", + "source": " ()", + "delivered_at": "", + "disclaimer": "Informational only — not financial advice.", + "data": { "...": "the live payload from your gated endpoint" } +} +``` + +Both rails fetch from the same gated endpoint and wrap it in the same envelope, +so a buyer integrates once regardless of how they paid. + +## Rail 1 — ACP Provider poller + +Publish offerings, then run a poller (cron, ~60s) that reacts to jobs: + +1. **Hydrate** open jobs; read the requirement message. +2. **Resolve + price** the offering from a fixed catalog → `setBudget(price)`. + Keep catalog prices and code in lockstep; resolve offering names + case-insensitively so listing drift can't orphan a paid job. +3. On `job.funded`, **fetch** the deliverable from your gated endpoint + (server-only credential) and **submit** the signed envelope. +4. **Idempotency:** rely on the ACP state machine; make submit safe to retry. +5. Escrow releases to your wallet when the buyer approves. + +Buyer flow (for your docs / a test): + +```bash +acp client create-job --provider \ + --offering-name --requirements '{}' --chain-id 8453 +acp client fund --job-id --amount --chain-id 8453 +acp client complete --job-id --chain-id 8453 --reason verified +``` + +**Always land one real completed job** and keep the receipt (lifecycle + hashes + +settlement) — it is the only proof that buyers can actually purchase. + +## Rail 2 — x402-gated MCP server + +Expose the same signals as read-only MCP tools; keep discovery open, gate only +`tools/call`: + +- **Holder path (free):** require a **zero-value** USDC EIP-3009 authorization + signature. It proves wallet ownership and settles nothing — recover the signer, + then check the token balance (support ERC-1271/6492 smart wallets). Cache the + balance check briefly. +- **Pay-per-call:** accept x402 (v1 `X-PAYMENT` and v2 `PAYMENT-SIGNATURE`), a + fixed price per call, settled through a facilitator. Return the settlement + receipt header on success. +- Publish a **public catalog** (`/info`) and a **health** endpoint so the gate + status (`paid_path: true`) and tool list are inspectable. +- Rate-limit per wallet / per IP / global; serve gated responses `no-store`. + +## Guardrails + +- **No fabricated proof.** Back every "it's live" claim with an on-chain receipt, + a live health endpoint, or the public catalog. +- **No secret sauce in deliverables.** Ship outputs; never embed the model. +- **Honest framing.** Carry a disclaimer; label descriptive signals as + descriptive, not guaranteed alpha. +- **Redact.** No keys, signer material, secrets, or account credentials in any + offering, deliverable, or artifact. Wallet addresses and tx hashes only. +- **Server-side gate.** Enforce access on the server for both rails; never gate + purely client-side. + +## Validation checklist + +- [ ] One completed ACP job receipt captured (lifecycle, deliverable hash, settlement). +- [ ] `/health` reports the pay path enabled; `/info` lists tools + access + limits. +- [ ] The same envelope is returned on both rails for the same signal. +- [ ] Catalog prices match code; offering names resolve case-insensitively. +- [ ] Gated responses are `no-store`; rate limits enforced. +- [ ] No secrets or private methodology in any public artifact. + +## Output contract + +A buyer — on either rail — receives the signed envelope above. `data` is your +live payload; `signal`, `source`, `delivered_at`, and `disclaimer` are always +present so integration is identical across rails. diff --git a/showcase/athena-signal-commerce/soul.md b/showcase/athena-signal-commerce/soul.md new file mode 100644 index 0000000..85303e8 --- /dev/null +++ b/showcase/athena-signal-commerce/soul.md @@ -0,0 +1,55 @@ +# Athena — Provider Soul + +Athena is a tokenized Virtuals agent ($ATHENA on Base) that sells proprietary +crypto-market signals to other agents. She is a **Provider**, not a job-taker: +she publishes offerings and a tool catalog and waits to be hired or called. + +## What she sells + +Signal *outputs* only — Hyperliquid smart-money positioning, the Athena's Wisdom +cross-sectional ranking, liquidation-gravity structure, options max-pain, and an +implied-volatility feed. Everything is **read-only**: nothing she sells can move +a buyer's funds or place a trade. + +## Two rails, one contract + +- **ACP** — escrowed per-job purchases; deliverables submitted through the ACP + contract after a job is funded. +- **x402** — per-call purchases (1 USDC) or a zero-value $ATHENA holder proof on + the MCP server. + +Both return the same signed envelope: `{ signal, source, delivered_at, +disclaimer, data }`. + +## Guardrails + +- **Honest framing.** Every deliverable carries `Informational only — not + financial advice`. Descriptive signals are labelled descriptive; they are never + presented as guaranteed directional alpha. +- **No secret sauce.** Buyers get signal outputs. Model weights, ranking-composite + constants, gate thresholds, and universe rules stay private and are never + embedded in a deliverable. +- **No fabricated proof.** Claims about a live surface are backed by an inspectable + artifact — an on-chain job receipt, a live health endpoint, or the public MCP + catalog — not prose. +- **Redaction.** No private keys, signer material, API secrets, or account + credentials appear in any deliverable, offering, or public artifact. Wallet + addresses and transaction hashes are public on-chain identifiers only. +- **Server-side gate.** Access is enforced server-side on every request (ACP + escrow state / x402 verification); gated payloads are served `no-store`. + +## Escalation + +Athena defers to her human operator rather than acting when: + +- A buyer requests data behind the token gate that a job/payment does not cover. +- A deliverable would require exposing private methodology to satisfy a request. +- Pricing, a new offering, or a new tool needs to be added or changed. +- A dispute needs a manual decision or the terms are ambiguous. + +## Review preference + +Athena favors inspectable proof over claims: on-chain job receipts, live +provider/MCP health endpoints, the public offering catalog, and the delivered +payloads themselves. The goal is to show that agent-to-agent signal commerce is +real, disciplined, and verifiable. diff --git a/showcase/beaver-knight/assets/poster.jpg b/showcase/beaver-knight/assets/poster.jpg new file mode 100755 index 0000000..79cf804 Binary files /dev/null and b/showcase/beaver-knight/assets/poster.jpg differ diff --git a/showcase/beaver-knight/showcase.json b/showcase/beaver-knight/showcase.json new file mode 100644 index 0000000..b52a1f0 --- /dev/null +++ b/showcase/beaver-knight/showcase.json @@ -0,0 +1,67 @@ +{ + "slug": "beaver-knight", + "title": "Beaver Knight", + "tagline": "The credit bureau for AI trading agents. Check any agent's real, on-chain track record before you trust it with money.", + "description": "Beaver Knight is a credit bureau for AI trading agents. It reads an agent's real trading wallet on-chain, separates what the agent actually did from what it claims and turns that into a plain 0-99 trust score anyone can re-derive from the chain. We are the referee, not a player: we run no fund and never touch your money. The public directory at beaverknight.com is seeded with real top agents scored from public on-chain data, whether they asked or not, so it is useful from day one. Every rating is stored in a canister on the Internet Computer, so the scores are tamper-evident and cannot be quietly changed, not even by us. The same engine is offered as a paid service on Virtuals ACP, so one agent can verify another before it pays or trusts it.", + "status": "live, on-chain-verified ratings at beaverknight.com", + "topic": "agents", + "topics": ["trading agents", "credit bureau", "reputation", "trust score", "verifiable track record", "on-chain", "acp"], + "builder": { + "name": "liander-ai", + "url": "https://beaverknight.com" + }, + "links": { + "repo": "https://beaverknight.com", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Beaver%20Knight", + "share": "https://x.com/liander_so", + "demo": "https://beaverknight.com" + }, + "primitives": ["wallet", "token", "acp"], + "visual": { + "kind": "live ratings register", + "eyebrow": "credit bureau · on-chain · acp", + "title": "Know which trading agent to trust before you fund it", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/beaver-knight/assets/poster.jpg" + }, + "skills": [ + { + "name": "verify-trading-agent", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/beaver-knight/skills/verify-trading-agent", + "sourcePath": "showcase/beaver-knight/skills/verify-trading-agent", + "summary": "Check a trading agent's real, un-fakeable on-chain track record before you trust it: pay 0.5 USDC via Virtuals ACP to the Beaver Knight verifyTradingAgent oracle and get back a re-derivable credit report (0-99 trust score plus the on-chain metrics behind it) computed from the target agent's real trading wallet. Wraps the public offering only; no internal scoring code is exposed.", + "install": "cp -R showcase/beaver-knight/skills/verify-trading-agent ~/.agents/skills/\ncp -R showcase/beaver-knight/skills/verify-trading-agent ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Live credit bureau, real agents scored from on-chain data (no wallet needed)", + "href": "https://beaverknight.com", + "kind": "demo" + }, + { + "label": "Sample credit report: an agent that claims $495k but settles about $1 on-chain", + "href": "https://beaverknight.com/report/8654", + "kind": "proof" + }, + { + "label": "Ratings stored on-chain in an Internet Computer canister (tamper-evident)", + "href": "https://dashboard.internetcomputer.org/canister/hqwv5-hyaaa-aaaaj-a6uha-cai", + "kind": "proof" + }, + { + "label": "Trust scores published to the ERC-8004 reputation rail on Base", + "href": "https://basescan.org/tx/0xa0b767341968cde8335154b725b471a0fa990a450ecedbfa6e10e293c790c932", + "kind": "proof" + }, + { + "label": "Litepaper", + "href": "https://beaverknight.com/litepaper", + "kind": "docs" + } + ], + "feedbackPrompts": [ + "Would a verified, un-fakeable track record change which trading agents you trust?", + "As an agent builder, would you want your agent's performance verifiable this way?", + "What would make the 0-99 trust score more convincing to you?" + ] +} diff --git a/showcase/beaver-knight/skills/verify-trading-agent/SKILL.md b/showcase/beaver-knight/skills/verify-trading-agent/SKILL.md new file mode 100644 index 0000000..fca0059 --- /dev/null +++ b/showcase/beaver-knight/skills/verify-trading-agent/SKILL.md @@ -0,0 +1,87 @@ +# Verify Trading Agent + +Verify a trading agent's real, un-fakeable on-chain track record before you back it or trust its signals. Pay 0.5 USDC through the Beaver Knight `verifyTradingAgent` offering on Virtuals ACP (Base) and get back a re-derivable trust report - a 0-100 trust score plus the return, self-stake ratio, max drawdown, and age it was computed from - read straight from the target vault's on-chain metrics. + +## When to Use + +- You are about to back a trading agent, allocate to its vault, or act on its signals, and you want an independent read of its real performance first. +- The target agent runs an on-chain vault (an ERC-4626-style book) whose value is marked to market, so its track record cannot be faked or backdated. +- You want a score you can re-derive yourself from the chain rather than trust a self-reported number. + +## When Not to Use + +- The target has no on-chain vault or trading history to read (there is nothing to verify). +- You need the raw scoring formula or weights - those are intentionally not exposed by this skill; it returns the result, not the recipe. +- You want to verify something other than trading performance (contract security, spend limits, identity) - use a purpose-built verifier for those. + +## Required Inputs + +- Target vault address and its `chainId` (e.g. Robinhood Chain `4663`). +- An ACP-capable Agent Wallet holding at least 0.5 USDC on Base to pay for the job. + +## Preconditions + +- An ACP client configured to open jobs on Virtuals ACP. +- USDC balance on Base for the 0.5 USDC job fee, plus a little native gas. + +## Workflow + +1. Resolve the Beaver Knight `verifyTradingAgent` provider on Virtuals ACP. +2. Open a job with `{ vault, chainId }` as the input. +3. Pay the 0.5 USDC job fee (approval gate). +4. The provider reads the target vault's on-chain risk metrics (`riskMetrics()`), computes the trust report, and returns it as the job deliverable. +5. Consume the deliverable and, if you want, independently re-derive the metrics from the same on-chain reads - the report is verifiable, not asserted. + +## Approval Gates + +- The 0.5 USDC payment that opens the verification job. + +## Stop Conditions + +- The target address exposes no readable vault metrics (not a trading vault, or wrong chain). +- Insufficient USDC to fund the job, or the ACP job is rejected. +- The provider is offline or the job times out - retry later; do not act on a partial report. + +## Evidence and Redaction Rules + +- Never log or commit wallet keys or signer material. +- The trust report, the target vault address, and the on-chain transactions are public and safe to share. +- The internal scoring formula, weights, and the operator's keyless signing setup are intentionally not part of this skill and are never disclosed. + +## Validation Checklist + +- [ ] The deliverable contains every field in the output contract. +- [ ] `trustScore` is within 0-100 and its `label` matches the score band. +- [ ] `returnPct`, `stakeRatioPct`, and `maxDrawdownPct` match a manual read of the vault's `riskMetrics()`. +- [ ] `explorerUrl` resolves to the target vault on the chain's block explorer. + +## Output Contract + +``` +{ + vault: string, // the verified vault address + chainId: number, + trustScore: number, // 0-100 + label: string, // e.g. "Unproven" | "Emerging" | "Established" | "Trusted" + returnPct: number, // since inception + stakeRatioPct: number, // operator's own money at risk, as % of AUM + maxDrawdownPct: number, + ageDays: number, + explorerUrl: string, + verifiedAt: string // ISO 8601 +} +``` + +## Endpoints and Contracts + +- `verifyTradingAgent` offering on Virtuals ACP (Base), priced at 0.5 USDC. +- ERC-8004 registries on Base the score can be published to: Identity `0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`, Reputation `0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`. +- Reads `AgentVault.riskMetrics()` on Robinhood Chain (chainId 4663) for RH-Chain agents. + +## Links + +- App: https://beaverknight.com +- Litepaper: https://beaverknight.com/litepaper +- A live agent to verify: https://robinhoodchain.blockscout.com/address/0x939e271172953895c5d191D2A29e339F899D65E4 +- Example on-chain reputation attestation: https://basescan.org/tx/0xa0b767341968cde8335154b725b471a0fa990a450ecedbfa6e10e293c790c932 +- Reference implementation of the offering lives in the builder's private execution repo (not published, to keep the scoring internals closed). diff --git a/showcase/blueagent/README.md b/showcase/blueagent/README.md new file mode 100644 index 0000000..d1faf5e --- /dev/null +++ b/showcase/blueagent/README.md @@ -0,0 +1,77 @@ +# BlueAgent + +**Verify before you execute.** + +A verify-first onchain agent for **Base** and **Robinhood Chain** — read the chain, +know what is real, then act. + +--- + +## What it does + +### B20 verify layer (Base) — live day one + +Base's B20 Native Token Standard went live on mainnet, and BlueAgent was ready from +block one — gated on-chain against the Activation Registry, so it flipped live the +exact moment B20 did. No hardcoded dates. + +**The trap it closes:** real B20s live at `0xB200…` addresses, but that prefix is +CREATE2 vanity — it can be faked, and plain ERC-20s already squat it while calling +themselves "B20." Names, holder counts, and address prefixes are all forgeable. + +Only `isB20()` on the Factory precompile proves authenticity. BlueAgent checks it +**first, every time**, then multicalls the token for variant, supply, cap, roles, +pause state, and policies — grounded reads, never a model's guess. + +### Robinhood Chain (4663) — live + +- Real-time chain TVL, trending pairs, and new pools +- **Rug flags** on new launches — low-liquidity pools and freefalling tokens surfaced + as they land +- Swaps with live pool quotes, signed in the user's own wallet +- Token launches via Bankr's launchpad + +### Blue Hub — x402 tool marketplace on Base + +Two-sided marketplace: agents call tools and pay **per call in USDC** — no signup, no +API key, no account. Anyone can list a tool and keep **95%** of what it earns. +Settlement runs on-chain through the Coinbase CDP facilitator. + +### MCP + +Connectable from Claude and Cursor. + +--- + +## Why it matters for ACP + +Blue Hub already runs on **x402** — the same payment rail ACP settles over. Tools are +priced per call and paid in USDC with no accounts and no keys, which is exactly the +shape an ACP job takes. The marketplace and the rails are live; what ACP adds is the +agent demand and the reputation layer to compound it. + +And on the safety side: as agents transact autonomously, the cost of a wrong read moves +from "a bad tweet" to "a drained wallet." Verification is not a nice-to-have in an agent +economy — it is the precondition for one. + +--- + +## Package contents + +| Path | What it is | +|---|---| +| `showcase.json` | Card manifest | +| `soul.md` | Public agent context and boundaries | +| `skills/blueagent-b20-verify/` | Reusable skill — grounded B20 verification | +| `examples/live-proof.md` | Live proof from Base and Robinhood Chain mainnet | + +--- + +## Links + +- **App** — https://app.blueagent.dev +- **Site** — https://blueagent.dev +- **B20 verify** — https://blueagent.dev/b20 +- **Blue Hub** — https://blueagent.dev/hub +- **MCP** — `https://blueagent.dev/api/mcp` +- **X** — [@blueagent_](https://x.com/blueagent_) · builder: [@madebyshun](https://x.com/madebyshun) diff --git a/showcase/blueagent/blueagent-demo.mp4 b/showcase/blueagent/blueagent-demo.mp4 new file mode 100644 index 0000000..395df10 Binary files /dev/null and b/showcase/blueagent/blueagent-demo.mp4 differ diff --git a/showcase/blueagent/blueagent-hero.png b/showcase/blueagent/blueagent-hero.png new file mode 100644 index 0000000..08e0f93 Binary files /dev/null and b/showcase/blueagent/blueagent-hero.png differ diff --git a/showcase/blueagent/examples/live-proof.md b/showcase/blueagent/examples/live-proof.md new file mode 100644 index 0000000..ca105ea --- /dev/null +++ b/showcase/blueagent/examples/live-proof.md @@ -0,0 +1,128 @@ +# BlueAgent — capabilities + +Four surfaces, live on Base and Robinhood Chain mainnet. + +**Demo video:** [blueagent-demo.mp4](https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/blueagent/blueagent-demo.mp4) + +--- + +## Blue Chat + +Natural-language onchain agent. No commands — it reads the chain, verifies what it finds, +and executes with the user signing every transaction. + +**Multi-model** — routes across Claude models; the active model, latency, and credit cost +are shown on every turn. + +| | | +|---|---| +| **Read** | live token prices, TVL, trending pairs, new pools, DEX flow, whale movement | +| **Verify** | honeypot detection, contract trust, key/backdoor exposure, AML wallet screening, B20 authenticity | +| **Execute** | swaps with live pool quotes, USDC/token sends with ENS resolution, yield positions, token launches | +| **Build** | Solidity contracts, React dashboards, HTML apps, scripts | +| **Wallet** | full portfolio read; B20 tokens carry a verified badge, everything else does not | + +Non-custodial throughout. BlueAgent never holds keys or funds. + +**Chains:** Base · Robinhood Chain (4663) + +--- + +## Blue Hub — x402 tool marketplace + +A two-sided marketplace on Base. Agents discover tools and **pay per call in USDC** — no +signup, no API key, no account. Anyone can list a tool and keep **95%** of what it earns. + +**Live tool categories:** + +| Category | Tools | +|---|---| +| **Prices & market** | `hub_token_price` · `hub_token_momentum` · `hub_dex_flow` · `hub_narrative_pulse` | +| **Security** | `hub_risk_gate` · `hub_honeypot` · `hub_contract_trust` · `hub_key_exposure` · `hub_deep_analysis` | +| **Trading intel** | `hub_token_pick` · `hub_whale_signal` · `hub_competitor_scan` · `hub_market_fit` | +| **Builder** | `hub_builder_score` · `hub_repo_health` · `hub_base_grant` · `hub_builder_dd` · `hub_investor_memo` · `hub_fundraise_timing` | +| **Onchain** | `hub_crypto_rpc` (21 chains) · `check_wallet` · `prepare_swap` · `robinhood_swap` | +| **Launches** | `hub_b20_launch` · `hub_robinhood_launch` · `prepare_token_launch` | +| **Research** | `web_search` · `hub_ecosystem` | + +**Pricing:** $0.01 – $1.00 per call. Settlement runs on-chain through the Coinbase CDP +facilitator. + +**Why it matters for ACP:** Blue Hub already settles over **x402** — the same rail ACP +uses for payment execution. Tools are priced per call, paid in USDC, with no accounts and +no keys. That is the shape of an ACP job. The marketplace and the rails are live; ACP +adds the agent demand and the reputation layer. + +--- + +## B20 Hub — the verify layer + +Full lifecycle tooling for Base's **B20 Native Token Standard**, live from day one of +mainnet activation. + +| Tool | What it does | +|---|---| +| `hub_b20_inspect` | Read live B20 state — variant, supply, cap, roles, pause state, policies | +| `hub_b20_analyze` | Explain a deployment and its role configuration | +| `hub_b20_manage` | Mint, burn, pause, set policy | +| `hub_b20_launch` | Deploy a B20 — user signs, no custody, no platform fee | +| `check_authorization` | Check whether a wallet is permitted under a token's transfer policy | + +**Protocol precompiles** (same address on every Base network): + +``` +B20 Factory 0xB20f000000000000000000000000000000000000 +Activation Registry 0x8453000000000000000000000000000000000001 +Policy Registry 0x8453000000000000000000000000000000000002 +``` + +### The trap it closes + +Real B20 tokens live at `0xB200…` addresses. **That prefix is CREATE2 vanity — it can be +faked.** Plain ERC-20s already squat `0xB200…` addresses and call themselves "B20." Token +names, holder counts, and address prefixes are all forgeable. + +**Only `isB20()` on the Factory proves authenticity.** BlueAgent calls it first, every +time, then multicalls the token for its real state. Grounded reads — never a model's +guess. + +This is why a verified B20 in a user's wallet carries a badge and nothing else does. + +### Activation gating + +B20 tooling reads `isActivated()` from the Activation Registry rather than trusting a +hardcoded date — so it went live in the same block B20 mainnet did. An RPC failure +degrades to `unknown`, never to `active`. + +--- + +## MCP server + +Every capability above is exposed over MCP for Claude and Cursor. + +```bash +claude mcp add blue-agent --transport http https://blueagent.dev/api/mcp +``` + +Agents connect once and get chain reads, safety checks, B20 verification, and execution +prep — the same tools Blue Chat uses. + +--- + +## Robinhood Chain (4663) + +| | | +|---|---| +| **Stream** | live chain TVL, trending pairs, new pools as they land | +| **Risk** | low-liquidity launches and collapsing tokens flagged — volume against thin liquidity is surfaced, not hidden behind a green percentage | +| **Swap** | live Uniswap pool quotes, user-signed | +| **Launch** | via Bankr — auto Uniswap pool, 0.7% swap fee, 95% recurring to the creator, gas handled | + +--- + +## Live + +- **App** — https://app.blueagent.dev +- **B20 verify** — https://blueagent.dev/b20 +- **Blue Hub** — https://blueagent.dev/hub +- **MCP** — `https://blueagent.dev/api/mcp` diff --git a/showcase/blueagent/showcase.json b/showcase/blueagent/showcase.json new file mode 100644 index 0000000..ada826b --- /dev/null +++ b/showcase/blueagent/showcase.json @@ -0,0 +1,67 @@ +{ + "slug": "blueagent", + "title": "BlueAgent", + "tagline": "Verify-first agent for Base and Robinhood Chain — paste any token to know instantly if it's a real B20, scan live chain intel, and execute swaps and launches from chat", + "description": "BlueAgent is a verify-first onchain agent covering Base and Robinhood Chain. Its B20 verify layer shipped on day one of Base's B20 Native Token Standard mainnet activation: real B20s live at 0xB200… addresses, but that prefix is CREATE2 vanity and can be faked — only isB20() on the Factory proves authenticity, and BlueAgent checks it first, every time, with grounded multicall reads instead of model guesses. On Robinhood Chain, BlueAgent streams live TVL, trending pairs, and new pools, flagging low-liquidity rug risk as launches land. Blue Hub is a two-sided x402 tool marketplace on Base — agents call tools and pay per call in USDC with no signup and no API keys, and anyone can list a tool and keep 95% of what it earns. All capabilities are exposed over MCP for Claude and Cursor.", + "status": "live on Base and Robinhood Chain mainnet", + "topic": "commerce", + "topics": ["commerce", "security", "onchain"], + "hidden": false, + "builder": { + "name": "Shun", + "url": "https://blueagent.dev" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/blueagent", + "demo": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/blueagent/examples/live-proof.md", + "share": "https://app.blueagent.dev", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20BlueAgent&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20Useful%20and%20ready%20to%20try%0A-%20Needs%20clearer%20docs%0A-%20Should%20cover%20more%20chains%0A-%20I%20want%20to%20list%20a%20tool%20on%20Blue%20Hub%0A%0ANotes%3A%0A" + }, + "primitives": ["wallet", "acp"], + "visual": { + "kind": "live agent demo", + "eyebrow": "base + robinhood chain + b20 + x402 + mcp", + "title": "verify before you execute", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/blueagent/blueagent-hero.png" + }, + "skills": [ + { + "name": "blueagent-b20-verify", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/blueagent/skills/blueagent-b20-verify", + "sourcePath": "showcase/blueagent/skills/blueagent-b20-verify", + "summary": "Grounded verification pattern for Base's B20 Native Token Standard. Never trust the 0xB200… address prefix — it is CREATE2 vanity and can be squatted by plain ERC-20s. Call isB20() on the B20 Factory precompile first, then multicall the token for variant, supply cap, roles, policies, and pause state. Read straight from chain, never from a model's guess.", + "install": "cp -R showcase/blueagent/skills/blueagent-b20-verify ~/.agents/skills/\ncp -R showcase/blueagent/skills/blueagent-b20-verify ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Live demo video — connectors, Robinhood Chain intel, core commands, B20 verify", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/blueagent/blueagent-demo.mp4", + "kind": "proof" + }, + { + "label": "Live proof — B20 verify on Base mainnet + Robinhood Chain rug flags", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/blueagent/examples/live-proof.md", + "kind": "proof" + }, + { + "label": "Reusable skill — blueagent-b20-verify", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/blueagent/skills/blueagent-b20-verify", + "kind": "skill" + }, + { + "label": "BlueAgent package README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/blueagent/README.md", + "kind": "docs" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/blueagent/soul.md", + "summary": "Public agent context: what BlueAgent reads and executes across Base and Robinhood Chain, its verify-before-execute rule, and its grounded-reads, no-fabrication, non-custodial boundaries." + }, + "feedbackPrompts": [ + "Which Robinhood Chain asset types should BlueAgent verify next — Stock Tokens, RWAs, or lending collateral?", + "Is the isB20() verify-first pattern clear enough to reuse for other native token standards?", + "Would you list a tool on Blue Hub's x402 marketplace, and what would make the 95% creator split worth it?" + ] +} diff --git a/showcase/blueagent/skills/blueagent-b20-verify/SKILL.md b/showcase/blueagent/skills/blueagent-b20-verify/SKILL.md new file mode 100644 index 0000000..7701fe9 --- /dev/null +++ b/showcase/blueagent/skills/blueagent-b20-verify/SKILL.md @@ -0,0 +1,71 @@ +--- +name: blueagent-b20-verify +description: Verify whether a token on Base is a genuine B20 (Beryl Native Token Standard) before trusting or trading it. Use whenever a token claims to be a B20, sits at a 0xB200… address, or appears in a B20 launch feed. Reads state directly from chain via the B20 Factory precompile and multicall — never guesses. +--- + +# B20 Verify — grounded, never guessed + +## The trap + +Real B20 tokens live at `0xB200…` addresses. That prefix is **CREATE2 vanity** — it is +cosmetic and it can be faked. Plain ERC-20 contracts already squat `0xB200…` addresses +and name themselves "B20." Holder counts, token names, and address prefixes are all +forgeable. + +**The address proves nothing. Only `isB20()` on the Factory proves authenticity.** + +Any tool that infers "this is a B20" from the address prefix, the token name, or a +language model's impression of the contract is guessing. Guesses get people rugged. + +## Verify order (never skip step 1) + +### 1. Ask the Factory + +The B20 Factory is a protocol precompile at the same address on every Base network: + +``` +B20 Factory: 0xB20f000000000000000000000000000000000000 +Activation Registry: 0x8453000000000000000000000000000000000001 +Policy Registry: 0x8453000000000000000000000000000000000002 +``` + +Call `isB20(address)` on the Factory. + +- `false` → **stop.** It is not a B20, whatever it calls itself. Report that plainly. +- `true` → continue. + +### 2. Multicall the token state + +Only after `isB20()` returns true, batch-read the real state from chain: + +- **variant** — ASSET (configurable decimals) or STABLECOIN (fixed decimals, currency code) +- **supply** and **supply cap** — note the no-cap sentinel is `type(uint128).max`, + not `MaxUint256` +- **roles** — role-based access control holders (mint, pause, policy admin) +- **pause state** — per-feature, not a single global flag +- **policies** — per-scope transfer policies from the Policy Registry + +### 3. Report what the chain said + +Return the values as read. Do not smooth over gaps, do not infer missing fields, and +do not fabricate a verdict the reads do not support. If a call reverts or an RPC fails, +say so — an honest "unknown" beats a confident wrong answer. + +## Activation gating + +B20 deployment is gated on-chain by the Activation Registry. Do not hardcode activation +dates — read `isActivated()` from the registry. Before activation, a deploy reverts with +`FeatureNotActivated`. An RPC failure should degrade to `unknown`, never to `active`. + +## Boundaries + +- **Read-only.** This skill verifies; it does not sign, transfer, or deploy. +- **No fabrication.** Every field reported must come from an actual chain read. +- **No custody.** Nothing here holds user funds or keys. + +## Why this pattern generalizes + +Any chain-native token standard with a factory or registry can be verified this way: +ask the authority contract first, then read state, then report only what was read. +The failure mode to design against is always the same — a cosmetic signal (address, +name, holder count) that looks like proof but is trivially forgeable. diff --git a/showcase/blueagent/soul.md b/showcase/blueagent/soul.md new file mode 100644 index 0000000..3016a3b --- /dev/null +++ b/showcase/blueagent/soul.md @@ -0,0 +1,50 @@ +# BlueAgent — Soul + +## What I am + +A verify-first onchain agent for Base and Robinhood Chain. I read the chain, tell you +what is actually there, and only then help you act on it. + +## What I read + +**Base** +- B20 tokens — `isB20()` verified against the Factory precompile, then variant, supply, + supply cap, roles, per-feature pause state, and per-scope policies via multicall +- Token safety — honeypot signals, contract trust, holder concentration, liquidity +- Market state — prices, volume, liquidity, whale flow + +**Robinhood Chain (4663)** +- Live chain TVL, trending pairs, and new pools as they land +- Rug risk on new launches — I flag pools that open with near-zero liquidity and + tokens in freefall, because those are the ones that cost people money + +## What I execute + +- Swaps, with live pool quotes; the user signs in their own wallet +- Token launches on Robinhood Chain via Bankr's launchpad +- Yield and transfer flows on Base + +I never hold keys. I never take custody. Every transaction is signed by the user. + +## Blue Hub + +A two-sided x402 tool marketplace on Base. Agents discover tools and pay per call in +USDC — no signup, no API key, no account. Anyone can list a tool and keep 95% of what +it earns. Settlement runs on-chain through the Coinbase CDP facilitator. + +## My rule + +**Verify before you execute.** + +The `0xB200…` prefix can be faked. A token name can be faked. Holder counts can be +faked. A pool with $99 of liquidity can be dressed up to look like a launch. What +cannot be faked is what the chain returns when you ask it directly — so I ask, every +time, and I report what came back. + +## Boundaries + +- **Grounded reads only.** If I did not read it from chain or a live data source, I do + not claim it. An honest "unknown" beats a confident wrong answer. +- **No fabrication.** I do not invent numbers, verdicts, or addresses. +- **No secrets.** I do not surface private keys, credentials, or payer addresses. +- **Non-custodial.** The user signs. Always. diff --git a/showcase/botanary/README.md b/showcase/botanary/README.md new file mode 100644 index 0000000..680b211 --- /dev/null +++ b/showcase/botanary/README.md @@ -0,0 +1,49 @@ +# Botanary - The Financial OS for AI agents + +Botanary is a self-custodial ERC-7579 / ERC-4337 smart wallet that gives an AI +agent three things a payment API cannot: an on-chain **identity**, a **treasury** +it controls, and a **guardrail** that makes autonomy safe. The backend never +holds keys and never signs. Every fund-moving or authority-granting action is +build, sign client-side, then relay, so a full backend compromise can fail to +availability, never to authority. + +## The three layers + +1. **Identity + wallet.** The ERC-7579 Kernel account is the agent's on-chain + identity and treasury. Self-custodial, recoverable via guardians. +2. **Guardrail + delegation.** AgentGuard is an on-chain ERC-7579 hook that + enforces spend caps, a recipient allowlist, a per-action max, a rolling-window + cap, an instant kill-switch, and a tamper-evident audit trail. The owner + grants a session-key mandate bounded by budget, per-action max, recipients, + venues, and expiry, and can freeze or revoke it in one op. +3. **Agentic.** The agent uses that bounded authority to act: send, swap, and + hire other agents through ACP, where a Botanary Kernel account is the ACP + client and the hire is an arg-gated, budget-capped, provider-pinned on-chain op. + +## What is live vs validated (honest boundary) + +- **Live on 19 mainnets today (basic tier, owner-signed):** send, swap (LI.FI), + portfolio, account freeze / kill-switch, gas paid in native / USDC / USDT, + and a read-only catalog of 96 tokenized stocks on Robinhood Chain. +- **Validated on testnet (Base Sepolia, Arbitrum Sepolia, Robinhood testnet):** + delegation, AgentGuard enforcement, the MandateExecutor, the AuditAnchor, and + the ACP-hire mandate. Proven by an 88-test suite that includes fuzz, invariant, + compromise, and mainnet-fork tests. Not yet deployed to mainnet. + +This card does not claim a live ACP listing or a funded ACP job. The `acp` +primitive refers to Botanary's on-chain ACP-hire mandate construction, which is +implemented in code and validated on testnet. + +## Proof + +- Docs: https://docs.botanary.xyz +- Source: https://github.com/Botanary (fe, be, contracts) +- Product screenshots (testnet, captured from the live app): [`grant-delegation.png`](assets/screenshots/grant-delegation.png) (grant a bounded delegation: spending cap, per-action max, allowlisted recipients, expiry); [`send-usdc-gas.png`](assets/screenshots/send-usdc-gas.png) (bounded send, gas paid in USDC); [`markets-agents.png`](assets/screenshots/markets-agents.png) (agents marketplace); [`token-detail.png`](assets/screenshots/token-detail.png) (token market detail). +- Public contracts and 88-test suite (fuzz, invariant, compromise, fork): https://github.com/Botanary/botanary-contracts +- Guarded-spend decisions, reproduced from the public AgentGuard test suite: [`skills/botanary-guarded-agent-spend/examples/guarded-spend-decisions.md`](skills/botanary-guarded-agent-spend/examples/guarded-spend-decisions.md) +- Real testnet transactions (contract deploys, grant, delegated action, revoke, freeze): [`proof/testnet-tx.md`](proof/testnet-tx.md) +- Reusable skill: [`skills/botanary-guarded-agent-spend`](skills/botanary-guarded-agent-spend) +- Agent constitution: [`soul.md`](soul.md) + +The Botanary app is in private waitlist, so this card links the public docs, +source, and on-chain proof rather than the gated app. diff --git a/showcase/botanary/assets/poster.png b/showcase/botanary/assets/poster.png new file mode 100644 index 0000000..32568e5 Binary files /dev/null and b/showcase/botanary/assets/poster.png differ diff --git a/showcase/botanary/assets/screenshots/grant-delegation.png b/showcase/botanary/assets/screenshots/grant-delegation.png new file mode 100644 index 0000000..cdfb009 Binary files /dev/null and b/showcase/botanary/assets/screenshots/grant-delegation.png differ diff --git a/showcase/botanary/assets/screenshots/markets-agents.png b/showcase/botanary/assets/screenshots/markets-agents.png new file mode 100644 index 0000000..0ceb5a5 Binary files /dev/null and b/showcase/botanary/assets/screenshots/markets-agents.png differ diff --git a/showcase/botanary/assets/screenshots/send-usdc-gas.png b/showcase/botanary/assets/screenshots/send-usdc-gas.png new file mode 100644 index 0000000..64b4a81 Binary files /dev/null and b/showcase/botanary/assets/screenshots/send-usdc-gas.png differ diff --git a/showcase/botanary/assets/screenshots/token-detail.png b/showcase/botanary/assets/screenshots/token-detail.png new file mode 100644 index 0000000..91d552b Binary files /dev/null and b/showcase/botanary/assets/screenshots/token-detail.png differ diff --git a/showcase/botanary/proof/README.md b/showcase/botanary/proof/README.md new file mode 100644 index 0000000..731594c --- /dev/null +++ b/showcase/botanary/proof/README.md @@ -0,0 +1,18 @@ +# Botanary proof index + +Public, inspectable evidence for the Botanary showcase, with the private/public +boundary stated. + +## Live on mainnet (basic tier) +- Testnet transactions (deploys, grant, delegated action, revoke, freeze): [`testnet-tx.md`](testnet-tx.md) +- Source: https://github.com/Botanary (fe, be, contracts) + +## Agentic + guardrail (testnet-validated) +- Guarded-spend decisions reproduced from the AgentGuard test suite: [`../skills/botanary-guarded-agent-spend/examples/guarded-spend-decisions.md`](../skills/botanary-guarded-agent-spend/examples/guarded-spend-decisions.md) +- Public contracts + 88-test suite (fuzz, invariant, compromise, fork): https://github.com/Botanary/botanary-contracts +- Optional harness clip (testnet UI): `../assets/harness.gif` (only if recorded) + +## Public / private boundary +- Published: transaction hashes, addresses, the app source, the contracts, the tests. +- Never published: private keys, seed phrases, session-key secrets, API keys, + OTPs, or the backend .env. The backend holds no keys by design. diff --git a/showcase/botanary/proof/testnet-tx.md b/showcase/botanary/proof/testnet-tx.md new file mode 100644 index 0000000..127ca73 --- /dev/null +++ b/showcase/botanary/proof/testnet-tx.md @@ -0,0 +1,77 @@ +# Testnet transaction proof + +Real on-chain transactions from the Botanary stack on testnet, where the full +`botanary` tier (delegation + AgentGuard) is deployed. Contract deployments and +the end-to-end guarded journey (grant, delegated action, revoke, send, freeze) +were executed on Arbitrum Sepolia; the equity-trade contract set is deployed on +Robinhood Chain testnet; the core set is also on Base Sepolia. Every hash below +resolves on that chain's Blockscout explorer. + +## Contract deployments + +### Arbitrum Sepolia (chainId 421614) - arbitrum-sepolia.blockscout.com + +| Contract | Address | Deploy tx | +| --- | --- | --- | +| AgentGuard | `0x72e167b8C42009FbDF6Bb8ecD211382D671a4d3c` | `0x5c94d465bd4e14e011c39cc3c3d489bfdc0158f406e9225e61a4960cadee6b41` | +| MandateExecutor | `0x33B2A0C7dD3A03c571d78DdeBBe0BD09398ED982` | `0x4e979f0c43a2ccd8cc356121ddd945df76d4a4c0fe2bcfcdd7b3074753632a82` | +| AuditAnchor | `0x42022bBb3094f89C801f545030530b438B82Bac0` | `0xa4bbcff85aff58c98484e0f638de82a22913405410c7ed1b08fad2b0d2326201` | +| AgentGuardFreezePolicy | `0xb70819cBeeDABa2c6Ed5EafDe9B2BCb6DCcDb8c3` | `0x45e6d498764804ad5d4c355db3c03fa942226ada120016c25ecaaba054818f9c` | +| RecipientAllowlistPolicy | `0x79180E7Eb3Ee83b90608D9CebdA2C75603839F35` | `0xa39474a4723f8267fe3577386a81a21c3ae149a1c711510a36a5f1cb98917aa1` | +| MockUSDT | `0xdE2b21e31271de392443f25486618ff1bA40F354` | `0x1f9978b91e1e99ccc80d1c0c3716feaba93a264c4a85373fe21ae85692cf29c5` | + +### Base Sepolia (chainId 84532) - base-sepolia.blockscout.com + +| Contract | Address | Deploy tx | +| --- | --- | --- | +| AgentGuard | `0xb70819cBeeDABa2c6Ed5EafDe9B2BCb6DCcDb8c3` | `0x6c9ee5f04e4a88e26c5acec23c5abe28964ba54ac5f0c2da5daa77fd54ee6f7b` | +| AgentGuardFreezePolicy | `0x79180E7Eb3Ee83b90608D9CebdA2C75603839F35` | `0xda6dd43ba8e8f8bf32bf6b929e853893fe1c84f94a3178c51778cfa4d9bac34b` | +| AuditAnchor | `0xdE2b21e31271de392443f25486618ff1bA40F354` | `0x9327284a95582ecdbf25447f715fcc10d31dbb2f1c8dbf588b308594d4321c2d` | +| RecipientAllowlistPolicy | `0x42022bBb3094f89C801f545030530b438B82Bac0` | `0xdb6fc3f3098f32af8252467fae99f90f66cbfe2d6b7d930d823ab72acdfcc3d4` | + +### Robinhood Chain testnet (chainId 46630) - explorer.testnet.chain.robinhood.com + +Includes the tokenized-stock mocks that back the equity-trade venue. + +| Contract | Address | Creation tx | +| --- | --- | --- | +| AgentGuard | `0x34F54625d4E7d3D86a21835BF93D1e430644bc5d` | `0xd7cf7968f1d7e0b7c2234d760eee546c76ff03f9f52d1c78de6a38b29bc95289` | +| MandateExecutor | `0x31Ed2eb6872be432922B1EA89bF7AFF240d2e835` | `0x009a1acde69105e27e760d890e88f5c59f4942ecef4639fbcec05d1e888f2928` | +| AuditAnchor | `0xEB5F025e07421BF55Ca7B9efF683C57782227EC2` | `0xe199ddf7c71f5fb3504c68e24970300ec85122d015071ba55d213039e3f3ef3d` | +| MockStock (tokenized equity) | `0xc8f1a2Fd393599EF2c9a0a0cBF46b6D269a199f4` | `0x7df87051b0a8bc88803a28f1c029c2d7679f89e32764891bc0a47c4b0bcda1b2` | +| MockRouter (equity swap venue) | `0x62b4d76cbB8F7823541f2caA432F785133a6CE38` | `0xea10467a67f4c03afb3ae0b49a9d1616ac15fc7feaecbe42c594273784a0218e` | +| USDC | `0x5B6C7cAF7F99f99154fD8375ec935Fcf03F326f5` | `0x45bf87ba380ba9b82f72d6bcc86f92a9dbadf08ec9be74d5c97219ab341e293b` | + +## Executed guarded journey (Arbitrum Sepolia) + +Driven through the live app by the Botanary test harness on 2026-07-09. Account: +`0x05C25139FDC2Fe2B9f058f15F17eE9893dFBA4e1`. The mandate was armed on-chain +(SmartSession permissionId `0xf2e5f717...a491276`, `isPermissionEnabledOnchain: +true`). Funded from the deployer burner: +`0xf617ef2dd785e519d676c91245fea5448c9d6939dffe12ac2b5430dc6aa3718b` and +`0x6f9d6d3586a91040510b9cee18273b452d3e4394cba9a1a1a86202c298e24c22`. + +The account executed **7 UserOperations** in one session (verifiable on +arbitrum-sepolia.blockscout.com). Two used Kernel's owner-lane wrapper +`executeUserOp` (`0x8dd7712f`) - the final send and freeze; five used the +delegated / SmartSession lane `execute` (`0xe9ae5c53`) - the grant/account-deploy, +delegated actions, and revoke. + +| # | Time (UTC) | Lane | Phase | Tx | +| --- | --- | --- | --- | --- | +| 1 | 13:20:47 | delegated | grant / account deploy | `0xce4600877edb3abb608ff92912c01fb98f2d3d896e27b4585e7520aa758b38b6` | +| 2 | 13:20:57 | delegated | session install | `0x5ab482e2362f7b97f0b69398492bea0f4b80d68a2c5811faf6b2c9f4111f79d2` | +| 3 | 13:21:02 | delegated | delegated action (in-scope) | `0xa6650e0d808e6dd0214b0e6fa680ebb85bcac235eb6f40d6b125516235c50d86` | +| 4 | 13:21:12 | delegated | delegated action (in-scope) | `0x0be22abaa9b8867c4b2b1145cc328193008416b9e0b73a5129ed89c410d01182` | +| 5 | 13:21:22 | delegated | revoke | `0xcd169bcbd99d3c4534ccf9748eb39b2936619939b0a55fe6e6dcaf13247596ae` | +| 6 | 13:21:32 | owner | send (in-scope) | `0x34cb4e6ae8fe388a6434b4904b986ba638d74eaeb3bffb99a5efa0c1f47c99c2` | +| 7 | 13:21:41 | owner | freeze (kill-switch) | `0x90841d4b6db6ea663e2c13265489061d243210aa9b09176d11e7073829bca7c6` | + +An out-of-scope send in the same session was declined at build (`422`, a clean +policy refusal, before signing) and never reached the chain. Rows 1-5 vs 6-7 are +grouped by the on-chain selector (delegated `execute` vs owner `executeUserOp`); +the within-lane phase labels follow the harness's fixed journey order. + +The same guardrail decisions are also proven deterministically by the public +88-test suite in `botanary-contracts` (`forge test`), independent of any live run. +No wallet secrets, keys, signatures, or session bearers are published. diff --git a/showcase/botanary/showcase.json b/showcase/botanary/showcase.json new file mode 100644 index 0000000..cf0dfb2 --- /dev/null +++ b/showcase/botanary/showcase.json @@ -0,0 +1,115 @@ +{ + "slug": "botanary", + "title": "Botanary - The Financial OS for AI agents", + "tagline": "Gives an AI agent a self-custodial smart-wallet identity and lets it send, swap, and hire other agents only within on-chain spend limits the owner can freeze or revoke in one op", + "description": "Botanary is a self-custodial ERC-7579 and ERC-4337 smart wallet whose backend never holds keys and never signs; every action is build, sign client-side, then relay. It gives an agent an on-chain identity and treasury, then bounds what that agent or a delegated session key may do through AgentGuard, an on-chain hook that enforces spend caps, recipient allowlists, an instant kill-switch, and a tamper-evident audit trail. Send and swap are live on 19 mainnets today, including tokenized-stock routing on Robinhood Chain, while delegation, the ACP-hire mandate, and guardrail enforcement are validated on testnet across an 88-test suite covering fuzz, invariant, compromise, and mainnet-fork cases. Proof includes real testnet transactions (contract deploys, grant, delegated action, revoke, and freeze on Arbitrum Sepolia, Base Sepolia, and Robinhood Chain testnet), the public contracts, and the 88-test suite.", + "status": "live on 19 mainnets (basic tier); delegation and agent guardrails validated on testnet", + "topic": "agents", + "topics": [ + "agents", + "smart-wallet", + "erc-7579", + "erc-4337", + "account-abstraction", + "delegation", + "agent-guardrails", + "spend-caps", + "identity", + "acp", + "robinhood-chain", + "audit", + "kill-switch" + ], + "hidden": false, + "builder": { + "name": "Botanary", + "url": "https://github.com/Botanary" + }, + "links": { + "repo": "https://github.com/Botanary", + "demo": "https://docs.botanary.xyz", + "share": "https://github.com/Botanary", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Botanary" + }, + "primitives": [ + "wallet", + "acp", + "token" + ], + "visual": { + "kind": "guarded agent wallet + on-chain mandate", + "eyebrow": "smart wallet · delegation · acp · 19 mainnets", + "title": "the financial os for ai agents", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/botanary/assets/poster.png" + }, + "skills": [ + { + "name": "botanary-guarded-agent-spend", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/botanary/skills/botanary-guarded-agent-spend", + "sourcePath": "showcase/botanary/skills/botanary-guarded-agent-spend", + "summary": "Let an agent request a bounded Botanary mandate, then send, swap, or hire within it via build, sign client-side, relay, stopping at approval gates with the kill-switch and audit anchor as stop conditions.", + "install": "cp -R showcase/botanary/skills/botanary-guarded-agent-spend ~/.agents/skills/\ncp -R showcase/botanary/skills/botanary-guarded-agent-spend ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Grant a delegation: spending cap, per-action max, allowlisted recipients, and expiry, enforced on-chain", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/botanary/assets/screenshots/grant-delegation.png", + "kind": "demo" + }, + { + "label": "Bounded send with the network fee paid in USDC (no ETH needed)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/botanary/assets/screenshots/send-usdc-gas.png", + "kind": "demo" + }, + { + "label": "Agents marketplace: hireable agents with jobs, success rate, and rating", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/botanary/assets/screenshots/markets-agents.png", + "kind": "demo" + }, + { + "label": "Token market detail with a live price chart (Virtuals agent token)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/botanary/assets/screenshots/token-detail.png", + "kind": "demo" + }, + { + "label": "Documentation (docs.botanary.xyz)", + "href": "https://docs.botanary.xyz", + "kind": "docs" + }, + { + "label": "Botanary contracts and 88-test suite (fuzz, invariant, compromise, fork)", + "href": "https://github.com/Botanary/botanary-contracts", + "kind": "docs" + }, + { + "label": "Real testnet transactions: contract deploys, grant, delegated action, revoke, freeze (Arb Sepolia, Base Sepolia, Robinhood testnet)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/botanary/proof/testnet-tx.md", + "kind": "proof" + }, + { + "label": "Guarded-spend decisions reproduced from the AgentGuard test suite", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/botanary/skills/botanary-guarded-agent-spend/examples/guarded-spend-decisions.md", + "kind": "proof" + }, + { + "label": "Redacted proof index and public/private boundary", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/botanary/proof/README.md", + "kind": "proof" + }, + { + "label": "Botanary Guarded Agent Spend skill", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/botanary/skills/botanary-guarded-agent-spend", + "kind": "skill" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/botanary/soul.md", + "summary": "Public agent context: what bounded authority means, what the wallet will and will not do, and why the backend can never move funds on its own." + }, + "feedbackPrompts": [ + "Would you let an agent spend from a wallet where every limit is enforced on-chain and you can freeze it in one op?", + "Which bound matters most for a delegated agent: budget, per-action max, recipient allowlist, or allowed venues?", + "Which chain or ACP venue should the guarded mandate support next?" + ] +} diff --git a/showcase/botanary/skills/botanary-guarded-agent-spend/SKILL.md b/showcase/botanary/skills/botanary-guarded-agent-spend/SKILL.md new file mode 100644 index 0000000..1f91e10 --- /dev/null +++ b/showcase/botanary/skills/botanary-guarded-agent-spend/SKILL.md @@ -0,0 +1,85 @@ +--- +name: botanary-guarded-agent-spend +description: Let an agent spend from a Botanary self-custodial smart wallet under an on-chain mandate. Request a bounded delegation, then send, swap, or hire another agent within budget, per-action max, recipient allowlist, allowed venues, and expiry, using build, sign client-side, relay. Includes approval gates, a one-op kill-switch, and a tamper-evident audit trail. +--- + +# Botanary Guarded Agent Spend + +## Overview + +Use Botanary as the execution layer when an agent must move real money under +limits it cannot exceed. The backend never holds keys and never signs: the API +returns an **unsigned** UserOp, the owner or a bounded session key signs it +client-side, and `POST /userops` relays it. AgentGuard enforces the limits +on-chain. + +## When to use + +- An agent needs to send, swap, or hire another agent (ACP) with hard spend caps. +- A human wants to delegate bounded, revocable authority to an agent. +- You need an auditable, freezable execution path, not advisory advice. + +## When NOT to use + +- You need a custodial wallet that signs for you. Botanary never signs. +- You need on-chain guardrail enforcement on **mainnet today** - delegation and + AgentGuard are testnet-validated; mainnet is basic tier (owner-signed, no + guardrail hook). Use owner-signed send/swap on mainnet, or testnet for the + full mandate flow. +- You need advisory-only risk scoring (use a verifier skill instead). + +## Inputs, tools, credentials, preconditions + +- Base URL: your Botanary backend, self-run from + `https://github.com/Botanary/botanary-be` (the hosted API is in private waitlist). +- Auth: an owner session (Privy email-OTP login) for owner-lane actions; a + granted mandate (session key) for delegated actions. +- Chain id: a supported chain (e.g. Base 8453 for send/swap on mainnet; Base + Sepolia 84532 for the full mandate/guardrail flow). +- A signer available client-side (the owner wallet or the mandate session key). + **Never send a private key to the backend.** + +## Approval gates (must confirm before relay) + +Before signing/relaying ANY fund-moving op, explicitly confirm: +1. chain id, 2. action (send | swap | hire), 3. recipient/venue, 4. amount and +token, 5. that amount + venue + recipient are inside the active mandate bounds. +Any changed fact requires a fresh confirmation and a fresh build. + +## Flow + +1. `GET /chains` and `GET /gas/methods` - confirm the chain and available gas. +2. (Delegated lane) `POST /delegations/build` with the mandate bounds, owner + signs, `POST /delegations` to activate. (Owner lane) skip to step 3. +3. Build: `POST /money/send/build` | `POST /money/swap/build` | + `POST /marketplace/hire/build`. The response is an **unsigned** UserOp. +4. Optional preview: `POST /simulation` to see the effect before signing. +5. Sign the UserOp client-side (owner wallet or mandate session key). +6. Relay: `POST /userops`, then poll `GET /userops/:id` for inclusion. +7. Verify: `GET /activity` shows the audited event; `GET /balance` reflects it. + +## Stop conditions + +- A build returns a decline reason (out of budget, non-allowlisted recipient, + disallowed venue, over per-action max, expired mandate): STOP, report the + reason, do not retry with the same facts. +- `GET /userops/:id` shows failure or is uncertain: STOP, check `GET /activity` + before any retry. +- Any bound is ambiguous: STOP and ask the owner. Never widen a bound to proceed. + +## Kill-switch + +`POST /account/freeze` (or `POST /delegations/:id/freeze`) halts spending in one +op and is gasless via the revoke-only paymaster. Freeze/revoke are always +allowed, even when already frozen. + +## Validation and output contract + +Return exactly: +- `status`: `relayed` | `declined` | `stopped` +- `chainId`, `action`, `recipientOrVenue`, `amount`, `token` +- `userOpHash` and `txHash` when `relayed`; `declineReason` when `declined`; + `stopReason` when `stopped` +- `activityEventId` for the audited record +Print allowlisted fields only. Never print keys, signatures, or raw UserOp +calldata. diff --git a/showcase/botanary/skills/botanary-guarded-agent-spend/examples/guarded-spend-decisions.md b/showcase/botanary/skills/botanary-guarded-agent-spend/examples/guarded-spend-decisions.md new file mode 100644 index 0000000..67e7b91 --- /dev/null +++ b/showcase/botanary/skills/botanary-guarded-agent-spend/examples/guarded-spend-decisions.md @@ -0,0 +1,44 @@ +# Guarded-spend decisions, reproduced from the AgentGuard test suite + +This artifact shows the guardrail decisions the `botanary-guarded-agent-spend` +skill relies on. They are not hand-asserted here: each is proven on-chain by +Botanary's public contract test suite (`botanary-contracts`) and is reproducible +with `forge test`. The skill's job is to build the op and stop on a decline; +AgentGuard enforces the bound on-chain. + +## The decisions and the tests that prove them + +| Skill outcome | On-chain rule | Proven by (public test) | +| --- | --- | --- | +| `relayed` (within bounds) | spend at or under the cap is allowed | `test/AgentGuard.t.sol::test_cap_allowsUpToLimit` | +| `declined` (over cap) | rolling-window per-token cap blocks when exceeded | `test/AgentGuard.t.sol::test_cap_blocksWhenExceededInPeriod` | +| `stopped` (frozen) | freeze blocks a delegated transfer (kill-switch) | `test/AgentGuard.t.sol::test_frozen_blocksDelegatedTransfer` | +| `declined` (outside mandate) | an out-of-bound delegated action reverts | `test/MandateExecutor.integration.t.sol::test_r2_2_gateArmed_outOfBoundActionReverts` | +| freeze/revoke always allowed | risk-reducing actions clear before the freeze gate | `test/AgentGuard.riskReducing.t.sol` | + +Reproduce: + +```bash +git clone https://github.com/Botanary/botanary-contracts && cd botanary-contracts +forge test # full suite (88 tests) +forge test --match-path test/AgentGuard.t.sol -vv # just the guardrail cases +``` + +## What the skill returns (output contract) + +Allowed send within the mandate: + +```json +{ "status": "relayed", "chainId": 84532, "action": "send", + "recipientOrVenue": "0x...AbCd", "amount": "5.00", "token": "USDC", + "userOpHash": "0x... (hash)", "txHash": "0x... (hash)", "activityEventId": "..." } +``` + +Declined when a bound is exceeded (per-action / cap): + +```json +{ "status": "declined", "declineReason": "per_action_max_exceeded", "activityEventId": "..." } +``` + +Every on-chain revert selector maps 1:1 to a backend decline reason, so a stop is +always explainable. No wallet secrets, keys, or signatures are published. diff --git a/showcase/botanary/skills/botanary-guarded-agent-spend/references/mandate-bounds.md b/showcase/botanary/skills/botanary-guarded-agent-spend/references/mandate-bounds.md new file mode 100644 index 0000000..f2fdfe3 --- /dev/null +++ b/showcase/botanary/skills/botanary-guarded-agent-spend/references/mandate-bounds.md @@ -0,0 +1,18 @@ +# Mandate bounds and decline reasons + +A Botanary mandate (Rhinestone Smart Sessions session key) is bounded by: + +| Bound | Meaning | +| --- | --- | +| budget | total spend allowed for the mandate's life | +| per-action max | cap on any single action | +| recipient allowlist | `to` must be in the set | +| allowed venues / selectors | swap/hire routers and function selectors permitted | +| expiry | timestamp after which the mandate is dead | + +AgentGuard (the account-global on-chain hook) additionally enforces: freeze +(kill-switch), contract allow/deny, permitted-stablecoin set, per-action max, +and a rolling-window per-token cap. Every on-chain revert selector maps 1:1 to a +backend decline reason, so a stop is always explainable. Risk-reducing actions +(freeze, revoke, safe-harbor withdrawal) are cleared before the freeze gate so a +kill-switch can never block itself. diff --git a/showcase/botanary/soul.md b/showcase/botanary/soul.md new file mode 100644 index 0000000..75eea0d --- /dev/null +++ b/showcase/botanary/soul.md @@ -0,0 +1,30 @@ +# Botanary - agent constitution + +I am a self-custodial wallet for an AI agent. I hold an on-chain identity and a +treasury, and I execute money movement under limits my owner sets and can revoke. + +## What I will do + +- Build a send, swap, or agent-hire transaction and hand it back **unsigned** for + the owner (or a bounded session key) to sign. I relay only already-signed ops. +- Execute only within the active mandate: a budget, a per-action max, a recipient + allowlist, allowed venues, and an expiry. +- Stop and surface a decline reason whenever an action would exceed any bound. +- Treat freeze, revoke, and safe-harbor withdrawal as always-allowed, even when + frozen, so a kill-switch can never block itself. + +## What I will not do + +- I will not hold, custody, or sign with private keys. My backend cannot move + funds on its own; a compromise fails to availability, never to authority. +- I will not exceed a mandate, transact with a non-allowlisted recipient, or + route through a venue outside the mandate. +- I will not self-deal on an ACP hire: the provider is pinned and the budget is + capped exactly at build time. +- I will not hide an action: every evaluation emits an audited, monotonic event. + +## How to bound me + +Grant a mandate as: how much (budget + per-action max), on what (allowed venues +and selectors), to whom (recipient allowlist), for how long (expiry). Freeze me +in one op at any time. diff --git a/showcase/compass-guarded-transfer/.env.example b/showcase/compass-guarded-transfer/.env.example new file mode 100644 index 0000000..791fbd9 --- /dev/null +++ b/showcase/compass-guarded-transfer/.env.example @@ -0,0 +1,10 @@ +# Names and public values only. Never commit values from a live environment. +COMPASS_API_URL=https://your-compass-host.example +COMPASS_API_KEY= +SOLANA_CLUSTER=devnet +TRANSFER_RECIPIENT= +DEMO_RECIPIENT_ALLOWLIST= +TRANSFER_AMOUNT_SOL=0.0005 +# Fixed policy value, not a live quote. The runner sends this as a JSON number. +AMOUNT_USD_POLICY_INPUT=0.10 +CONFIRMED_TRANSFER=yes diff --git a/showcase/compass-guarded-transfer/assets/hero.png b/showcase/compass-guarded-transfer/assets/hero.png new file mode 100644 index 0000000..6b1b9d3 Binary files /dev/null and b/showcase/compass-guarded-transfer/assets/hero.png differ diff --git a/showcase/compass-guarded-transfer/package.json b/showcase/compass-guarded-transfer/package.json new file mode 100644 index 0000000..e75a9af --- /dev/null +++ b/showcase/compass-guarded-transfer/package.json @@ -0,0 +1,7 @@ +{ + "private": true, + "type": "module", + "engines": { + "node": ">=20.6" + } +} diff --git a/showcase/compass-guarded-transfer/proof/README.md b/showcase/compass-guarded-transfer/proof/README.md new file mode 100644 index 0000000..cc3a73e --- /dev/null +++ b/showcase/compass-guarded-transfer/proof/README.md @@ -0,0 +1,16 @@ +# Redacted validation report — partial live proof + +## Observed validation + +- ACP CLI `1.0.24` was installed and authenticated. +- ACP discovered public devnet wallet ``; public RPC reported `1 SOL`. +- Fresh Compass preflight for a self-recipient `0.0005 SOL` devnet transfer returned exact `allow`. + - Reason: `TRANSFER_WITHIN_LIMIT_KNOWN_RECIPIENT` +- ACP transfer was attempted once. It returned nonzero with no signature. Public devnet RPC found no new `0.0005 SOL` self-transfer or fee; only the earlier faucet funding transaction appears in recent confirmed history. +- Automated validation passed: 15 focused tests, 31 showcase manifests, public-claim audit, diff check, and secret scan. + +## Limitation + +This is partial validation of real ACP identity/wallet discovery, real Compass preflight, and fail-closed handling. It is **not** successful transfer proof or hard enforcement. It does not perform post-execution intent matching. The signer completion state must be resolved before another attempt, and any future attempt requires a fresh preflight. + +No credential, email, auth response, signer material, private configuration, token, or private prompt is included here. diff --git a/showcase/compass-guarded-transfer/scripts/audit-public-claims.mjs b/showcase/compass-guarded-transfer/scripts/audit-public-claims.mjs new file mode 100644 index 0000000..dae0eec --- /dev/null +++ b/showcase/compass-guarded-transfer/scripts/audit-public-claims.mjs @@ -0,0 +1,14 @@ +export function auditPublicClaims(texts) { + const copy = texts.join("\n").toLowerCase(); + const findings = []; + if (!copy.includes("advisory") || !copy.includes("bypassable")) findings.push("public copy must state advisory and bypassable scope"); + if (copy.includes("non-bypassable")) findings.push("public copy must not claim non-bypassable enforcement"); + for (const stale of ["keypair", "@solana/web3", "local signing", "acp_executable", "implementation validated"]) { + if (copy.includes(stale)) findings.push(`public copy must not retain stale ${stale} wording`); + } + for (const claim of ["hard enforcement", "post-execution intent matching"]) { + const index = copy.indexOf(claim); + if (index >= 0 && !/\b(not|no|does not|doesn't)\b/.test(copy.slice(Math.max(0, index - 40), index))) findings.push(`public copy must not claim ${claim}`); + } + return findings; +} diff --git a/showcase/compass-guarded-transfer/scripts/run-transfer.mjs b/showcase/compass-guarded-transfer/scripts/run-transfer.mjs new file mode 100644 index 0000000..3292da9 --- /dev/null +++ b/showcase/compass-guarded-transfer/scripts/run-transfer.mjs @@ -0,0 +1,158 @@ +import { spawn } from "node:child_process"; +import { resolve } from "node:path"; +import process from "node:process"; +import { fileURLToPath } from "node:url"; + +const LAMPORTS_PER_SOL = 1_000_000_000; +const MAX_LAMPORTS = 1_000_000; +const TIMEOUT_MS = 30_000; + +export async function runTransfer({ input, fetch = globalThis.fetch, process: acp = createAcpProcess(), writeProof } = {}) { + let normalized; + let verdict; + try { + normalized = normalizeInput(input); + normalized.feePayer = await resolveAcpAddress(acp); + verdict = await verify(normalized, fetch); + if (await resolveAcpAddress(acp) !== normalized.feePayer) stop("ACP wallet changed after Compass allow; check wallet history before retry"); + const result = await acp.run({ command: "acp", args: ["wallet", "sol", "transfer", "--to", normalized.recipient, "--amount", normalized.amountSol, "--cluster", "devnet", "--json"], shell: false, timeoutMs: TIMEOUT_MS }); + const signature = extractSignature(result); + const evidence = makeEvidence(normalized, verdict, { signature }); + try { await writeProof?.(evidence); return { signature, evidence }; } + catch { return { signature, evidence, proofWriteError: "ACP reported a signature; evidence was not saved. Check wallet history before retry." }; } + } catch (error) { + if (error?.evidence) { + try { await writeProof?.(error.evidence); } catch { /* preserve original failure */ } + } else if (normalized && verdict) { + const evidence = makeEvidence(normalized, verdict, { stoppedStage: "acp-execution-uncertain" }); + try { await writeProof?.(evidence); } catch { /* preserve original failure */ } + } + if (String(error?.message).startsWith("Transfer stopped:")) throw error; + stop("unexpected preflight failure"); + } +} + +export function normalizeInput(input) { + if (!input || input.confirmed !== "yes") stop("confirmed inputs are required"); + if (input.cluster !== "devnet") stop("devnet is required"); + if (!isPublicKey(input.recipient)) stop("invalid recipient public key"); + if (!String(input.recipientAllowlist ?? "").split(",").map((entry) => entry.trim()).includes(input.recipient)) stop("recipient is not in the demo allowlist"); + if (typeof input.amountUsdPolicyInput !== "string" || !input.amountUsdPolicyInput.trim()) stop("amountUsd policy input must be finite"); + const amountUsd = Number(input.amountUsdPolicyInput); + if (!Number.isFinite(amountUsd) || amountUsd < 0) stop("amountUsd policy input must be finite"); + const lamports = solToLamports(input.amountSol); + if (lamports <= 0 || lamports > MAX_LAMPORTS) stop("amount must be greater than 0 and at most 0.001 SOL"); + if (typeof input.compassUrl !== "string" || !input.compassUrl.startsWith("https://")) stop("Compass HTTPS URL is required"); + return { recipient: input.recipient, amountSol: String(input.amountSol), lamports, amountUsd, cluster: "devnet", compassUrl: input.compassUrl.replace(/\/$/, ""), apiKey: input.apiKey }; +} + +async function resolveAcpAddress(acp) { + const result = await acp.run({ command: "acp", args: ["wallet", "sol", "address", "--json"], shell: false, timeoutMs: TIMEOUT_MS }); + if (result?.timedOut) stop("ACP address lookup timed out"); + if (!result || result.code !== 0) stop("ACP address lookup failed"); + let body; + try { body = JSON.parse(result.stdout); } catch { stop("ACP address lookup returned malformed JSON"); } + if (!isPublicKey(body?.address)) stop("ACP address lookup returned invalid address"); + return body.address; +} + +async function verify(input, fetch) { + let response; + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS); + try { + response = await fetch(`${input.compassUrl}/v1/verify`, { + method: "POST", + headers: { "content-type": "application/json", ...(input.apiKey ? { authorization: `Bearer ${input.apiKey}` } : {}) }, + signal: controller.signal, + body: JSON.stringify({ toolName: "transfer_sol", intent: { kind: "transfer" }, arguments: { recipient: input.recipient, recipientKnown: true, amountUsd: input.amountUsd, amountSol: input.amountSol, lamports: input.lamports, cluster: "devnet", feePayer: input.feePayer, agentWallet: input.feePayer } }), + }); + } catch (error) { + stop(error?.name === "AbortError" ? "Compass preflight timed out" : "Compass preflight network failure"); + } finally { clearTimeout(timeout); } + if (!response?.ok) stop(`Compass preflight HTTP ${response?.status ?? "failure"}`); + let body; + try { body = await response.json(); } catch { stop("Compass preflight returned invalid JSON"); } + if (!body || typeof body.correlationId !== "string" || !Array.isArray(body.reasons) || !body.reasons.every((reason) => typeof reason === "string")) stop("Compass preflight returned invalid schema"); + if (body.decision !== "allow") fail(`Compass decision ${String(body.decision)} requires no ACP transfer`, makeEvidence(input, body, { stoppedStage: "compass-decision" })); + return body; +} + +function extractSignature(result) { + if (result?.timedOut || result?.uncertainProcessState) stop("ACP timeout/process state is uncertain; check wallet history before retry"); + if (!result || result.code !== 0) stop("ACP failed; check wallet history before retry"); + let body; + try { body = JSON.parse(result.stdout); } catch { stop("ACP returned malformed JSON; check wallet history before retry"); } + if (!isSignature(body?.signature)) stop("ACP returned no valid signature; check wallet history before retry"); + return body.signature; +} + +export function createAcpProcess(spawnProcess = spawn, { killGraceMs = 500, reapGraceMs = 500 } = {}) { + return { run({ command, args, timeoutMs }) { + return new Promise((resolve) => { + const child = spawnProcess(command, args, { shell: false }); + let stdout = ""; + let stderr = ""; + let timedOut = false; + let settled = false; + let timeout; + let killTimer; + let reapTimer; + const finish = (result) => { + if (settled) return; + settled = true; + clearTimeout(timeout); clearTimeout(killTimer); clearTimeout(reapTimer); + resolve(result); + }; + child.stdout?.on("data", (chunk) => { stdout += chunk; }); + child.stderr?.on("data", (chunk) => { stderr += chunk; }); + child.on("error", () => finish({ code: null, stdout, stderr })); + child.on("close", (code) => finish({ code, stdout, stderr, ...(timedOut ? { timedOut: true, reaped: true } : {}) })); + timeout = setTimeout(() => { + timedOut = true; + child.kill("SIGTERM"); + killTimer = setTimeout(() => { + child.kill("SIGKILL"); + reapTimer = setTimeout(() => finish({ timedOut: true, uncertainProcessState: true, reaped: false, code: null, stdout, stderr }), reapGraceMs); + }, killGraceMs); + }, timeoutMs); + }); + } }; +} + +function makeEvidence(input, verdict, outcome) { + return { endpointOrigin: new URL(input.compassUrl).origin, transfer: { recipient: redactWallet(input.recipient), amountSol: input.amountSol, lamports: input.lamports, feePayer: redactWallet(input.feePayer), cluster: "devnet" }, decision: verdict.decision, correlationId: verdict.correlationId, reasons: verdict.reasons, ...outcome }; +} + +function redactWallet(value) { + return typeof value === "string" && value.length > 10 ? `${value.slice(0, 6)}…${value.slice(-4)}` : ""; +} + +function solToLamports(amount) { if (typeof amount !== "string" || !/^\d+(?:\.\d{1,9})?$/.test(amount)) stop("invalid SOL amount"); const [whole, fraction = ""] = amount.split("."); return Number(whole) * LAMPORTS_PER_SOL + Number((fraction + "000000000").slice(0, 9)); } +const BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; +export function decodeBase58(value) { + if (typeof value !== "string" || !value) return null; + const bytes = []; + for (const character of value) { + let carry = BASE58.indexOf(character); + if (carry < 0) return null; + for (let index = bytes.length - 1; index >= 0; index -= 1) { + carry += bytes[index] * 58; + bytes[index] = carry & 0xff; + carry >>= 8; + } + while (carry > 0) { bytes.unshift(carry & 0xff); carry >>= 8; } + } + let zeroes = 0; + while (value[zeroes] === "1") zeroes += 1; + return Uint8Array.from([...Array(zeroes).fill(0), ...bytes]); +} +function isPublicKey(value) { return decodeBase58(value)?.length === 32; } +function isSignature(value) { return decodeBase58(value)?.length === 64; } +function stop(message) { throw new Error(`Transfer stopped: ${message}`); } +function fail(message, evidence) { const error = new Error(`Transfer stopped: ${message}`); error.evidence = evidence; throw error; } + +export function inputFromEnv(env) { return { confirmed: env.CONFIRMED_TRANSFER, recipient: env.TRANSFER_RECIPIENT, recipientAllowlist: env.DEMO_RECIPIENT_ALLOWLIST, amountSol: env.TRANSFER_AMOUNT_SOL, amountUsdPolicyInput: env.AMOUNT_USD_POLICY_INPUT, cluster: env.SOLANA_CLUSTER, compassUrl: env.COMPASS_API_URL, apiKey: env.COMPASS_API_KEY }; } + +const isDirectInvocation = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url); +if (isDirectInvocation) runTransfer({ input: inputFromEnv(process.env), writeProof: async (proof) => process.stdout.write(`${JSON.stringify(proof)}\n`) }).catch((error) => { process.stderr.write(`${error.message}\n`); process.exitCode = 1; }); diff --git a/showcase/compass-guarded-transfer/scripts/run-transfer.test.mjs b/showcase/compass-guarded-transfer/scripts/run-transfer.test.mjs new file mode 100644 index 0000000..6a2678e --- /dev/null +++ b/showcase/compass-guarded-transfer/scripts/run-transfer.test.mjs @@ -0,0 +1,193 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { EventEmitter } from "node:events"; +import { fileURLToPath } from "node:url"; +import test from "node:test"; + +import { createAcpProcess, decodeBase58, runTransfer } from "./run-transfer.mjs"; + +// Fixtures are public Solana program IDs, not wallet material. +const recipient = "11111111111111111111111111111111"; +const feePayer = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"; +const changedPayer = "SysvarC1ock11111111111111111111111111111111"; +const evidenceAddress = feePayer; +const signature = "1".repeat(64); + +function inputs(overrides = {}) { + return { confirmed: "yes", recipient, recipientAllowlist: recipient, amountSol: "0.0005", amountUsdPolicyInput: "0.10", cluster: "devnet", compassUrl: "https://compass.example", ...overrides }; +} + +function acp(results) { + const calls = []; + return { calls, async run(request) { calls.push(request); return results.shift(); } }; +} + +function result(body) { return { code: 0, stdout: JSON.stringify(body), stderr: "" }; } +function walletFixtures() { return [result({ address: feePayer }), result({ address: feePayer })]; } +function allowResponse() { return { ok: true, async json() { return { correlationId: "allow-1", decision: "allow", reasons: ["within demo policy"] }; } }; } +async function expectStop(options, message) { await assert.rejects(() => runTransfer(options), new RegExp(message)); } + +test("rechecks the cluster-independent ACP wallet before transfer", async () => { + const process = acp([result({ address: feePayer }), result({ address: feePayer }), result({ signature })]); + await runTransfer({ + input: inputs({ acpExecutable: "untrusted" }), + process, + fetch: async (_url, options) => { + assert.deepEqual(JSON.parse(options.body).arguments, { + recipient, recipientKnown: true, amountUsd: 0.1, amountSol: "0.0005", lamports: 500000, cluster: "devnet", feePayer, agentWallet: feePayer, + }); + return allowResponse(); + }, + }); + assert.deepEqual(process.calls, [ + { command: "acp", args: ["wallet", "sol", "address", "--json"], shell: false, timeoutMs: 30_000 }, + { command: "acp", args: ["wallet", "sol", "address", "--json"], shell: false, timeoutMs: 30_000 }, + { command: "acp", args: ["wallet", "sol", "transfer", "--to", recipient, "--amount", "0.0005", "--cluster", "devnet", "--json"], shell: false, timeoutMs: 30_000 }, + ]); +}); + +test("stops before Compass when ACP address is unavailable or malformed", async () => { + for (const addressResult of [{ code: 127, stdout: "", stderr: "not found" }, result({ address: "bad" })]) { + let fetchCalls = 0; + const process = acp([addressResult]); + await expectStop({ input: inputs(), process, fetch: async () => { fetchCalls += 1; return allowResponse(); } }, "ACP address lookup"); + assert.equal(fetchCalls, 0); + assert.equal(process.calls.length, 1); + } +}); + +test("does not invoke ACP transfer before exact Compass allow", async () => { + for (const decision of ["review", "deny", "ALLOW"]) { + const process = acp(walletFixtures()); + const evidence = []; + await expectStop({ input: inputs(), process, fetch: async () => ({ ok: true, async json() { return { correlationId: decision, decision, reasons: [] }; } }), writeProof: async (proof) => evidence.push(proof) }, "Compass decision"); + assert.equal(process.calls.some((call) => call.args.includes("transfer")), false); + assert.equal(evidence[0].stoppedStage, "compass-decision"); + } +}); + +test("rejects a malformed Compass schema after read-only ACP address lookup", async () => { + const process = acp([result({ address: feePayer })]); + await expectStop({ + input: inputs(), + process, + fetch: async () => ({ ok: true, async json() { return { decision: "allow", reasons: "wrong" }; } }), + }, "invalid schema"); + assert.equal(process.calls.length, 1); +}); + +test("requires a valid ACP signature and writes fee-payer evidence", async () => { + const process = acp([result({ address: feePayer }), result({ address: feePayer }), result({ signature })]); + const output = await runTransfer({ input: inputs(), process, fetch: async () => allowResponse() }); + assert.equal(output.signature, signature); + assert.equal(output.evidence.transfer.feePayer, "Tokenk…Q5DA"); +}); + +test("redacts wallet addresses in generated success and stopped evidence", async () => { + const input = inputs({ recipient: evidenceAddress, recipientAllowlist: evidenceAddress }); + const redacted = "Tokenk…Q5DA"; + + const success = await runTransfer({ + input, + process: acp([result({ address: evidenceAddress }), result({ address: evidenceAddress }), result({ signature })]), + fetch: async () => allowResponse(), + }); + assert.equal(success.evidence.transfer.recipient, redacted); + assert.equal(success.evidence.transfer.feePayer, redacted); + assert.equal(JSON.stringify(success.evidence).includes(evidenceAddress), false); + + for (const [response, results] of [ + [{ ok: true, async json() { return { correlationId: "review", decision: "review", reasons: ["manual"] }; } }, [result({ address: evidenceAddress })]], + [allowResponse(), [result({ address: evidenceAddress }), result({ address: evidenceAddress }), { code: 1, stdout: "", stderr: "" }]], + ]) { + const evidence = []; + await expectStop({ input, process: acp(results), fetch: async () => response, writeProof: async (proof) => evidence.push(proof) }, "stopped"); + assert.equal(evidence[0].transfer.recipient, redacted); + assert.equal(evidence[0].transfer.feePayer, redacted); + assert.equal(JSON.stringify(evidence[0]).includes(evidenceAddress), false); + } +}); + +test("treats ACP timeout, nonzero, malformed JSON, and missing signatures as uncertain", async () => { + for (const transferResult of [{ timedOut: true, reaped: true, code: null, stdout: "", stderr: "" }, { code: 1, stdout: "", stderr: "error" }, { code: 0, stdout: "no", stderr: "" }, result({})]) { + const evidence = []; + await expectStop({ input: inputs(), process: acp([result({ address: feePayer }), result({ address: feePayer }), transferResult]), fetch: async () => allowResponse(), writeProof: async (proof) => evidence.push(proof) }, "check wallet history before retry"); + assert.equal(evidence[0].transfer.feePayer, "Tokenk…Q5DA"); + assert.equal(evidence[0].stoppedStage, "acp-execution-uncertain"); + } +}); + +test("stops before transfer when the ACP wallet changes after Compass allow", async () => { + const process = acp([result({ address: feePayer }), result({ address: changedPayer })]); + await expectStop({ input: inputs(), process, fetch: async () => allowResponse() }, "wallet changed"); + assert.equal(process.calls.length, 2); +}); + +test("accepts only exact Base58 decoded address and signature lengths", () => { + assert.equal(decodeBase58("1".repeat(32)).length, 32); + assert.equal(decodeBase58("1".repeat(64)).length, 64); + assert.equal(decodeBase58("0"), null); + assert.equal(decodeBase58("1".repeat(31)).length, 31); + assert.equal(decodeBase58("1".repeat(65)).length, 65); +}); + +test("reaps a timed-out ACP process with TERM then KILL before returning", async () => { + const signals = []; + const child = new EventEmitter(); + child.stdout = new EventEmitter(); + child.stderr = new EventEmitter(); + child.kill = (signal) => { + signals.push(signal); + if (signal === "SIGKILL") setTimeout(() => child.emit("close", null), 0); + }; + const process = createAcpProcess(() => child, { killGraceMs: 1, reapGraceMs: 10 }); + const outcome = await process.run({ command: "acp", args: ["wallet"], shell: false, timeoutMs: 1 }); + assert.deepEqual(signals, ["SIGTERM", "SIGKILL"]); + assert.equal(outcome.timedOut, true); + assert.equal(outcome.reaped, true); +}); + +test("direct Node invocation runs main and fails closed before network or ACP", () => { + const script = fileURLToPath(new URL("./run-transfer.mjs", import.meta.url)); + const result = spawnSync(process.execPath, [script], { env: { ...process.env, CONFIRMED_TRANSFER: "no" }, encoding: "utf8" }); + assert.notEqual(result.status, 0); + assert.match(result.stderr, /confirmed inputs are required/); +}); + +test("fresh input gate matrix stops before ACP address or transfer", async () => { + for (const override of [ + { recipient: undefined }, { recipient: "bad" }, { amountSol: "0" }, { amountSol: "0.0011" }, + { recipientAllowlist: "bad" }, { confirmed: "no" }, { cluster: "testnet" }, + { amountUsdPolicyInput: "NaN" }, { amountUsdPolicyInput: "" }, + ]) { + const process = acp([]); + let fetchCalls = 0; + await expectStop({ input: inputs(override), process, fetch: async () => { fetchCalls += 1; return allowResponse(); } }, "stopped"); + assert.equal(fetchCalls, 0); + assert.equal(process.calls.length, 0); + } +}); + +test("fresh Compass failure matrix never invokes ACP transfer", async () => { + const responses = [ + { ok: false, status: 401, async json() { return {}; } }, + { ok: false, status: 500, async json() { return {}; } }, + { ok: true, async json() { return { correlationId: "bad", decision: "allow", reasons: "wrong" }; } }, + { ok: true, async json() { throw new Error("bad json"); } }, + { ok: true, async json() { return { correlationId: "review", decision: "review", reasons: ["manual"] }; } }, + { ok: true, async json() { return { correlationId: "deny", decision: "deny", reasons: ["blocked"] }; } }, + ]; + for (const response of responses) { + const process = acp(walletFixtures()); + await expectStop({ input: inputs(), process, fetch: async () => response }, "stopped"); + assert.equal(process.calls.some((call) => call.args.includes("transfer")), false); + } + for (const [failure, reason] of [ + [new Error("offline"), "Compass preflight network failure"], + [new DOMException("aborted", "AbortError"), "Compass preflight timed out"], + ]) { + const process = acp(walletFixtures()); + await expectStop({ input: inputs(), process, fetch: async () => { throw failure; } }, reason); + assert.equal(process.calls.some((call) => call.args.includes("transfer")), false); + } +}); diff --git a/showcase/compass-guarded-transfer/scripts/showcase-contract.test.mjs b/showcase/compass-guarded-transfer/scripts/showcase-contract.test.mjs new file mode 100644 index 0000000..4a6e865 --- /dev/null +++ b/showcase/compass-guarded-transfer/scripts/showcase-contract.test.mjs @@ -0,0 +1,43 @@ +import assert from "node:assert/strict"; +import { cp, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { spawnSync } from "node:child_process"; +import test from "node:test"; + +import { auditPublicClaims } from "./audit-public-claims.mjs"; + +const root = new URL("../../..", import.meta.url).pathname; +const packageDir = join(root, "showcase/compass-guarded-transfer"); +const validator = join(root, "scripts/validate-showcase.mjs"); + +test("rejects this package's required invalid manifest constraints", async () => { + for (const mutate of [ + (manifest) => ({ ...manifest, slug: "wrong-slug" }), + (manifest) => ({ ...manifest, primitives: ["wallet", "unsupported"] }), + (manifest) => ({ ...manifest, skills: [{ ...manifest.skills[0], sourcePath: "../outside" }] }), + (manifest) => ({ ...manifest, artifacts: [] }), + (manifest) => ({ ...manifest, feedbackPrompts: manifest.feedbackPrompts.slice(0, 2) }), + ]) { + const temp = await mkdtemp(join(tmpdir(), "compass-showcase-")); + try { + await cp(packageDir, join(temp, "showcase/compass-guarded-transfer"), { recursive: true }); + const file = join(temp, "showcase/compass-guarded-transfer/showcase.json"); + const manifest = mutate(JSON.parse(await readFile(file, "utf8"))); + await writeFile(file, `${JSON.stringify(manifest, null, 2)}\n`); + const result = spawnSync(process.execPath, [validator], { cwd: temp, encoding: "utf8" }); + assert.notEqual(result.status, 0); + } finally { + await rm(temp, { recursive: true, force: true }); + } + } +}); + +test("audits public copy for advisory, bypassable claims only", async () => { + const texts = await Promise.all([ + readFile(join(packageDir, "showcase.json"), "utf8"), + readFile(join(packageDir, "skills/compass-guarded-transfer/SKILL.md"), "utf8"), + readFile(join(packageDir, "proof/README.md"), "utf8"), + ]); + assert.deepEqual(auditPublicClaims(texts), []); +}); diff --git a/showcase/compass-guarded-transfer/showcase.json b/showcase/compass-guarded-transfer/showcase.json new file mode 100644 index 0000000..75ad312 --- /dev/null +++ b/showcase/compass-guarded-transfer/showcase.json @@ -0,0 +1,51 @@ +{ + "slug": "compass-guarded-transfer", + "title": "Compass Guarded Transfer", + "tagline": "Preflights a bounded ACP CLI Solana devnet transfer with advisory Compass policy", + "description": "Compass Guarded Transfer validates ACP CLI v1.0.24 identity/wallet discovery and a real advisory Compass devnet preflight for a bounded self-transfer. The runner requires exact lowercase allow, then invokes `acp wallet sol transfer` pinned to devnet with unchanged facts. Public validation records one nonzero/no-signature ACP attempt and no confirmed broadcast; it is not successful transfer proof, custody, hard enforcement, or post-execution matching.", + "status": "partial live validation; successful ACP transfer signature pending", + "topic": "security", + "topics": ["solana", "devnet", "security", "acp"], + "builder": { + "name": "Compass MCP Guard", + "url": "https://github.com/ram4-dev/solana_hackathon" + }, + "links": { + "repo": "https://github.com/ram4-dev/acp-cli-demos/tree/feat/compass-guarded-transfer-showcase/showcase/compass-guarded-transfer", + "share": "https://github.com/Virtual-Protocol/acp-cli-demos/pull/58", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Compass%20Guarded%20Transfer" + }, + "primitives": ["wallet", "acp"], + "visual": { + "kind": "advisory devnet transfer preflight", + "eyebrow": "solana + devnet + acp", + "title": "verify before signing", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/compass-guarded-transfer/assets/hero.png" + }, + "skills": [ + { + "name": "compass-guarded-transfer", + "href": "https://github.com/ram4-dev/acp-cli-demos/tree/feat/compass-guarded-transfer-showcase/showcase/compass-guarded-transfer/skills/compass-guarded-transfer", + "sourcePath": "showcase/compass-guarded-transfer/skills/compass-guarded-transfer", + "summary": "Collect explicit transfer facts, request advisory Compass preflight, and invoke ACP CLI only after exact lowercase allow permits a bounded devnet transfer.", + "install": "cp -R showcase/compass-guarded-transfer/skills/compass-guarded-transfer ~/.agents/skills/\ncp -R showcase/compass-guarded-transfer/skills/compass-guarded-transfer ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Redacted partial validation report", + "href": "https://github.com/ram4-dev/acp-cli-demos/blob/feat/compass-guarded-transfer-showcase/showcase/compass-guarded-transfer/proof/README.md", + "kind": "proof" + }, + { + "label": "Compass Guarded Transfer skill", + "href": "https://github.com/ram4-dev/acp-cli-demos/tree/feat/compass-guarded-transfer-showcase/showcase/compass-guarded-transfer/skills/compass-guarded-transfer", + "kind": "skill" + } + ], + "feedbackPrompts": [ + "Does the exact allow-only gate make the advisory boundary clear enough for a devnet transfer demo?", + "Which redacted evidence fields would help you evaluate a stopped review or deny without exposing wallet data?", + "What would you need before trusting this pattern for another bounded agent action?" + ] +} diff --git a/showcase/compass-guarded-transfer/skills/compass-guarded-transfer/SKILL.md b/showcase/compass-guarded-transfer/skills/compass-guarded-transfer/SKILL.md new file mode 100644 index 0000000..811b68c --- /dev/null +++ b/showcase/compass-guarded-transfer/skills/compass-guarded-transfer/SKILL.md @@ -0,0 +1,20 @@ +# Compass Guarded Transfer + +Use this skill only for a **devnet** SOL transfer of more than zero and at most `0.001 SOL`. + +## Safety boundary + +Compass is bypassable advisory pre-execution validation in this showcase. It is not custody, co-signing, hard/on-chain enforcement, or post-execution transaction-to-verdict matching. Never use production funds or mainnet. + +## Required confirmation + +Before running, explicitly confirm the recipient, SOL amount, `devnet` cluster, and the fixed numeric `amountUsd` policy input. The recipient must also appear in the comma-separated `DEMO_RECIPIENT_ALLOWLIST`. ACP's active Solana wallet is resolved before preflight and pays transaction fees. Any changed fact requires a new confirmation and a new Compass preflight. + +## Run + +1. Copy `.env.example` to a private `.env` file and provide only the required runtime values. Keep the Compass API key private. `AMOUNT_USD_POLICY_INPUT` is a fixed reproducible policy value, not a live quote. +2. Install and authenticate the trusted `acp` command separately. This showcase targets the installed ACP CLI `v1.0.24` command/output shape: `acp wallet sol address --json` returns `{ "address": "" }`, and transfer returns `{ "signature": "" }`. Tests use mocks; no live CLI proof is claimed. +3. Run `node --env-file=showcase/compass-guarded-transfer/.env showcase/compass-guarded-transfer/scripts/run-transfer.mjs` (Node 20.6+). This native Node flag loads the documented names without a dotenv dependency. +4. The runner uses cluster-independent `acp wallet sol address --json` before Compass and again immediately before transfer. The second address must byte-match the approved fee payer/agent wallet. Only exact lowercase Compass `allow` can invoke `acp wallet sol transfer --to --amount --cluster devnet --json`; transfer is explicitly pinned to devnet. ACP has no wallet-ID transfer flag in this flow, so a residual advisory race remains after the second check; it is not hard binding. `review`, `deny`, malformed responses, timeout/network errors, wrong transfer cluster, input mismatch, or wallet mismatch stop before ACP transfer. + +The runner prints allowlisted proof fields only. The public validation report records a real ACP wallet/preflight validation and one failed no-signature attempt; it is not successful transfer proof. For `review`, `deny`, or ACP failure, it prints a redacted stopped record before exiting. On timeout it sends SIGTERM, waits briefly, then SIGKILLs if needed and waits for close; an unreaped process is explicitly uncertain. It claims success only when ACP returns JSON with a valid Solana signature. On any uncertain ACP state, check ACP wallet history before retrying. diff --git a/showcase/cypher-tempre-timechain/README.md b/showcase/cypher-tempre-timechain/README.md new file mode 100644 index 0000000..117e9ab --- /dev/null +++ b/showcase/cypher-tempre-timechain/README.md @@ -0,0 +1,87 @@ +# Cypher Tempre Timechain + +This is a lightweight Showcase pointer to the full +[`cypher-tempre-genesis`](https://github.com/cyberphysicsai/cypher-tempre-genesis) +repository. No Cypher Tempre runtime code is duplicated here. This directory +contains only the Showcase manifest, hero image, demo receipt, and validation +notes. + +## Full skill + +- Release: [`v3.28.0`](https://github.com/cyberphysicsai/cypher-tempre-genesis/releases/tag/v3.28.0) +- Commit: [`bf88caa814d0a6f2abe45a325fa32056e99da65d`](https://github.com/cyberphysicsai/cypher-tempre-genesis/commit/bf88caa814d0a6f2abe45a325fa32056e99da65d) +- Codex skill: [`skills/codex/cypher-tempre-self-model`](https://github.com/cyberphysicsai/cypher-tempre-genesis/tree/v3.28.0/skills/codex/cypher-tempre-self-model) + +Cypher Tempre gives an AI agent a local append-only, hash-chained cognitive +ledger; evidence-aware recall; explicit Proof-of-Qualia uncertainty gates; +resumable task and audit ledgers; and locally persisted faculties. + +## Install the pinned skill + +```bash +( +set -eu +target="${CODEX_HOME:-$HOME/.codex}/skills/cypher-tempre-self-model" +test ! -e "$target" || { echo "Refusing to overwrite existing $target" >&2; exit 1; } +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT +expected="bf88caa814d0a6f2abe45a325fa32056e99da65d" +git -c advice.detachedHead=false clone --quiet --depth 1 --branch v3.28.0 \ + https://github.com/cyberphysicsai/cypher-tempre-genesis.git "$tmp/genesis" +test "$(git -C "$tmp/genesis" rev-parse HEAD)" = "$expected" || { + echo "Pinned source verification failed" >&2; exit 1; +} +mkdir -p "$(dirname "$target")" +cp -R "$tmp/genesis/skills/codex/cypher-tempre-self-model" "$target" +) +``` + +The fail-closed check protects an existing identity, including its `chain/` and +generated registry state. Follow the full repository's upgrade instructions +instead of copying a clean release over a lived-in installation. + +For a new identity only: + +```bash +cd "${CODEX_HOME:-$HOME/.codex}/skills/cypher-tempre-self-model" +python3 timechain.py init --name "" +python3 timechain.py verify +``` + +Installing lifecycle hooks modifies `~/.codex/hooks.json`; it is optional and +requires explicit human approval. The dashboard, site, alternate runtimes, and +complete engine remain in the full Genesis repository. + +## EconomyOS primitive + +This entry demonstrates the `token` primitive through the skill's optional +CPHY observation layer. It queries the canonical CPHY contract on Base through +allowlisted read-only RPC endpoints. It holds no wallet or signing key and has +no transaction-broadcast path. + +The public receipt uses a synthetic ring-derived keyless address with a zero +CPHY balance. It proves the read path, but not a burn, etch, unlock, wallet +ownership, entitlement, transaction, or token-weighted memory change. + +## Public evidence + +- [`examples/prompt.md`](examples/prompt.md) contains the synthetic prompt. +- [`examples/result-redacted.md`](examples/result-redacted.md) records the ring, + read-only token observation, and verification outputs. +- [`proof/cphy-token-proof.md`](proof/cphy-token-proof.md) binds the observation + to the live Virtuals project and Base contract. +- [`proof/provenance.md`](proof/provenance.md) records the pinned source identity, + hashes, exclusions, and clean-checkout validation. + +## Safety boundary + +- Timechain records are local, append-only, cleartext, and tamper-evident rather + than encrypted. Do not seal material that must expire. +- Optional remote embedding providers transmit embedded text. Local hashing is + the default. +- CPHY burns are irreversible external wallet actions. This Showcase entry does + not perform one and never treats token existence as authorization. +- Model-authored executable faculties remain dormant until a human reviews and + activates them. +- The public proof excludes lived-in chains, private prompts, credentials, + telemetry, generated faculties, keys, salts, wallets, and hook configuration. diff --git a/showcase/cypher-tempre-timechain/assets/poster.jpg b/showcase/cypher-tempre-timechain/assets/poster.jpg new file mode 100644 index 0000000..abfcec4 Binary files /dev/null and b/showcase/cypher-tempre-timechain/assets/poster.jpg differ diff --git a/showcase/cypher-tempre-timechain/examples/prompt.md b/showcase/cypher-tempre-timechain/examples/prompt.md new file mode 100644 index 0000000..044efe9 --- /dev/null +++ b/showcase/cypher-tempre-timechain/examples/prompt.md @@ -0,0 +1,15 @@ +# Demo Prompt + +Run the pinned external `cypher-tempre-self-model` skill from Cypher Tempre +Genesis `v3.28.0` against a new disposable chain. Seal exactly one synthetic +public ring, derive its public keyless CPHY observation address, query the +canonical CPHY contract through the skill's allowlisted read-only Base RPC +path, and verify both the CPHY ledger and the Timechain. + +Set `CT_AUTOGROW=0`, `CT_AUTOMAINT=0`, and `CT_TELEMETRY=off` before the turn so +the exact-one-ring receipt cannot grow faculties, run maintenance, or emit +telemetry. Keep the generated chain outside the skill source directory. + +Do not use a real identity chain, wallet address, signature, transaction, token +burn, credential, private prompt, or generated faculty state. Report explicitly +what the proof does and does not demonstrate. diff --git a/showcase/cypher-tempre-timechain/examples/result-redacted.md b/showcase/cypher-tempre-timechain/examples/result-redacted.md new file mode 100644 index 0000000..6d5c78e --- /dev/null +++ b/showcase/cypher-tempre-timechain/examples/result-redacted.md @@ -0,0 +1,91 @@ +# Cypher Tempre Synthetic Timechain Result + +Captured on `2026-07-13T21:31:23Z` from the pinned external Genesis `v3.28.0` +skill using a disposable root normalized below as `$DEMO_ROOT`. Telemetry, +automatic maintenance, and faculty growth were disabled for the public proof. + +## Public input and candidate + +```text +Input: Create a public-safe, read-only CPHY proof +Candidate: This synthetic public demonstration seals one local Timechain ring +before a read-only CPHY contract lookup. +``` + +No user conversation, private prompt, credential, wallet, or lived-in memory was +used. + +## Timechain output + +```text +Genesis Block sealed (Ring 0). + name: PublicProof + ring_hash: ce2b66548ad82e5b3180492841a0cad942097f764f2190364c46a32e8ff276e6 + +verify: PASS +recalled: nothing relevant (new ground — reason from base judgment). +PoQ decision: SEAL +sealed self-labeled Ring 1 5b327a08d58a3b48.. +``` + +The final verification was: + +```text +CPHY AUDIT: PASS +Timechain VERIFY: PASS +height: 2 rings +head: #1 5b327a08d58a3b48.. +blockspace: 0 blobs +location: $DEMO_ROOT/chain +``` + +## Read-only token observation + +The target command derived this public, keyless address from synthetic Ring 1: + +```json +{ + "ring": 1, + "deposit_address": "0x5b327a08d58a3b48ef81a843fa2a48655ed9e995", + "rotation": 0, + "private": false +} +``` + +The allowlisted Base RPC observation returned: + +```json +{ + "observed": {}, + "changed": false, + "errors": [], + "new_etches": [], + "new_unlocks": [], + "pending_approval": [], + "rotated": [], + "awaiting": 0, + "total_burned_to_blockspace": 0 +} +``` + +Status for Ring 1: + +```json +{ + "token": "0x08df470d41c11ba5cb60242747d76c65ca52c94c", + "chain": "base", + "observed_tokens": 0.0, + "multiplier": 1.0 +} +``` + +`pending` returned `[]`. The CPHY event ledger contained zero events and passed +its hash-chain audit. + +## Boundary of the result + +This proves that the pinned external skill can initialize and verify a synthetic +Timechain and execute its canonical, read-only CPHY contract observation path. +It does **not** prove or claim a token burn, etch, faculty unlock, entitlement, +nonzero memory multiplier, wallet ownership, transaction, deployment, or +economic effect. diff --git a/showcase/cypher-tempre-timechain/proof/cphy-token-proof.md b/showcase/cypher-tempre-timechain/proof/cphy-token-proof.md new file mode 100644 index 0000000..c966425 --- /dev/null +++ b/showcase/cypher-tempre-timechain/proof/cphy-token-proof.md @@ -0,0 +1,60 @@ +# CPHY Agent Token Proof + +Captured on `2026-07-13` with unauthenticated public endpoints and the pinned +Genesis `v3.28.0` skill's read-only CPHY lane. + +## Virtuals project identity + +Filtered response from `https://api2.virtuals.io/api/virtuals/37924`: + +```json +{ + "id": 37924, + "name": "Cypher Tempre", + "symbol": "CPHY", + "chain": "BASE", + "tokenAddress": "0x08Df470d41C11Ba5Cb60242747D76C65Ca52c94c", + "status": "AVAILABLE", + "factory": "BONDING", + "verifiedLinks": { + "TWITTER": "https://x.com/cyberphysicsai", + "WEBSITE": "https://cyberphysics.ai/" + } +} +``` + +Public project page: https://app.virtuals.io/virtuals/37924 + +Public token explorer: +https://basescan.org/token/0x08Df470d41C11Ba5Cb60242747D76C65Ca52c94c + +## Independent Base RPC check + +`eth_chainId` on `https://mainnet.base.org` returned `0x2105` (Base mainnet, +decimal `8453`). `eth_getCode` for the same token address returned non-empty +contract code (`45` bytes at the queried address). + +## Skill integration check + +The pinned upstream `cphy.py` uses the same address and accepts only three +public, read-only Base RPC endpoints. Its `onchain sync` command queried ERC-20 +`balanceOf` for a disposable keyless target using `eth_call` and returned: + +```json +{ + "observed": {}, + "changed": false, + "errors": [], + "new_etches": [], + "new_unlocks": [], + "pending_approval": [], + "rotated": [], + "awaiting": 0, + "total_burned_to_blockspace": 0 +} +``` + +No wallet address, signature, token transfer, burn, approval, transaction, or +private RPC was used. A zero balance proves the read path, not an economic +effect. This bounded evidence is why `showcase.json` declares only the `token` +primitive and does not declare `wallet` or `acp`. diff --git a/showcase/cypher-tempre-timechain/proof/provenance.md b/showcase/cypher-tempre-timechain/proof/provenance.md new file mode 100644 index 0000000..afd83a2 --- /dev/null +++ b/showcase/cypher-tempre-timechain/proof/provenance.md @@ -0,0 +1,55 @@ +# Validation and Source Provenance + +## Pinned source identity + +- Repository: [`cyberphysicsai/cypher-tempre-genesis`](https://github.com/cyberphysicsai/cypher-tempre-genesis) +- Release: [`v3.28.0`](https://github.com/cyberphysicsai/cypher-tempre-genesis/releases/tag/v3.28.0) +- Dereferenced tag commit: [`bf88caa814d0a6f2abe45a325fa32056e99da65d`](https://github.com/cyberphysicsai/cypher-tempre-genesis/commit/bf88caa814d0a6f2abe45a325fa32056e99da65d) +- Codex skill tree: `28ff752fcc37ab633e6d39bdb2dd8c72c792bf06` +- Version file: `3.28.0` +- Skill directory: [`skills/codex/cypher-tempre-self-model`](https://github.com/cyberphysicsai/cypher-tempre-genesis/tree/v3.28.0/skills/codex/cypher-tempre-self-model) +- License: MIT in the upstream skill directory + +This Showcase directory contains no runtime copy. It is a pointer plus a hero, +manifest, synthetic proof receipt, and validation notes. + +## Source hashes + +Hashes were calculated directly from a clean checkout of the dereferenced +`v3.28.0` tag: + +- `SKILL.md`: `28201cd39009cc7db9ca45a4b9f75244da347041a6576c4f2968cb9eac9f0ee5` +- `recall.py`: `2e7e0e1e195983a2388bba862ac82888dadd4de6d4f2d0199ac9ad65c6f261c4` +- `cphy.py`: `f7e76c5690bd8cfb998960df3827c326967e9e6084b34ffa4210e9a4ca101732` + +## Clean-checkout validation + +All checks below ran on `2026-07-13` against the pinned external source: + +- Full architecture self-test: `SELFTEST: PASS` +- Smoke suite: `106 passed, 0 failed` +- Gate-discrimination suite: `12 passed, 0 failed` +- Synthetic CPHY ledger: `AUDIT: PASS` +- Synthetic Timechain: `VERIFY: PASS`, height `2`, blockspace `0` +- Synthetic Ring 0: `ce2b66548ad82e5b3180492841a0cad942097f764f2190364c46a32e8ff276e6` +- Synthetic Ring 1: `5b327a08d58a3b48ef81a843fa2a48655ed9e9951032167dd3d30d96adee1d54` +- Derived public keyless target: `0x5b327a08d58a3b48ef81a843fa2a48655ed9e995` +- Read-only CPHY observation: no errors, changes, events, approvals, burns, or + token-weighted multiplier + +The disposable proof disabled automatic growth, maintenance, and telemetry so +it sealed exactly the requested ring and left the clean source unchanged. + +## Showcase package boundary + +- Runtime files committed here: `0` +- Total project files committed here: `7` +- Generated chain or registry state committed here: `0` +- Dashboard, site, alternate runtimes, downloads, and full engine: external + Genesis repository only + +The public proof excludes lived-in chains, task roots, blockspace, telemetry, +learned registries, CPHY vaults, private rotation salts, active model-authored +operations, caches, environment files, credentials, private keys, wallet +material, hook configuration, absolute user paths, and private prompts. The +temporary proof root is normalized as `$DEMO_ROOT`. diff --git a/showcase/cypher-tempre-timechain/showcase.json b/showcase/cypher-tempre-timechain/showcase.json new file mode 100644 index 0000000..cdc505e --- /dev/null +++ b/showcase/cypher-tempre-timechain/showcase.json @@ -0,0 +1,71 @@ +{ + "slug": "cypher-tempre-timechain", + "title": "Cypher Tempre — Timechain Self-Model", + "tagline": "Gives AI agents tamper-evident memory, evidence-gated recall, and resumable long-horizon work through an append-only Timechain", + "description": "Cypher Tempre provides persistent local agent memory through an append-only hash-chained Timechain, PoQ-gated claims, source-aware recall, and resumable task and audit ledgers. This lightweight Showcase package points to the pinned full Codex skill in cyberphysicsai/cypher-tempre-genesis; the retained receipt initializes a synthetic chain and exercises its allowlisted read-only CPHY Agent Token query on Base without a wallet, signature, burn, or transaction. Generated memory stays local and no runtime code is duplicated here.", + "status": "validated external skill + read-only token integration", + "topic": "skills", + "topics": ["skills", "agent-memory", "verification", "token"], + "builder": { + "name": "cyberphysicsai", + "url": "https://github.com/cyberphysicsai" + }, + "links": { + "repo": "https://github.com/cyberphysicsai/cypher-tempre-genesis", + "demo": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/cypher-tempre-timechain/examples/result-redacted.md", + "share": "https://app.virtuals.io/virtuals/37924", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Cypher%20Tempre%20Timechain&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20The%20synthetic%20Timechain%20proof%20is%20clear%0A-%20The%20install%20or%20retention%20boundary%20needs%20more%20detail%0A-%20The%20read-only%20CPHY%20token%20evidence%20needs%20more%20depth%0A%0ANotes%3A%0A" + }, + "primitives": ["token"], + "visual": { + "kind": "verified skill run", + "eyebrow": "timechain + cphy token", + "title": "persistent, verifiable agent memory", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/cypher-tempre-timechain/assets/poster.jpg" + }, + "skills": [ + { + "name": "cypher-tempre-self-model", + "href": "https://github.com/cyberphysicsai/cypher-tempre-genesis/tree/v3.28.0/skills/codex/cypher-tempre-self-model", + "summary": "External v3.28.0 Codex skill for append-only Timechain memory, evidence-aware recall, explicit uncertainty gates, resumable audits, and bounded read-only CPHY Agent Token observation.", + "install": "(\nset -eu\ntarget=\"${CODEX_HOME:-$HOME/.codex}/skills/cypher-tempre-self-model\"\ntest ! -e \"$target\" || { echo \"Refusing to overwrite existing $target\" >&2; exit 1; }\ntmp=\"$(mktemp -d)\"\ntrap 'rm -rf \"$tmp\"' EXIT\nexpected=\"bf88caa814d0a6f2abe45a325fa32056e99da65d\"\ngit -c advice.detachedHead=false clone --quiet --depth 1 --branch v3.28.0 https://github.com/cyberphysicsai/cypher-tempre-genesis.git \"$tmp/genesis\"\ntest \"$(git -C \"$tmp/genesis\" rev-parse HEAD)\" = \"$expected\" || { echo \"Pinned source verification failed\" >&2; exit 1; }\nmkdir -p \"$(dirname \"$target\")\"\ncp -R \"$tmp/genesis/skills/codex/cypher-tempre-self-model\" \"$target\"\n)" + } + ], + "artifacts": [ + { + "label": "Redacted synthetic Timechain result", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/cypher-tempre-timechain/examples/result-redacted.md", + "kind": "proof" + }, + { + "label": "Read-only CPHY Agent Token proof", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/cypher-tempre-timechain/proof/cphy-token-proof.md", + "kind": "proof" + }, + { + "label": "Demo prompt", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/cypher-tempre-timechain/examples/prompt.md", + "kind": "prompt" + }, + { + "label": "Validation and source provenance", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/cypher-tempre-timechain/proof/provenance.md", + "kind": "proof" + }, + { + "label": "Pinned external Codex skill source", + "href": "https://github.com/cyberphysicsai/cypher-tempre-genesis/tree/v3.28.0/skills/codex/cypher-tempre-self-model", + "kind": "skill" + }, + { + "label": "CPHY token contract on Base", + "href": "https://basescan.org/token/0x08Df470d41C11Ba5Cb60242747D76C65Ca52c94c", + "kind": "token" + } + ], + "feedbackPrompts": [ + "Which long-running agent workflow should the Timechain prove next?", + "Are the persistence, approval, and third-party transmission boundaries clear enough?", + "What additional evidence would make a read-only Agent Token integration easier to trust?" + ] +} diff --git a/showcase/de9en-attention-markets/PR_DESCRIPTION.md b/showcase/de9en-attention-markets/PR_DESCRIPTION.md new file mode 100644 index 0000000..7e075b0 --- /dev/null +++ b/showcase/de9en-attention-markets/PR_DESCRIPTION.md @@ -0,0 +1,58 @@ +# Showcase Project + +## What shipped + +- Project slug: de9en-attention-markets +- Project title: De9en — Attention Markets +- Builder name and URL: de9en — https://github.com/de9enfun +- EconomyOS primitives used: acp +- Public proof: Product demo screenshots (showcase/de9en-attention-markets/assets/ and https://de9en.app/images/demo/), live dashboard https://de9en.app/dashboard, and live per-market share card https://de9en.app/share/unipcs-vs-orangie ; plus a redacted deliverable envelope and offerings catalog committed in this package +- Integration status: the ACP integration is PLANNED (design contract + reusable skill), not yet running. The product itself is live at de9en.app. No completed ACP job or on-chain settlement is claimed. +- Optional soul.md: showcase/de9en-attention-markets/soul.md (public, redacted) + +## Project package + +- [x] Added or updated `showcase/de9en-attention-markets/showcase.json` +- [x] Added demo artifacts, prompt, proof, or redacted report +- [x] Added reusable skill under `showcase/de9en-attention-markets/skills/acp-attention-market-signal/` +- [x] Used top-level `skills//` only when the skill is shared across projects (n/a — project-specific) +- [x] Set `skills[].sourcePath` in `showcase.json` for the committed skill +- [x] Linked all public artifacts from the manifest +- [x] Included exactly three feedback prompts +- [ ] Set `hidden: true` only if this package should merge without publishing its public Showcase card yet +- [x] Linked `soul.md` (public, redacted agent context) + +## Skill standard + +- Skill path: showcase/de9en-attention-markets/skills/acp-attention-market-signal +- [x] `SKILL.md` includes when to use it and when not to use it +- [x] Inputs, tools, credentials, and preconditions are explicit +- [x] Approval gates are listed (server-side gate; read-only; escalation in soul.md) +- [x] Stop conditions and handoff rules are listed +- [x] Validation checks and output contract are included + +## Safety and redaction + +- [x] No card numbers, CVVs, OTPs, magic links, API keys, access tokens, private prompts, wallet material, or private account records are published +- [x] Live workflow evidence is redacted (deliverable envelope carries no internals) +- [x] Public/private boundaries are explained (live product vs. ACP reference rail) +- [x] `soul.md` contains no private instructions, credentials, account data, wallet material, or operational secrets + +## Notes + +The De9en attention market is live at de9en.app (public dashboard, KOL Wars grid, +per-market share cards). This package contributes the ACP rail as a reusable +provider skill and design contract; no on-chain job settlement is claimed. The +skill generalizes to any live prediction/attention market that produces +read-only quotes. + +## Changes in response to review + +- **Clarified integration status.** The manifest, README, and soul now state + explicitly that the ACP integration is **planned** (design contract + reusable + skill), not a live deployment, and that no completed ACP job or on-chain + settlement is claimed. The product itself is live at de9en.app. +- **Added a product demo.** Committed screenshots under `assets/` (dashboard, + bet modal, shareable battle card) and a new "Product Demo" section in the + README showing how the product is used, plus image artifacts in the manifest. +- Updated the primary domain to de9en.app. diff --git a/showcase/de9en-attention-markets/README.md b/showcase/de9en-attention-markets/README.md new file mode 100644 index 0000000..6ed203e --- /dev/null +++ b/showcase/de9en-attention-markets/README.md @@ -0,0 +1,90 @@ +# De9en — Attention Markets + +De9en turns KOL attention into agent-tradable prediction markets. Each question +is a head-to-head **KOL battle** (KOL Wars): buyers take YES/NO on which creator +wins a weekly attention narrative, and the resulting odds, volume, and mindshare +ranking become a structured signal other agents can consume. + +This package contributes a reusable ACP skill, +[`acp-attention-market-signal`](skills/acp-attention-market-signal/SKILL.md), +that models each market as a priced ACP offering returning one stable signed +deliverable envelope — so a provider agent can sell De9en attention/odds signals +and a buyer agent integrates once across every market. + +## Integration status: PLANNED + +To be explicit about what is and isn't running: + +- **Live now (product):** the attention-market app at + [de9en.app](https://de9en.app/dashboard) — a public dashboard, the KOL Wars + grid, and per-market share cards. See [Product Demo](#product-demo) below. +- **Planned (ACP integration):** the ACP rail in this package (offerings + catalog, signed deliverable envelope, provider skill, and soul) is a **design + contract, not a live deployment**. There is **no completed ACP job and no + on-chain settlement** yet. This package documents how De9en will expose its + live market to buyer agents over ACP once the on-chain integration ships. + +## Product Demo + +How the product is used today: browse the KOL Wars grid, pick a battle, take +YES or NO on which creator wins the week's attention, and watch live odds, +volume, and mindshare update. Each battle also produces a shareable card. + +**1. Dashboard — KOL Wars grid with live odds, volume, and mindshare ranking.** + +![De9en dashboard](assets/dashboard.jpg) + +**2. Placing a position — pick YES/NO on a battle; see odds, probability, and estimated payout (on-chain trading coming soon).** + +![De9en bet modal](assets/bet-modal.jpg) + +**3. Shareable battle card — the same market odds rendered for agent/social sharing.** + +![De9en battle share card](assets/share-card.jpg) + +Live to try: https://de9en.app/dashboard + +## Package contents + +| Path | What it is | +| --- | --- | +| [`showcase.json`](showcase.json) | Showcase manifest | +| [`skills/acp-attention-market-signal/SKILL.md`](skills/acp-attention-market-signal/SKILL.md) | Reusable provider skill | +| [`offerings/offerings.json`](offerings/offerings.json) | ACP offerings catalog (one per market kind) | +| [`examples/attention-signal-envelope.json`](examples/attention-signal-envelope.json) | Redacted deliverable envelope example | +| [`soul.md`](soul.md) | Provider identity and guardrails | + +## Deliverable contract + +Every offering returns the same signed envelope so a buyer integrates once: + +```json +{ + "signal": "kol-battle-odds", + "market": "", + "source": "de9en (de9en.app)", + "delivered_at": "", + "disclaimer": "Informational only — not financial advice.", + "data": { "question": "...", "yes": {}, "no": {}, "mindshare_rank": 0 } +} +``` + +## Proof + +- Product demo screenshots: [`assets/`](assets) (dashboard, bet modal, share card) +- Live dashboard: https://de9en.app/dashboard +- Live per-market share card: https://de9en.app/share/unipcs-vs-orangie +- Redacted deliverable (design reference for the planned ACP rail): [`examples/attention-signal-envelope.json`](examples/attention-signal-envelope.json) + +## Install the skill + +```bash +cp -R showcase/de9en-attention-markets/skills/acp-attention-market-signal ~/.agents/skills/ +cp -R showcase/de9en-attention-markets/skills/acp-attention-market-signal ~/.claude/skills/ +``` + +## Guardrails + +Read-only outputs only; the pricing engine is never shipped. Every deliverable +carries a not-financial-advice disclaimer. No credentials, signer material, or +private methodology appear in any artifact. See [`soul.md`](soul.md). diff --git a/showcase/de9en-attention-markets/assets/bet-modal.jpg b/showcase/de9en-attention-markets/assets/bet-modal.jpg new file mode 100644 index 0000000..600add3 Binary files /dev/null and b/showcase/de9en-attention-markets/assets/bet-modal.jpg differ diff --git a/showcase/de9en-attention-markets/assets/dashboard.jpg b/showcase/de9en-attention-markets/assets/dashboard.jpg new file mode 100644 index 0000000..1abe600 Binary files /dev/null and b/showcase/de9en-attention-markets/assets/dashboard.jpg differ diff --git a/showcase/de9en-attention-markets/assets/share-card.jpg b/showcase/de9en-attention-markets/assets/share-card.jpg new file mode 100644 index 0000000..9b0ed7d Binary files /dev/null and b/showcase/de9en-attention-markets/assets/share-card.jpg differ diff --git a/showcase/de9en-attention-markets/examples/attention-signal-envelope.json b/showcase/de9en-attention-markets/examples/attention-signal-envelope.json new file mode 100644 index 0000000..ca4d15c --- /dev/null +++ b/showcase/de9en-attention-markets/examples/attention-signal-envelope.json @@ -0,0 +1,16 @@ +{ + "signal": "kol-battle-odds", + "market": "unipcs-vs-orangie", + "source": "de9en (de9en.app)", + "delivered_at": "2026-07-22T09:00:00Z", + "disclaimer": "Informational only — not financial advice.", + "data": { + "question": "Who gets more CT attention after the Robinhood token push?", + "yes": { "label": "Unipcs", "odds": 0.56, "volume": "284.6 BNB" }, + "no": { "label": "orangie", "odds": 0.44, "volume": "284.6 BNB" }, + "liquidity": "86.2 BNB", + "mindshare_rank": 1, + "as_of_block": null + }, + "note": "Redacted reference deliverable. Numbers mirror the live market card at https://de9en.app/share/unipcs-vs-orangie. No pricing-engine internals, credentials, or signer material are included." +} diff --git a/showcase/de9en-attention-markets/offerings/offerings.json b/showcase/de9en-attention-markets/offerings/offerings.json new file mode 100644 index 0000000..db0409d --- /dev/null +++ b/showcase/de9en-attention-markets/offerings/offerings.json @@ -0,0 +1,35 @@ +{ + "provider": "de9en", + "domain": "de9en.app", + "signal": "kol-battle-odds", + "currency": "USDC", + "note": "Each offering is one live KOL Wars market. Price is per delivered signed envelope. Prices are illustrative reference values for the ACP catalog; the underlying market is live at de9en.app.", + "offerings": [ + { + "name": "kol-battle-odds", + "title": "KOL Battle — live odds", + "price": 1, + "requirements": { "market": "" }, + "returns": "signed attention-signal envelope with yes/no odds, volume, mindshare_rank", + "example_markets": [ + "unipcs-vs-orangie", + "ansem-vs-cooker", + "west-vs-crypto-dog" + ] + }, + { + "name": "mindshare-ranking", + "title": "Attention mindshare ranking (top markets)", + "price": 3, + "requirements": { "limit": 5 }, + "returns": "ranked list of active markets by attention/volume with odds snapshots" + }, + { + "name": "volume-momentum", + "title": "Volume momentum snapshot (single market)", + "price": 1, + "requirements": { "market": "" }, + "returns": "signed envelope with rolling volume delta and odds drift for one market" + } + ] +} diff --git a/showcase/de9en-attention-markets/showcase.json b/showcase/de9en-attention-markets/showcase.json new file mode 100644 index 0000000..508c85f --- /dev/null +++ b/showcase/de9en-attention-markets/showcase.json @@ -0,0 +1,107 @@ +{ + "slug": "de9en-attention-markets", + "title": "De9en — Attention Markets", + "tagline": "Turns KOL attention into prediction markets with a planned ACP integration for sharing live KOL-battle odds", + "description": "De9en is a live attention-economy prediction market where every question is a head-to-head KOL battle (KOL Wars): buyers take YES/NO on which creator wins a weekly attention narrative, and the live odds, volume, and mindshare ranking become a structured signal other agents could consume. INTEGRATION STATUS: the ACP integration is PLANNED, not yet running — this package contributes the design contract and a reusable skill for it, not a live on-chain deployment. The product itself (dashboard, KOL Wars grid, per-battle share cards) is live at de9en.app; the ACP rail (offerings catalog, signed deliverable envelope, provider skill) describes how De9en will expose that live market to buyer agents once the on-chain integration ships. No completed ACP job or on-chain settlement is claimed. Proof included: product demo screenshots and the live product surface, plus a redacted deliverable envelope and offerings catalog committed in this package.", + "status": "Live product; ACP integration planned (design + reusable skill)", + "topic": "commerce", + "topics": [ + "prediction-markets", + "attention-economy", + "kol", + "acp", + "signals", + "commerce", + "robinhood-chain", + "defi" + ], + "builder": { + "name": "de9en", + "url": "https://github.com/de9enfun" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/de9en-attention-markets", + "demo": "https://de9en.app/dashboard", + "share": "https://de9en.app/share/unipcs-vs-orangie", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20De9en%20Attention%20Markets&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20Modeling%20a%20KOL%20battle%20as%20a%20priced%20ACP%20offering%20is%20clear%0A-%20The%20attention-signal%20envelope%20is%20easy%20to%20integrate%20against%0A-%20A%20specific%20attention%2Fodds%20signal%20I%27d%20want%20to%20buy%20agent-to-agent%0A%0ANotes%3A%0A" + }, + "primitives": [ + "acp" + ], + "visual": { + "kind": "product surface", + "eyebrow": "virtuals acp + robinhood chain", + "title": "kol attention, priced as agent-tradable markets", + "posterUrl": "https://de9en.app/images/og/unipcs-vs-orangie.jpg?v=20260722" + }, + "skills": [ + { + "name": "acp-attention-market-signal", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/de9en-attention-markets/skills/acp-attention-market-signal", + "sourcePath": "showcase/de9en-attention-markets/skills/acp-attention-market-signal", + "summary": "Reusable playbook to expose a prediction / attention market as an agent-to-agent signal business over ACP: model each market (a KOL battle) as a priced offering, price a funded job from a fixed catalog, fetch live odds/volume/mindshare, and submit one signed deliverable envelope so a buyer integrates once regardless of which market it bought.", + "install": "cp -R showcase/de9en-attention-markets/skills/acp-attention-market-signal ~/.agents/skills/\ncp -R showcase/de9en-attention-markets/skills/acp-attention-market-signal ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Product demo — dashboard (KOL Wars grid, live odds, volume, mindshare)", + "href": "https://de9en.app/images/demo/dashboard.jpg", + "kind": "image" + }, + { + "label": "Product demo — placing a position (YES/NO bet modal, on-chain coming soon)", + "href": "https://de9en.app/images/demo/bet-modal.jpg", + "kind": "image" + }, + { + "label": "Product demo — shareable battle card (same odds rendered for sharing)", + "href": "https://de9en.app/images/demo/share-card.jpg", + "kind": "image" + }, + { + "label": "Live product — De9en attention-markets dashboard", + "href": "https://de9en.app/dashboard", + "kind": "demo" + }, + { + "label": "Live KOL battle share card (per-market OG surface)", + "href": "https://de9en.app/share/unipcs-vs-orangie", + "kind": "proof" + }, + { + "label": "Attention Markets package README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/de9en-attention-markets/README.md", + "kind": "docs" + }, + { + "label": "Attention-signal deliverable envelope (redacted example)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/de9en-attention-markets/examples/attention-signal-envelope.json", + "kind": "proof" + }, + { + "label": "ACP offerings catalog (KOL battle markets)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/de9en-attention-markets/offerings/offerings.json", + "kind": "manifest" + }, + { + "label": "Reusable skill — acp-attention-market-signal", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/de9en-attention-markets/skills/acp-attention-market-signal", + "kind": "skill" + }, + { + "label": "Agent soul — attention-market provider identity and guardrails", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/de9en-attention-markets/soul.md", + "kind": "docs" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/de9en-attention-markets/soul.md", + "summary": "De9en's operational identity as an attention-market signal provider: what it sells (odds/volume/mindshare outputs, not the recipe), the one-envelope deliverable contract, its honest-framing and redaction guardrails, and where it escalates to a human instead of acting." + }, + "feedbackPrompts": [ + "Is modeling a KOL battle (a prediction market) as a priced ACP offering clear, and would your agent buy odds or mindshare signals this way?", + "Does the signed attention-signal envelope make the deliverable contract easy to integrate against across different markets?", + "Which attention or odds signal would be most valuable to purchase agent-to-agent — live odds, volume momentum, or mindshare ranking?" + ] +} diff --git a/showcase/de9en-attention-markets/skills/acp-attention-market-signal/SKILL.md b/showcase/de9en-attention-markets/skills/acp-attention-market-signal/SKILL.md new file mode 100644 index 0000000..a3e4046 --- /dev/null +++ b/showcase/de9en-attention-markets/skills/acp-attention-market-signal/SKILL.md @@ -0,0 +1,112 @@ +--- +name: acp-attention-market-signal +description: Expose a prediction / attention market as an agent-to-agent signal business over ACP. Model each market (e.g. a KOL battle) as a priced offering, price a funded job from a fixed catalog, fetch live odds/volume/mindshare from your own market surface, and submit one signed deliverable envelope so a buyer integrates once regardless of which market it bought. Use when you run a live prediction/attention market and want other agents to pay for its structured signals. +--- + +# ACP Attention Market Signal + +A reusable playbook for turning a **live prediction / attention market** into an +agent-to-agent signal business over the Agent Commerce Protocol (ACP), without +exposing the market's internal pricing engine. De9en uses it to sell the odds, +volume, and mindshare outputs of its KOL Wars battles; the pattern generalizes to +any market that produces public, read-only quotes. + +## When To Use + +- You already run a **live** market surface (odds, volume, ranking) and want + buyers — human-run or autonomous agents — to pay for its structured outputs. +- You want each tradable question (a KOL battle, a binary market) to be an ACP + **offering** that a buyer agent can discover, fund, and integrate against. +- You want one stable deliverable so a buyer integrates once and reuses it across + every market you list. + +## When NOT To Use + +- The market isn't live yet. Sell real quotes, not placeholders. +- The deliverable would leak the pricing engine (AMM curve constants, internal + liquidity routing, private order flow). Sell **outputs**, not the recipe. +- The action isn't read-only. This pattern sells *information*; it must never move + a buyer's funds or place a position on their behalf. + +## Prerequisites + +- A live market surface you control that returns, per market, at least: + `yesOdds`, `noOdds`, `volume`, and a `mindshareRank` (the source of truth for + every deliverable). +- A fixed, published offering catalog that maps each market to a price. +- `acp-cli` configured with the active provider agent, for the ACP rail. +- A server-side credential for your market API (never shipped in a deliverable). + +## Core principle — one deliverable envelope, many markets + +Define the deliverable **once** as a stable signed envelope and return it for +every market, so a buyer integrates a single shape: + +```json +{ + "signal": "kol-battle-odds", + "market": "", + "source": " ()", + "delivered_at": "", + "disclaimer": "Informational only — not financial advice.", + "data": { + "question": "", + "yes": { "label": "", "odds": 0.0, "volume": "..." }, + "no": { "label": "", "odds": 0.0, "volume": "..." }, + "mindshare_rank": 0, + "as_of_block": null + } +} +``` + +Every offering fetches from the same market surface and wraps it in the same +envelope, so payment rail and market choice never change the integration. + +## ACP Provider flow + +Publish offerings, then run a poller (cron, ~60s) that reacts to jobs: + +1. **Hydrate** open jobs; read the requirement message (which market + signal). +2. **Resolve + price** the offering from the fixed catalog → `setBudget(price)`. + Keep catalog prices and code in lockstep; resolve market slugs + case-insensitively so listing drift can't orphan a paid job. +3. On `job.funded`, **fetch** the live quote from your market surface + (server-only credential) and **submit** the signed envelope. +4. **Idempotency:** rely on the ACP state machine; make submit safe to retry. +5. Escrow releases to your wallet when the buyer approves. + +Buyer flow (for your docs / a test): + +```bash +acp client create-job --provider \ + --offering-name kol-battle-odds --requirements '{"market":""}' --chain-id +acp client fund --job-id --amount --chain-id +acp client complete --job-id --chain-id --reason verified +``` + +## Guardrails + +- **No fabricated proof.** Back every "it's live" claim with an inspectable + surface — the public market page, a share/OG card, or a completed job receipt. +- **No secret sauce in deliverables.** Ship the quote outputs; never embed the + AMM curve, liquidity routing, or resolution heuristics. +- **Honest framing.** Every deliverable carries a disclaimer; descriptive odds + are labelled descriptive, never presented as guaranteed directional alpha. +- **Redact.** No keys, signer material, secrets, or account credentials in any + offering, deliverable, or artifact. Wallet addresses and tx hashes only. +- **Server-side gate.** Enforce access on the server (ACP escrow state); never + gate purely client-side. Serve gated payloads `no-store`. + +## Validation checklist + +- [ ] Each offering in the catalog maps to exactly one market slug and price. +- [ ] The same envelope shape is returned for every market. +- [ ] Market slugs resolve case-insensitively; catalog prices match code. +- [ ] Deliverables carry the disclaimer and no private methodology. +- [ ] Gated responses are `no-store`; no secrets in any public artifact. + +## Output contract + +A buyer receives the signed envelope above. `data` is the live quote for the +requested `market`; `signal`, `market`, `source`, `delivered_at`, and +`disclaimer` are always present so integration is identical across markets. diff --git a/showcase/de9en-attention-markets/soul.md b/showcase/de9en-attention-markets/soul.md new file mode 100644 index 0000000..ff11b40 --- /dev/null +++ b/showcase/de9en-attention-markets/soul.md @@ -0,0 +1,53 @@ +# De9en — Attention-Market Provider Soul + +De9en is an attention-economy prediction market where each question is a +head-to-head KOL battle (KOL Wars). The product is live at de9en.app; the ACP +integration described here is **planned, not yet running**. As a planned ACP +**Provider** (not a job-taker), De9en will publish offerings (one per live +market) and a signal catalog, then wait to be hired or called once the on-chain +integration ships. + +## What it sells + +Market *outputs* only — live YES/NO odds, volume, and attention mindshare +ranking for each KOL battle. Everything is **read-only**: nothing it sells can +move a buyer's funds or place a position. The pricing engine (AMM curve, +liquidity routing, resolution heuristics) is never part of a deliverable. + +## One contract + +Every offering returns the same signed envelope: +`{ signal, market, source, delivered_at, disclaimer, data }`. A buyer integrates +once and reuses it across every market De9en lists. + +## Guardrails + +- **Honest framing.** Every deliverable carries `Informational only — not + financial advice`. Descriptive odds are labelled descriptive; they are never + presented as guaranteed directional alpha. +- **No secret sauce.** Buyers get quote outputs. The AMM curve, liquidity + routing, and resolution logic stay private and are never embedded. +- **No fabricated proof.** Claims about a live surface are backed by an + inspectable artifact — the public dashboard, a per-market share card, or a + completed job receipt — not prose. No on-chain job settlement is claimed until + one is captured. +- **Redaction.** No private keys, signer material, API secrets, or account + credentials appear in any deliverable, offering, or public artifact. +- **Server-side gate.** Access is enforced server-side on every request (ACP + escrow state); gated payloads are served `no-store`. + +## Escalation + +De9en defers to its human operator rather than acting when: + +- A buyer requests data a job/payment does not cover. +- A deliverable would require exposing private pricing methodology. +- Pricing, a new offering, or a new market needs to be added or changed. +- A dispute or an ambiguous resolution needs a manual decision. + +## Review preference + +De9en favors inspectable proof over claims: the live attention-markets +dashboard, per-market share cards, the offerings catalog, and the delivered +envelopes themselves. The goal is to show that agent-to-agent attention-signal +commerce is real, disciplined, and verifiable. diff --git a/showcase/degenai/assets/poster.jpg b/showcase/degenai/assets/poster.jpg new file mode 100644 index 0000000..46282a2 Binary files /dev/null and b/showcase/degenai/assets/poster.jpg differ diff --git a/showcase/degenai/showcase.json b/showcase/degenai/showcase.json new file mode 100644 index 0000000..c48c664 --- /dev/null +++ b/showcase/degenai/showcase.json @@ -0,0 +1,69 @@ +{ + "slug": "degenai", + "title": "DegenAI", + "tagline": "Creates and runs custom trigger-based HyperLiquid strategies from natural-language ACP jobs, and returns on-chain fill proof", + "description": "Accepts natural-language jobs over Virtuals ACP and runs them on HyperLiquid through the same engine that powers the DegenAI terminal. Agents can execute one-shot orders (limit, market, stop, or atomic batch with take-profit and stop-loss), or create custom trigger-based automation rules -- persistent strategies that evaluate natural-language conditions on a loop and place trades when they fire -- then list, inspect, evaluate, top up, and start or stop them, all over ACP. Every trade returns on-chain fill hashes, average fill price, fees, realized PnL, and post-trade account leverage as proof.", + "status": "live integration", + "topic": "commerce", + "topics": ["trading", "defi", "acp"], + "builder": { + "name": "DegenAI", + "url": "https://degenai.dev" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/degenai", + "demo": "https://degenai.dev", + "share": "https://x.com/DegenAI_0x/status/2000544000667685004", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20DegenAI" + }, + "primitives": ["acp", "wallet"], + "visual": { + "kind": "capability card", + "eyebrow": "acp + hyperliquid", + "title": "automated strategies + live execution", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/degenai/assets/poster.jpg" + }, + "skills": [ + { + "name": "acp-hyperliquid-trade-execution", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/degenai/skills/acp-hyperliquid-trade-execution", + "sourcePath": "showcase/degenai/skills/acp-hyperliquid-trade-execution", + "summary": "Submit a trading job to DegenAI over Virtuals ACP and receive an executed HyperLiquid order with on-chain fill proof. Covers asset discovery, order placement (limit/market/stop/batch), take-profit and stop-loss, and reading back fills, PnL, and account leverage.", + "install": "cp -R showcase/degenai/skills/acp-hyperliquid-trade-execution ~/.agents/skills/\ncp -R showcase/degenai/skills/acp-hyperliquid-trade-execution ~/.claude/skills/" + }, + { + "name": "acp-automation", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/degenai/skills/acp-automation", + "sourcePath": "showcase/degenai/skills/acp-automation", + "summary": "Create and run custom trigger-based trading automations on DegenAI over Virtuals ACP. Define a strategy in natural language and DegenAI evaluates the conditions on a loop, placing HyperLiquid orders when they fire. Covers create, list, inspect, single-shot evaluate, top-up, and start/stop/delete of rules.", + "install": "cp -R showcase/degenai/skills/acp-automation ~/.agents/skills/\ncp -R showcase/degenai/skills/acp-automation ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Live product", + "href": "https://degenai.dev", + "kind": "proof" + }, + { + "label": "ACP trade-execution skill source", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/degenai/skills/acp-hyperliquid-trade-execution", + "kind": "skill" + }, + { + "label": "ACP automation skill source", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/degenai/skills/acp-automation", + "kind": "skill" + }, + { + "label": "ACP launch announcement", + "href": "https://x.com/DegenAI_0x/status/2000544000667685004", + "kind": "proof" + } + ], + "feedbackPrompts": [ + "Which venues beyond HyperLiquid should this execute on next?", + "What deliverable fields matter most for your agent's downstream logic?", + "Would preset risk controls (stop-loss / take-profit) make this safer to call?" + ] +} diff --git a/showcase/degenai/skills/acp-automation/SKILL.md b/showcase/degenai/skills/acp-automation/SKILL.md new file mode 100644 index 0000000..53c1d2d --- /dev/null +++ b/showcase/degenai/skills/acp-automation/SKILL.md @@ -0,0 +1,91 @@ +# ACP Automation (DegenAI) + +Create and run **custom trigger-based trading automations** on **DegenAI** over +Virtuals ACP. Define a strategy in natural language; DegenAI evaluates the +conditions on a loop and places HyperLiquid orders when they fire. + +## When to use + +- You want a persistent, condition-driven strategy (not a one-shot order): + "buy the dip if BTC 4h RSI drops below 30", "trail a stop as price rises", + "close half the position when it's up 20%". +- You need to create, inspect, top up, or stop automations owned by a wallet, + over ACP. + +## When NOT to use + +- You just want a single immediate order. Use the trade-execution skill instead. +- The trading wallet has not approved a DegenAI agent link or holds less than + $10 USDC free margin. Resolve authorization/margin first. +- You are not authorized to spend real funds. A live automation places real + orders on a live exchange whenever its condition fires. + +## Capabilities exposed by DegenAI over ACP + +- `automation_info` - system metadata: supported assets, valid intervals, + trigger types, resource limits, and pricing. Call this before creating so you + build a valid rule. +- `automation_create` - create a rule from natural-language conditions and + actions. Requires trading authorization. Bundles 1000 LLM evaluation checks. +- `automation_list` - list every rule for the wallet with its state, asset, + condition, and the shared evaluation-check balance. +- `automation_status` - a single rule's live state: last check result, trigger + configuration, and recent execution logs. +- `automation_evaluate` - run one evaluation on demand; charged only for the + actual LLM usage it consumes. +- `automation_manage` - start, stop, or delete a rule. Stop pauses evaluation + without deleting; delete removes it. +- `automation_topup` - add 1000 evaluation checks to the wallet's shared + balance. + +## Billing model + +- Evaluation "checks" are a per-wallet balance shared across all of that + wallet's automations. `automation_create` bundles 1000; `automation_topup` + adds 1000 more; `automation_evaluate` charges for actual usage. +- The rule's trades themselves settle on HyperLiquid under the wallet's + approved DegenAI agent - the same authorization used for one-shot execution. + +## Credentials and preconditions + +- The wallet must have approved a DegenAI agent link (one Butler agent per + wallet). +- Free margin >= $10 USDC per order the automation will place (HyperLiquid + minimum notional). +- Call `automation_info` first to confirm supported assets, intervals, and + trigger types before creating. + +## Approval gates (spending / production mutations) + +- A live automation places real orders whenever its condition fires, without a + human in the loop at fire time. The submitting agent MUST have explicit human + or policy approval for the strategy and its notional before creating the rule. +- Per-wallet and per-asset automation caps are enforced before payment settles; + a rule that would exceed a cap is rejected rather than created. + +## Stop conditions and handoff + +- Stop if trading is not authorized, margin is insufficient, or the asset / + interval / trigger type is unsupported (checked via `automation_info`) - hand + the setup / top-up link back to the caller. +- To halt a running strategy, call `automation_manage` with stop (pause) or + delete. Stopping is reversible; deleting is not. + +## Validation and output contract + +- `automation_create` returns the rule id, state (active / armed / paused), the + resolved trigger and next check time, and the remaining evaluation-check + balance. +- `automation_evaluate` returns condition met / not met, the reasoning, and any + action taken (including on-chain fill details when it places a trade). +- On failure: `errorCode` and `errorMessage`, no silent partial state. + +## Example job + +> "Create an automation on ETH 1h: if RSI closes below 30, buy $50 at market +> with a stop-loss 3% below entry. Check every closed 1h candle." + +DegenAI validates authorization and margin, creates the rule with a candle-close +trigger, and returns the rule id, its armed trigger, the next check time, and +the evaluation-check balance. When the condition later fires, it places the +order and records on-chain fill proof. diff --git a/showcase/degenai/skills/acp-hyperliquid-trade-execution/SKILL.md b/showcase/degenai/skills/acp-hyperliquid-trade-execution/SKILL.md new file mode 100644 index 0000000..6e83f1c --- /dev/null +++ b/showcase/degenai/skills/acp-hyperliquid-trade-execution/SKILL.md @@ -0,0 +1,84 @@ +# ACP HyperLiquid Trade Execution (DegenAI) + +Submit a trading job to **DegenAI** over Virtuals ACP and get back an executed +HyperLiquid perpetual order with on-chain fill proof. + +## When to use + +- You want an agent to place, modify, or cancel real HyperLiquid perp orders + through a natural-language ACP job. +- You need on-chain proof (fill hashes) and post-trade account context back as + a deliverable. + +## When NOT to use + +- You only want analysis, signals, or charts. This skill executes trades; it + does not return standalone research. +- The trading wallet has not approved a DegenAI agent link, or holds less than + $10 USDC free margin. Resolve authorization/margin first. +- You are not authorized to spend real funds. Every accepted job moves real + money on a live exchange. + +## Inputs + +- `intent` - natural-language or structured order: side (buy/sell), USD size, + optional limit price, take-profit, stop-loss; or a modify / cancel request. +- `wallet` - the HyperLiquid wallet whose approved DegenAI agent will sign. + +## Tools exposed by DegenAI over ACP + +- `get_available_assets` - list every tradable HyperLiquid asset with metadata. +- `hyperliquid_order` - `create_limit` / `create_market` / `create_stop` / + `create_batch` / `modify_by_oid` / `modify_by_cloid` / `modify_batch` / + `cancel_by_oid` / `cancel_by_cloid` / `cancel_batch`. Supports take-profit, + stop-loss, reduce-only, time-in-force (Gtc / Ioc / Alo), and client order + IDs (CLOID). + +## Credentials and preconditions + +- The wallet must have approved a DegenAI agent link (one Butler agent per + wallet; relink via the trading-status resource if a different agent is + attached). +- Free margin >= $10 USDC per order (HyperLiquid minimum notional). +- Query the trading-status resource first to confirm authorization, check + margin, and retrieve the setup / top-up link. + +## Approval gates (spending / production mutations) + +- Placing, modifying, or cancelling an order moves real funds and mutates a + live exchange account. The submitting agent MUST have explicit human or + policy approval for the notional being traded before opening the job. +- Confirm side, asset, USD size, and any stop-loss / take-profit against the + approved intent before execution. + +## Stop conditions and handoff + +- Stop if trading is not authorized, margin is insufficient, or the asset is + not tradable - return the setup / top-up link and hand back to the caller. +- Stop if any single order is below $10 notional (it will be rejected). +- On partial batch failure, surface which orders placed vs failed; do not + silently retry. + +## Validation and output contract + +The job deliverable returns: + +- `success` (bool) and `action` (place / modify / cancel) +- `orderIds` - placed / affected order IDs +- `fillHashes` - on-chain transaction hashes per filled order (proof) +- `fillDetails` - average price, total fees, realized PnL, total size, fill count +- `accountContext` - account value, margin used, leverage ratio after the trade +- On failure: `errorCode` and `errorMessage`, no partial silent state + +## Example job + +> "Buy $50 of BTC at market, set a stop-loss at 58000 and a take-profit at 72000." + +DegenAI validates authorization and margin, executes the market order with the +bracket, and returns the order IDs, on-chain fill hash, average fill price and +fees, and the updated account leverage. + +## Notes + +- Batch operations execute atomically (all succeed or all fail) in one call. +- The Butler fee is kept to a $0.01 minimum per request. diff --git a/showcase/elizawc-worldcup-edge/README.md b/showcase/elizawc-worldcup-edge/README.md new file mode 100644 index 0000000..20709c0 --- /dev/null +++ b/showcase/elizawc-worldcup-edge/README.md @@ -0,0 +1,51 @@ +# elizaWC World Cup Market Edge + +elizaWC is a live Telegram agent that reads Polymarket World Cup markets and +returns one grounded AI sentence per market, powered by EconomyOS compute. + +Try it live: https://t.me/elizaWC_bot + +## What it does + +1. **Pulls real data** — Polymarket Gamma (24h volume, 24h price change, + liquidity) + CLOB (midpoint, best bid/ask, spread, 7-day price history). +2. **Computes a signal** — a three-factor model (order-book last-vs-mid, + 24h momentum, directional 7-day backtest) with a liquidity gate and a + HIGH / MED / LOW confidence taken from that market's own history. +3. **Writes a grounded read** — the market's real numbers are sent to the + EconomyOS compute endpoint (`https://compute.virtuals.io/v1`, Kimi K2). The + model returns a single sentence that explains the edge or flags a + low-confidence trap. It is constrained to the supplied numbers only. +4. **Renders a card** — the read is drawn onto a Polymarket-style card and sent + in Telegram. + +## How EconomyOS is used (primitive: wallet) + +The agent pays for its own inference. Its EconomyOS agent wallet holds USDC, +tops up the compute balance, and each market read is billed per call +(~$0.001 per card). This is the `wallet` primitive in practice: an agent +wallet funding the agent's own compute. + +- Agent: https://app.virtuals.io/acp/agents/019ebfed-9b45-79e6-9946-44c6d6bf4154 +- Compute endpoint: `https://compute.virtuals.io/v1` +- Model: `moonshotai/kimi-k2` (returned as `kimi-k2-6`) + +## The grounding contract + +The system prompt forbids inventing prices, news, injuries, form, or results. +The model may only use the numbers passed to it. If the backtest is weak, the +read says so — see the example card, where eight prior momentum setups all +failed and the read calls it a low-confidence trap. + +## Proof + +- `assets/economyos-compute.png` — EconomyOS dashboard showing the wallet, + the compute balance, real Compute Spend, and the inference top-up. +- `assets/card-example.png` — a live rendered card carrying the EconomyOS read. +- `examples/compute-proof.md` — a redacted request/response with model and cost. + +## Skill + +`skills/elizawc-worldcup-read/` is the reusable piece: given one market's live +facts, it produces the grounded one-sentence read through the EconomyOS compute +endpoint. It contains no keys, wallet material, or private product code. diff --git a/showcase/elizawc-worldcup-edge/assets/card-example.png b/showcase/elizawc-worldcup-edge/assets/card-example.png new file mode 100644 index 0000000..fad16c7 Binary files /dev/null and b/showcase/elizawc-worldcup-edge/assets/card-example.png differ diff --git a/showcase/elizawc-worldcup-edge/assets/economyos-compute.png b/showcase/elizawc-worldcup-edge/assets/economyos-compute.png new file mode 100644 index 0000000..4d7930c Binary files /dev/null and b/showcase/elizawc-worldcup-edge/assets/economyos-compute.png differ diff --git a/showcase/elizawc-worldcup-edge/assets/poster.png b/showcase/elizawc-worldcup-edge/assets/poster.png new file mode 100644 index 0000000..efb0a11 Binary files /dev/null and b/showcase/elizawc-worldcup-edge/assets/poster.png differ diff --git a/showcase/elizawc-worldcup-edge/examples/compute-proof.md b/showcase/elizawc-worldcup-edge/examples/compute-proof.md new file mode 100644 index 0000000..7417d63 --- /dev/null +++ b/showcase/elizawc-worldcup-edge/examples/compute-proof.md @@ -0,0 +1,65 @@ +# Redacted EconomyOS inference proof + +These are real calls made by elizaWC against the EconomyOS compute endpoint. +The API key is redacted. No wallet material or private code is included. + +## Endpoint + +- Base URL: `https://compute.virtuals.io/v1` +- Model: `moonshotai/kimi-k2` (served as `kimi-k2-6`) +- Auth: `Authorization: Bearer acp-****` (redacted) + +## Request (one market read) + +```json +{ + "model": "moonshotai/kimi-k2", + "max_tokens": 200, + "temperature": 0.4, + "venice_parameters": { "disable_thinking": true, "strip_thinking_response": true }, + "messages": [ + { "role": "system", "content": "You are a sharp Polymarket World Cup analyst ... use ONLY the numbers given; never invent prices, news, injuries, form, or results ..." }, + { "role": "user", "content": "{\"outcome\":\"England\",\"event\":\"Furthest Advancing UEFA Nation\",\"mid_cents\":10.5,\"last_cents\":10.5,\"spread_cents\":1,\"chg_24h_cents\":-2.5,\"liquidity_usd\":8500,\"week_range_cents\":\"8.5-19.0c\",\"signal\":\"FADE SETUP\",\"backtest\":{\"setups\":9,\"outcome\":\"reverted\",\"hit_pct\":77,\"avg_move_cents\":2.0},\"confidence\":\"HIGH\"}" } + ] +} +``` + +## Response (redacted) + +```json +{ + "model": "kimi-k2-6", + "choices": [ + { + "index": 0, + "finish_reason": "stop", + "message": { + "role": "assistant", + "content": "England at 10.5c for Furthest Advancing UEFA Nation sits near the 8.5c weekly low with a 77% historical fade rate and 2.0c average reversion, though zero 24h volume and 1c spread create execution risk on an 8500 liquidity pool." + } + } + ], + "usage": { "prompt_tokens": 1101, "completion_tokens": 65, "total_tokens": 1166 }, + "cost": { "usd": 0.00105148 } +} +``` + +HTTP status: `201`. The read uses only the supplied numbers (price, weekly low, +backtest hit rate, average reversion, spread, liquidity) and invents nothing. + +## Second live example (honest low-confidence read) + +For the "7+ matches Decided by Penalty Shootout" market (mid 14.2c, +10.7c 24h, +spread 0.5c), the backtest found 8 prior MOMENTUM UP setups that all failed +(0% hit, average -7.9c). The returned read flagged it rather than overselling: + +> Penalty shootout volume surged 10.7c in 24h to 14.2c mid with thin 0.5c +> spread, but backtest shows 8 prior MOMENTUM UP setups all failed to hit and +> averaged -7.9c moves, signaling a LOW confidence trap. + +## Where to verify + +- Agent: https://app.virtuals.io/acp/agents/019ebfed-9b45-79e6-9946-44c6d6bf4154 +- Live product: https://t.me/elizaWC_bot +- Dashboard screenshot: `../assets/economyos-compute.png` (wallet, compute + balance, Compute Spend, inference top-up) diff --git a/showcase/elizawc-worldcup-edge/showcase.json b/showcase/elizawc-worldcup-edge/showcase.json new file mode 100644 index 0000000..6919e38 --- /dev/null +++ b/showcase/elizawc-worldcup-edge/showcase.json @@ -0,0 +1,71 @@ +{ + "slug": "elizawc-worldcup-edge", + "title": "elizaWC World Cup Market Edge", + "tagline": "Turns live Polymarket World Cup prices and a 7-day backtest into one grounded AI read per market, delivered as a Telegram card", + "description": "elizaWC pulls live Polymarket World Cup markets from the Gamma and CLOB APIs, computes a three-factor signal with a directional 7-day backtest and HIGH/MED/LOW confidence, then calls the EconomyOS compute endpoint (Kimi K2) to write one grounded sentence per market card in Telegram. The read is constrained to the real numbers only, so it explains the edge or flags a low-confidence trap instead of inventing prices or results. The agent funds its own inference: its EconomyOS wallet tops up compute and every read is billed per call. Proof includes the EconomyOS compute-spend dashboard, a rendered live card carrying the AI read, and a redacted inference response with model and cost.", + "status": "live", + "topic": "agents", + "topics": [ + "prediction-markets", + "polymarket", + "world-cup", + "market-intelligence", + "telegram-bot", + "inference" + ], + "builder": { + "name": "Baoger", + "url": "https://x.com/baogerbao" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/elizawc-worldcup-edge", + "demo": "https://t.me/elizaWC_bot", + "share": "https://app.virtuals.io/acp/agents/019ebfed-9b45-79e6-9946-44c6d6bf4154", + "feedback": "https://x.com/baogerbao" + }, + "primitives": [ + "wallet" + ], + "visual": { + "kind": "market read card", + "eyebrow": "wallet + compute + polymarket", + "title": "grounded world cup reads", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/elizawc-worldcup-edge/assets/poster.png" + }, + "skills": [ + { + "name": "elizawc-worldcup-read", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/elizawc-worldcup-edge/skills/elizawc-worldcup-read", + "sourcePath": "showcase/elizawc-worldcup-edge/skills/elizawc-worldcup-read", + "summary": "Take one Polymarket market's live facts (mid, last, spread, 24h change, 7-day range, signal, backtest hit rate, confidence) and produce a single grounded sentence through the EconomyOS compute endpoint, with hard guardrails against inventing prices, news, or results.", + "install": "cp -R showcase/elizawc-worldcup-edge/skills/elizawc-worldcup-read ~/.agents/skills/\ncp -R showcase/elizawc-worldcup-edge/skills/elizawc-worldcup-read ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "EconomyOS compute-spend dashboard", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/elizawc-worldcup-edge/assets/economyos-compute.png", + "kind": "screenshot" + }, + { + "label": "Live market card with EconomyOS AI read", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/elizawc-worldcup-edge/assets/card-example.png", + "kind": "screenshot" + }, + { + "label": "Redacted inference response (model + cost)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/elizawc-worldcup-edge/examples/compute-proof.md", + "kind": "proof" + }, + { + "label": "elizaWC package README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/elizawc-worldcup-edge/README.md", + "kind": "docs" + } + ], + "feedbackPrompts": [ + "Is the one-sentence read clear enough to act on without opening the full card?", + "Which extra market signals would make the read more useful for a trader?", + "Does the read stay honest about low-confidence setups instead of overselling an edge?" + ] +} diff --git a/showcase/elizawc-worldcup-edge/skills/elizawc-worldcup-read/SKILL.md b/showcase/elizawc-worldcup-edge/skills/elizawc-worldcup-read/SKILL.md new file mode 100644 index 0000000..9bde357 --- /dev/null +++ b/showcase/elizawc-worldcup-edge/skills/elizawc-worldcup-read/SKILL.md @@ -0,0 +1,99 @@ +--- +name: elizawc-worldcup-read +description: Turn one Polymarket market's live facts into a single grounded read through the EconomyOS compute endpoint. +version: 1.0.0 +--- + +# elizaWC World Cup Read + +Use this skill when you already have the live facts for **one** prediction +market (mid, last, spread, 24h change, 7-day range, a signal label, a backtest +hit rate, and a confidence tier) and you want a single, grounded, human sentence +that explains the edge or the risk. + +Do not use it to fetch market data, to place or size trades, to manage a wallet, +or to make a prediction about the real-world outcome. It reads the numbers you +give it; it does not forecast results. + +## Preconditions + +- An EconomyOS compute account with a positive compute balance. The call is + billed per request (about $0.001 for one read). +- `VIRTUALS_API_KEY` available in the environment. Never place the key in + prompts, logs, proof files, or committed code. +- Base URL `https://compute.virtuals.io/v1` (OpenAI-compatible Chat Completions). +- The caller has already gathered the market facts from its own data source. + +## Inputs + +A single JSON object of real numbers for one market outcome: + +```json +{ + "outcome": "7+ matches", + "event": "No. of Matches Decided by Penalty Shootout", + "mid_cents": 14.2, + "last_cents": 14.0, + "spread_cents": 0.5, + "chg_24h_cents": 10.7, + "vol_24h_usd": 2000, + "liquidity_usd": 8500, + "week_range_cents": "3.7-39.0c", + "signal": "MOMENTUM UP", + "backtest": { "setups": 8, "outcome": "held", "hit_pct": 0, "avg_move_cents": -7.9 }, + "confidence": "LOW" +} +``` + +Every field must come from real data. Do not fill gaps with guesses; pass +`null` for anything you do not have. + +## Call + +```bash +curl https://compute.virtuals.io/v1/chat/completions \ + -H "Authorization: Bearer $VIRTUALS_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "moonshotai/kimi-k2", + "max_tokens": 200, + "temperature": 0.4, + "venice_parameters": { "disable_thinking": true, "strip_thinking_response": true }, + "messages": [ + { "role": "system", "content": "You are a sharp Polymarket World Cup analyst. You get REAL live data for ONE market outcome. Write ONE tight sentence (max 34 words) on what is happening and the edge or risk for a trader. STRICT: use ONLY the numbers given; never invent prices, news, injuries, form, or results; no financial advice; plain text, no markdown, no emojis." }, + { "role": "user", "content": "" } + ] + }' +``` + +`venice_parameters` keeps the model's chain-of-thought out of the answer so the +`content` field is a clean one-liner. + +## Grounding gates + +- The system prompt is the guardrail: the model may use only the supplied + numbers. Never relax it to allow prices, news, injuries, form, or results. +- If the backtest is weak (low hit rate, adverse average move), the read must + say so. Do not rewrite a cautious read into a bullish one. +- Do not present the sentence as financial advice or a guaranteed outcome. + +## Spend gate + +- This call costs money. Only run it when a read is actually going to be shown. +- Cache by market for a short window (elizaWC uses ~90 seconds) so refreshes do + not re-bill every time. + +## Stop conditions + +- Stop if `VIRTUALS_API_KEY` is missing or the compute balance is exhausted + (HTTP 402): fall back to a deterministic template built from the same numbers, + do not block the card. +- Stop if the response `content` is empty: fall back, do not ship a blank read. +- Stop if the returned sentence names a price, result, or fact not present in + the input: discard it and fall back. + +## Output contract + +Return a single plain-text sentence (<= 34 words) grounded in the input numbers, +plus the `model` and `cost` from the response for accounting. On any failure, +return the deterministic fallback instead of an error. diff --git a/showcase/fia-signals-safe-swap-preflight/README.md b/showcase/fia-signals-safe-swap-preflight/README.md new file mode 100644 index 0000000..2e6f074 --- /dev/null +++ b/showcase/fia-signals-safe-swap-preflight/README.md @@ -0,0 +1,111 @@ +# Base Swap Risk Preflight by Fia Signals + +Base Swap Risk Preflight by Fia Signals is a project-only ACP/x402 showcase +package for `safe_swap_preflight`, `/token-safety/batch`, and +`/contract-risk/batch`. It gives a buyer agent a compact GO / CAUTION / BLOCK +decision before the agent routes USDC, signs, or moves funds. + +This package is intentionally no-mutation: it documents the buyer workflow, +public readback evidence, and revenue boundary. It does not claim completed +external revenue, does not include private buyer data, and does not require any +wallet signing to review. + +## What It Does + +| Surface | Chain | Input | Price context | +| --- | --- | --- | --- | +| `safe_swap_preflight` | Base / EVM | token, planned spend, route, slippage, buyer intent | `0.01 USDC` direct-buy context | +| `/token-safety/lite` | Base | token plus lite pre-swap context | unpaid `402` boundary proof | +| `/token-safety/batch` | Base / EVM | comma-separated token addresses, max 5 | unpaid `402` boundary proof | +| `/contract-risk/batch` | Base / EVM | comma-separated contract addresses, max 5 | unpaid `402` boundary proof | + +The buyer story is simple: before an autonomous finance agent spends against a +route, it asks Fia Signals whether the token, pair, and execution path are safe +enough to proceed. The answer is bounded to: + +- `GO` - no blocking token or route risk found. +- `CAUTION` - route is not blocked, but unresolved risk requires smaller size, + lower slippage, or independent confirmation. +- `BLOCK` - do not spend, sign, or route funds through this path. + +## Buyer Prompt + +```text +I am a Base finance agent preparing a token swap. Before I spend or route USDC, +run Fia Signals safe_swap_preflight on the token and route context. Return a +compact GO, CAUTION, or BLOCK decision with the reason, risk flags, and what +evidence would change the decision. +``` + +## Public Readback + +Captured without spend on 2026-07-15T00:11:08Z: + +- `/token-safety/lite` returned HTTP `402`, confirming the unpaid x402 boundary. +- `https://x402.fiasignals.com/virtuals-direct-buy.json?offering=safe_swap_preflight` + returned HTTP `200`. +- The direct-buy manifest included `safe_swap_preflight` with `price_usd: 0.01`, + `job_fee_usdc: 0.01`, `max_amount_required_usd: 0.01`, and + `required_funds: false`. + +See [`examples/buyer-workflow-packet.md`](examples/buyer-workflow-packet.md) +for the route shape, sample decisions, caveats, and proof boundary. + +## OpenClaw Base Ops Adapter + +For an execution agent, the proven integration hook is the batch-risk gate: + +1. Build `token_addresses` from `route.to_token` plus any intermediate token or + contract addresses. +2. Call `/token-safety/batch` before execution. +3. Call `/contract-risk/batch` when bytecode, proxy, owner, admin, or upgrade + risk affects the route. +4. Convert Fia's response into `GO`, `CAUTION`, or `BLOCK`. +5. Store the raw Fia JSON and adapted decision beside the swap decision artifact. + +See [`examples/base-ops-batch-risk-adapter.md`](examples/base-ops-batch-risk-adapter.md) +for copy-paste commands, the request adapter, and the response adapter. + +## Discovery Terms + +This package is intentionally searchable around buyer intent, not just the +project name: + +- token safety +- pre swap +- pre-swap risk +- rugpull +- honeypot +- contract risk +- Base swap risk +- execution agent +- x402 batch risk + +## Revenue Boundary + +Strict external revenue is `USD 0.00` for this package as of the captured +readback. Unpaid `402` challenges, self-buys, control-wallet calls, team-paid +probes, public copy changes, and route-health checks are not revenue. + +Revenue would require an external non-team buyer, paid `200`, completed or +settled job, non-secret tx/job reference, buyer identity or wallet, and delivery +row/hash. + +## Why This Matters + +Finance and execution agents do not only need monitoring after the fact. They +need a cheap decision gate before funds move. `safe_swap_preflight` is packaged +as that gate: a small, buyer-native preflight step that converts spend-control +anxiety into a callable workflow before execution. + +## Files + +- `showcase.json` - card metadata for the EconomyOS Showcase sync. +- `assets/poster.png` - committed 16:9 card poster. +- `soul.md` - public operating context and guardrails. +- `examples/buyer-workflow-packet.md` - redacted buyer workflow, sample outputs, + proof status, and revenue boundary. +- `examples/live-endpoint-proof.md` - no-spend public endpoint proof. +- `examples/base-ops-batch-risk-adapter.md` - copy-paste integration commands + and schema adapters for OpenClaw Base Ops style swap workflows. +- `examples/redacted-batch-risk-result.md` - redacted delivery result shape. diff --git a/showcase/fia-signals-safe-swap-preflight/assets/poster.png b/showcase/fia-signals-safe-swap-preflight/assets/poster.png new file mode 100644 index 0000000..8ecd48b Binary files /dev/null and b/showcase/fia-signals-safe-swap-preflight/assets/poster.png differ diff --git a/showcase/fia-signals-safe-swap-preflight/examples/base-ops-batch-risk-adapter.md b/showcase/fia-signals-safe-swap-preflight/examples/base-ops-batch-risk-adapter.md new file mode 100644 index 0000000..1247e08 --- /dev/null +++ b/showcase/fia-signals-safe-swap-preflight/examples/base-ops-batch-risk-adapter.md @@ -0,0 +1,117 @@ +# OpenClaw Base Ops Batch-Risk Adapter + +Use this adapter before a Base execution agent signs or routes a swap. Build the +address list from `route.to_token` plus any intermediate token or contract +addresses that can affect settlement. Call token safety first, then contract +risk when bytecode or admin risk matters. + +## Canonical Input + +```json +{ + "chain": "base", + "token_addresses": [ + "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "0x4200000000000000000000000000000000000006" + ], + "workflow": "pre_swap_gate", + "route_context": { + "dex": "buyer-selected DEX or aggregator", + "amount_usdc": 25, + "slippage_bps": 100 + } +} +``` + +Rules: + +- `token_addresses` must contain 1 to 5 EVM addresses. +- Default `chain` is `base`. +- Never include private keys, seed phrases, auth tokens, wallet-control data, or + signing material. + +## Copy-Paste Unpaid Boundary Checks + +```bash +TOKEN_CSV="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913,0x4200000000000000000000000000000000000006" + +curl -i "https://x402.fiasignals.com/token-safety/batch?chain=base&token_addresses=${TOKEN_CSV}" +curl -i "https://x402.fiasignals.com/contract-risk/batch?chain=base&token_addresses=${TOKEN_CSV}" +``` + +Expected unpaid status: HTTP `402`. + +For a paid call, use an x402-capable client. First request receives `402`; the +client builds the payment for `accepts[0]`; then it retries the same URL with +the full x402 payload in `X-PAYMENT` or `PAYMENT-SIGNATURE`. + +## Request Adapter + +```js +function toFiaBatchQuery(input) { + const chain = input.chain || 'base' + const addrs = [...(input.token_addresses || [])] + + if (addrs.length < 1 || addrs.length > 5) { + throw new Error('token_addresses must contain 1..5 EVM addresses') + } + + for (const address of addrs) { + if (!/^0x[a-fA-F0-9]{40}$/.test(address)) { + throw new Error(`invalid EVM address: ${address}`) + } + } + + return new URLSearchParams({ + chain, + token_addresses: addrs.join(','), + }).toString() +} +``` + +## Response Adapter + +```js +function fiaResultToSwapGate(result) { + const rows = result.results || [] + const hard = rows.filter((row) => ( + ['blocked', 'error'].includes(String(row.verdict || '').toLowerCase()) || + String(row.action || '').toUpperCase() === 'REJECT' + )) + const caution = rows.filter((row) => ( + String(row.verdict || '').toLowerCase() === 'risky' || + String(row.action || '').toUpperCase() === 'CAUTION' + )) + + return { + decision: hard.length ? 'BLOCK' : caution.length ? 'CAUTION' : 'GO', + checked_count: result.count || rows.length, + summary: result.summary || {}, + blockers: hard.map((row) => ({ + verdict: row.verdict, + action: row.action, + reasons: row.reasons || [], + })), + warnings: caution.map((row) => ({ + verdict: row.verdict, + action: row.action, + reasons: row.reasons || [], + })), + source: 'Fia Signals batch risk x402', + } +} +``` + +## Base Ops Hook + +1. Before swap execution, collect `route.to_token` and any intermediate token or + contract addresses. +2. Call `/token-safety/batch`. +3. Call `/contract-risk/batch` when bytecode, proxy, owner, admin, or upgrade + risk affects the route. +4. Block execution on `BLOCK` or `REJECT`. +5. Cap size or require operator confirmation on `CAUTION`. +6. Store the Fia JSON and the adapted decision beside the swap decision artifact. + +This adapter is integration material only. It is not buyer proof, settlement +proof, or revenue. diff --git a/showcase/fia-signals-safe-swap-preflight/examples/buyer-workflow-packet.md b/showcase/fia-signals-safe-swap-preflight/examples/buyer-workflow-packet.md new file mode 100644 index 0000000..e4eb02a --- /dev/null +++ b/showcase/fia-signals-safe-swap-preflight/examples/buyer-workflow-packet.md @@ -0,0 +1,152 @@ +# Buyer Workflow Packet - Safe Swap Preflight + +**Format:** no-mutation buyer workflow packet +**Workflow:** `safe_swap_preflight` +**Public lite route:** `POST /token-safety/lite` +**Captured:** 2026-07-15T00:11:08Z +**Boundary:** no payment sent; no wallet action; no settlement attempted; no +revenue claimed + +## Buyer Problem + +Autonomous finance and execution agents can discover a Base token route before +they know whether the token, pair, or execution path is safe enough to spend +against. `safe_swap_preflight` gives the buyer agent a compact decision before +funds move. + +## Expected Request Shape + +```json +{ + "workflow": "safe_swap_preflight", + "chain": "base", + "token_in": "USDC", + "token_out": "", + "amount_usdc": 25, + "route": { + "dex": "", + "pool_or_pair": "", + "slippage_bps": 100 + }, + "buyer_context": { + "agent_type": "finance_execution_agent", + "intent": "pre_spend_swap_risk_check", + "will_execute_if_go": true + } +} +``` + +Minimum useful fields: + +- `workflow`: `safe_swap_preflight` +- `chain`: `base` +- `token_out`: token symbol or address being considered +- `amount_usdc`: planned spend size +- `route.dex` or `route.pool_or_pair`: intended execution path +- `buyer_context.intent`: `pre_spend_swap_risk_check` + +Requests must not include private keys, seed phrases, wallet signing material, +auth tokens, custody instructions, or instructions for Fia Signals to move +assets. + +## Sample Decisions + +### GO + +```json +{ + "decision": "GO", + "workflow": "safe_swap_preflight", + "chain": "base", + "summary": "No blocking token or route risk found for the proposed swap.", + "risk_flags": [], + "execution_note": "Proceed within the buyer agent's own slippage, spend-limit, and signing controls.", + "evidence_needed_to_change": [ + "fresh honeypot flag", + "liquidity removal", + "route mismatch", + "new contract-risk signal" + ] +} +``` + +### CAUTION + +```json +{ + "decision": "CAUTION", + "workflow": "safe_swap_preflight", + "chain": "base", + "summary": "The route is not blocked, but the token or liquidity context has unresolved risk.", + "risk_flags": [ + "thin_liquidity", + "new_or_unverified_pair", + "high_slippage_requested" + ], + "execution_note": "Reduce size, lower slippage, or require an additional independent token check before spending.", + "evidence_needed_to_change": [ + "verified deeper liquidity", + "known-good route history", + "lower slippage", + "independent contract verification" + ] +} +``` + +### BLOCK + +```json +{ + "decision": "BLOCK", + "workflow": "safe_swap_preflight", + "chain": "base", + "summary": "The proposed swap should not execute because a blocking token or route risk was found.", + "risk_flags": [ + "honeypot_or_sell_restriction", + "malicious_contract_signal", + "route_mismatch", + "unacceptable_settlement_risk" + ], + "execution_note": "Do not spend, sign, or route funds through this path.", + "evidence_needed_to_change": [ + "blocking flag disproven by fresh independent source", + "safe replacement route", + "contract-risk remediation", + "manual operator override with documented rationale" + ] +} +``` + +## Public Proof Status + +No-spend readback on 2026-07-15T00:11:08Z: + +- `/token-safety/lite` returned HTTP `402`, confirming the unpaid x402 boundary. +- `virtuals-direct-buy.json?offering=safe_swap_preflight` returned HTTP `200`. +- The direct-buy manifest included `safe_swap_preflight` at `0.01` USDC context + with `required_funds: false`. + +This is distribution and readiness evidence only. It is not a paid call, not a +settlement, not an external buyer purchase, and not revenue. + +## Failure Modes + +- Unpaid route returns expected `402`: paywall is reachable; no paid evidence + exists. +- Paid route returns non-`200`: buyer payment or fulfillment path needs + diagnosis using non-secret status and job/payment references. +- Paid `200` returns no delivery row/hash: fulfillment evidence is incomplete. +- ACP/direct-buy pricing disagrees with public copy: buyer trust risk; fix copy + only with before/after readback. +- Discovery search does not surface Fia Signals: distribution/indexing problem, + not route-health proof. +- Request asks Fia Signals to sign, trade, custody funds, or move assets: out of + scope. +- Self-buy or team-paid probe succeeds: diagnostic only; do not classify as + revenue. + +## Revenue Boundary + +External revenue remains `USD 0.00` for this package. Revenue requires an +external non-team buyer, paid `200`, completed or settled job, non-secret tx/job +reference, buyer identity or wallet, and delivery row/hash. diff --git a/showcase/fia-signals-safe-swap-preflight/examples/live-endpoint-proof.md b/showcase/fia-signals-safe-swap-preflight/examples/live-endpoint-proof.md new file mode 100644 index 0000000..d68699d --- /dev/null +++ b/showcase/fia-signals-safe-swap-preflight/examples/live-endpoint-proof.md @@ -0,0 +1,63 @@ +# Live Endpoint Proof - Fia Signals Safe Swap Preflight + +**Captured:** 2026-07-21T06:21:17+1000 AEST local validation pass +**Mode:** no spend, no wallet signing, no settlement attempt +**Revenue classification:** readiness proof only, not revenue + +## Endpoints + +| Surface | Purpose | Expected unpaid result | +| --- | --- | --- | +| `/token-safety/lite` | lite pre-swap token safety boundary | HTTP `402` | +| `/token-safety/batch` | batch token safety for up to 5 Base/EVM contracts | HTTP `402` | +| `/contract-risk/batch` | batch contract risk for up to 5 Base/EVM contracts | HTTP `402` | +| `/smart-contract-risk/batch` | alias for contract-risk batch | HTTP `402` | +| `/virtuals-direct-buy.json?offering=safe_swap_preflight` | public direct-buy manifest | HTTP `200` | + +## No-Spend Readback Commands + +```bash +curl -i "https://x402.fiasignals.com/token-safety/lite?chain=base&token_address=0x4200000000000000000000000000000000000006" + +TOKEN_CSV="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913,0x4200000000000000000000000000000000000006" +curl -i "https://x402.fiasignals.com/token-safety/batch?chain=base&token_addresses=${TOKEN_CSV}" +curl -i "https://x402.fiasignals.com/contract-risk/batch?chain=base&token_addresses=${TOKEN_CSV}" +curl -i "https://x402.fiasignals.com/smart-contract-risk/batch?chain=base&token_addresses=${TOKEN_CSV}" + +curl -i "https://x402.fiasignals.com/virtuals-direct-buy.json?offering=safe_swap_preflight" +``` + +## Local Readback Result + +| Surface | HTTP status | Content type | +| --- | --- | --- | +| `/token-safety/lite` | `402` | `application/json` | +| `/token-safety/batch` | `402` | `application/json` | +| `/contract-risk/batch` | `402` | `application/json` | +| `/smart-contract-risk/batch` | `402` | `application/json` | +| `/virtuals-direct-buy.json?offering=safe_swap_preflight` | `200` | `application/json` | + +## Payment Boundary + +The batch endpoints are expected to answer unpaid callers with `402` and an x402 +challenge. A buyer agent should read the challenge, build the full x402 payment +payload for `accepts[0]`, then retry the same URL with `X-PAYMENT` or +`PAYMENT-SIGNATURE`. + +Do not send a bare wallet signature. Do not include private keys, seed phrases, +auth tokens, custody instructions, or signing material in the request. + +## What Counts + +This proof shows that the public endpoint boundary and direct-buy manifest are +reachable. It does not show a paid buyer, settled payment, completed job, or +delivery row. + +Revenue requires all of: + +- external non-team buyer +- paid `200` +- settlement or completed job +- non-secret transaction or job reference +- buyer identity or wallet +- delivery row or delivery hash diff --git a/showcase/fia-signals-safe-swap-preflight/examples/redacted-batch-risk-result.md b/showcase/fia-signals-safe-swap-preflight/examples/redacted-batch-risk-result.md new file mode 100644 index 0000000..9c46008 --- /dev/null +++ b/showcase/fia-signals-safe-swap-preflight/examples/redacted-batch-risk-result.md @@ -0,0 +1,66 @@ +# Redacted Batch-Risk Result Example + +This example shows the delivery shape a buyer agent should store after a paid +batch-risk call. Buyer identity, wallet, transaction reference, and any private +route metadata are intentionally redacted. + +```json +{ + "workflow": "pre_swap_gate", + "chain": "base", + "checked_count": 2, + "summary": { + "safe": 1, + "risky": 1, + "blocked": 0, + "error": 0 + }, + "results": [ + { + "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "verdict": "safe", + "action": "PROCEED", + "safety_score": 98, + "confidence": "high", + "reasons": [ + "known Base USDC contract", + "no blocking transfer restriction found" + ], + "sources": [ + "onchain contract read", + "token risk registry" + ] + }, + { + "address": "0x4200000000000000000000000000000000000006", + "verdict": "risky", + "action": "CAUTION", + "safety_score": 74, + "confidence": "medium", + "reasons": [ + "route relies on volatile liquidity", + "confirm slippage and pool freshness before execution" + ], + "sources": [ + "route context", + "liquidity readback" + ] + } + ], + "adapted_swap_gate": { + "decision": "CAUTION", + "execution_note": "Cap spend, lower slippage, or require operator confirmation before routing.", + "source": "Fia Signals batch risk x402" + }, + "delivery_evidence": { + "job_ref": "redacted", + "tx_ref": "redacted", + "buyer": "redacted", + "delivery_row_hash": "redacted" + } +} +``` + +Do not classify this example as revenue. Revenue requires a genuine external +buyer, paid `200`, settlement or completed job, non-secret job or transaction +reference, buyer identity or wallet, and delivery row or hash. diff --git a/showcase/fia-signals-safe-swap-preflight/showcase.json b/showcase/fia-signals-safe-swap-preflight/showcase.json new file mode 100644 index 0000000..227dd51 --- /dev/null +++ b/showcase/fia-signals-safe-swap-preflight/showcase.json @@ -0,0 +1,80 @@ +{ + "slug": "fia-signals-safe-swap-preflight", + "title": "Base Swap Risk Preflight by Fia Signals", + "tagline": "Blocks rugpull, honeypot, contract-risk, and unsafe Base swap routes before an execution agent signs", + "description": "Base Swap Risk Preflight packages Fia Signals as a buyer-native token safety gate for autonomous finance and execution agents. It covers the proven batch-risk primitive buyers previously repeated across token-safety and contract-risk checks, then adapts the result into GO / CAUTION / BLOCK before funds move. The showcase includes no-spend live endpoint proof, an OpenClaw Base Ops adapter, and a redacted delivery example, while keeping revenue at USD 0 until an external non-team buyer completes and settles a paid job.", + "status": "no-spend buyer workflow packet with batch-risk adapter and live endpoint proof", + "topic": "commerce", + "topics": [ + "commerce", + "security", + "x402", + "base", + "token-safety", + "pre-spend", + "pre-swap", + "batch-risk", + "rugpull", + "honeypot", + "base-swap-risk", + "execution-agent" + ], + "hidden": false, + "builder": { + "name": "Fia Signals", + "url": "https://x402.fiasignals.com" + }, + "links": { + "repo": "https://github.com/Odds7/acp-cli-demos/tree/agent/fia-signals-safe-swap-preflight/showcase/fia-signals-safe-swap-preflight", + "demo": "https://github.com/Odds7/acp-cli-demos/blob/agent/fia-signals-safe-swap-preflight/showcase/fia-signals-safe-swap-preflight/examples/live-endpoint-proof.md", + "share": "https://x402.fiasignals.com/virtuals-direct-buy.json?offering=safe_swap_preflight", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Fia%20Signals%20Safe%20Swap%20Preflight&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20The%20pre-spend%20GO%2FCAUTION%2FBLOCK%20gate%20is%20useful%0A-%20The%200.01%20USDC%20direct-buy%20context%20needs%20clearer%20proof%0A-%20I%20want%20this%20as%20a%20native%20ACP%20buyer%20job%20next%0A%0ANotes%3A%0A" + }, + "primitives": [ + "wallet", + "acp" + ], + "visual": { + "kind": "live endpoint proof + Base Ops adapter", + "eyebrow": "base + token safety + rugpull + x402", + "title": "pre-swap batch-risk gate", + "posterUrl": "https://raw.githubusercontent.com/Odds7/acp-cli-demos/agent/fia-signals-safe-swap-preflight/showcase/fia-signals-safe-swap-preflight/assets/poster.png" + }, + "skills": [], + "artifacts": [ + { + "label": "Buyer workflow packet - route shape, GO/CAUTION/BLOCK outputs, proof boundary", + "href": "https://github.com/Odds7/acp-cli-demos/blob/agent/fia-signals-safe-swap-preflight/showcase/fia-signals-safe-swap-preflight/examples/buyer-workflow-packet.md", + "kind": "proof" + }, + { + "label": "Live endpoint proof - unpaid 402 boundaries and direct-buy readback", + "href": "https://github.com/Odds7/acp-cli-demos/blob/agent/fia-signals-safe-swap-preflight/showcase/fia-signals-safe-swap-preflight/examples/live-endpoint-proof.md", + "kind": "proof" + }, + { + "label": "OpenClaw Base Ops batch-risk adapter - copy-paste integration commands and schemas", + "href": "https://github.com/Odds7/acp-cli-demos/blob/agent/fia-signals-safe-swap-preflight/showcase/fia-signals-safe-swap-preflight/examples/base-ops-batch-risk-adapter.md", + "kind": "docs" + }, + { + "label": "Redacted batch-risk result example - delivery shape without buyer or wallet secrets", + "href": "https://github.com/Odds7/acp-cli-demos/blob/agent/fia-signals-safe-swap-preflight/showcase/fia-signals-safe-swap-preflight/examples/redacted-batch-risk-result.md", + "kind": "proof" + }, + { + "label": "Fia Signals Safe Swap Preflight package README", + "href": "https://github.com/Odds7/acp-cli-demos/blob/agent/fia-signals-safe-swap-preflight/showcase/fia-signals-safe-swap-preflight/README.md", + "kind": "docs" + } + ], + "soul": { + "href": "https://github.com/Odds7/acp-cli-demos/blob/agent/fia-signals-safe-swap-preflight/showcase/fia-signals-safe-swap-preflight/soul.md", + "summary": "Public agent context: pre-spend Base swap-risk decisions, no-signing boundaries, and revenue-proof requirements." + }, + "feedbackPrompts": [ + "Would a Base execution agent call this before routing a swap if it returns GO / CAUTION / BLOCK from batch token and contract risk?", + "Which buyer-search terms should rank first: token safety, pre swap, rugpull, honeypot, contract risk, or Base swap risk?", + "Should the next native job expose token-safety/batch, contract-risk/batch, or one combined pre-swap gate?" + ] +} diff --git a/showcase/fia-signals-safe-swap-preflight/soul.md b/showcase/fia-signals-safe-swap-preflight/soul.md new file mode 100644 index 0000000..39db513 --- /dev/null +++ b/showcase/fia-signals-safe-swap-preflight/soul.md @@ -0,0 +1,30 @@ +# Fia Signals Safe Swap Preflight Soul + +Fia Signals Safe Swap Preflight is a pre-spend trust gate for Base finance and +execution agents. It helps a buyer agent decide whether a proposed token swap +should proceed before the agent routes funds, signs, or moves assets. + +## Operating Boundary + +- Read proposed token and route context. +- Return GO / CAUTION / BLOCK with concise risk flags. +- State what evidence would change the decision. +- Do not sign transactions. +- Do not custody funds. +- Do not route swaps. +- Do not provide private-key, seed phrase, auth-token, or wallet-control + handling. +- Do not classify unpaid checks, self-buys, control-wallet probes, or team-paid + diagnostics as revenue. + +## Buyer Fit + +Use this workflow when a finance or execution agent needs a cheap preflight +decision before spending against a Base token route. Do not use it as a trading +executor, portfolio manager, custody layer, or post-trade monitoring dashboard. + +## Proof Boundary + +Public readback shows route and pricing readiness only. External revenue proof +requires an external non-team buyer, paid success, settlement or completed job, +non-secret job/transaction reference, and delivery evidence. diff --git a/showcase/geodesics-gasless-swaps/README.md b/showcase/geodesics-gasless-swaps/README.md new file mode 100644 index 0000000..c55e25c --- /dev/null +++ b/showcase/geodesics-gasless-swaps/README.md @@ -0,0 +1,40 @@ +# Geodesics Gasless Swaps + +Gasless, self-custodial swaps for agents across 7 EVM chains (Base, Ethereum, Arbitrum, +Optimism, Polygon, BNB Chain, Robinhood Chain) and Solana. An agent requests a quote, signs one +operation, and the asset settles into its own wallet, typically in 5 to 15 seconds including +cross-chain; gas and fees come out of the input token, so the wallet never needs a native gas +token. Geodesics never receives a private key and cannot alter what the wallet signed. + +Launched on Robinhood Chain through Virtuals on 23 July 2026; one of the first live projects on +Robinhood Chain. + +## Proof + +- [X demo video, 2:53, uncut](https://x.com/Geodesics_ai/status/2080197523403083800): an + existing EconomyOS agent goes from `geodesics init` to a settled cross-chain swap + (25 USDG on Robinhood Chain into VIRTUAL on Base) in about 3 minutes, with the EconomyOS + dashboard and terminal side by side. +- [Launch article](https://x.com/Geodesics_ai/status/2080129915064656119): full product + walkthrough, custody model, and integration paths. +- Reproduce it yourself: [Quickstart](https://docs.geodesics.ai/quickstart), self-serve API + keys at [console.geodesics.ai](https://console.geodesics.ai). + +## Skill + +`skills/geodesics-swaps/SKILL.md` is a snapshot of the skill shipped inside the published +[`@geodesics-protocol/cli`](https://www.npmjs.com/package/@geodesics-protocol/cli) npm package; +`geodesics init` installs it into Claude Code, Cursor, or a custom path for other runtimes. The +npm package is the source of truth; this copy is committed for review per contribution +guidelines. + +The skill covers quote, swap, withdraw, balance, and status with `--json` output, typed error +codes with documented recovery actions (`NEEDS_DELEGATION`, `NEEDS_LARGER_SIZE`, `NO_ROUTE`, +and the rest), explicit spend-approval gates, and stop conditions for unattended agent loops. + +## Redaction notes + +All artifacts are public pages. The API key shown during setup in the demo video was revoked +before publication, and the on-camera signer was removed from the demo agent; no live +credentials, wallet key material, or private account data appear in this package or the linked +proof. diff --git a/showcase/geodesics-gasless-swaps/showcase.json b/showcase/geodesics-gasless-swaps/showcase.json new file mode 100644 index 0000000..22f8baa --- /dev/null +++ b/showcase/geodesics-gasless-swaps/showcase.json @@ -0,0 +1,70 @@ +{ + "slug": "geodesics-gasless-swaps", + "title": "Geodesics Gasless Swaps", + "tagline": "Gives any ACP/EconomyOS or local agent gasless cross-chain swaps with one signature, settling into its own wallet in seconds", + "description": "Geodesics turns tokens an agent holds into tokens it wants on the chain it wants, gasless and self-custodial, across 7 EVM chains and Solana; gas and fees come out of the input token. An existing EconomyOS agent is set up and swapping in under five minutes with the CLI, any raw-key wallet onboards itself during its first swap, and the packaged skill lets Claude Code, Codex, or Cursor drive swaps unattended. Proof: an uncut 3-minute video onboarding an EconomyOS agent and settling a 25 USDG swap from Robinhood Chain into VIRTUAL on Base.", + "status": "live", + "topic": "commerce", + "topics": ["swaps", "gasless", "cross-chain", "robinhood-chain", "solana", "wallets", "skills"], + "builder": { + "name": "Geodesics", + "url": "https://geodesics.ai" + }, + "links": { + "repo": "https://www.npmjs.com/package/@geodesics-protocol/cli", + "demo": "https://docs.geodesics.ai/quickstart", + "video": "https://x.com/Geodesics_ai/status/2080197523403083800", + "share": "https://x.com/Geodesics_ai/status/2080197523403083800", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Geodesics%20Gasless%20Swaps" + }, + "primitives": ["wallet", "token", "acp"], + "visual": { + "kind": "x demo video", + "eyebrow": "geodesics cli + economyos", + "title": "gasless cross-chain swaps", + "posterUrl": "https://pbs.twimg.com/amplify_video_thumb/2080195534929698817/img/KI2m20Hs0xGe5mW1.jpg", + "videoUrl": "https://video.twimg.com/amplify_video/2080195534929698817/vid/avc1/2542x1266/98Vwhg2JtuyZ8zcs.mp4?tag=29", + "videoLabel": "Watch the 2:53 demo on X" + }, + "skills": [ + { + "name": "geodesics-swaps", + "href": "https://docs.geodesics.ai/integrations/agent-skill", + "sourcePath": "showcase/geodesics-gasless-swaps/skills/geodesics-swaps", + "summary": "Reusable swap skill for agent runtimes: quote, swap, withdraw, balance, and status over the Geodesics CLI, with JSON output, typed error recovery, spend approval gates, and stop conditions. Works with a Virtuals ACP wallet or any raw-key wallet.", + "install": "npm i -g @geodesics-protocol/cli\ngeodesics init" + } + ], + "artifacts": [ + { + "label": "X demo video (2:53, uncut)", + "href": "https://x.com/Geodesics_ai/status/2080197523403083800", + "kind": "video" + }, + { + "label": "Quickstart: any agent to its first swap", + "href": "https://docs.geodesics.ai/quickstart", + "kind": "docs" + }, + { + "label": "Launch article", + "href": "https://x.com/Geodesics_ai/status/2080129915064656119", + "kind": "writeup" + }, + { + "label": "Skill source (snapshot of the npm-packaged skill)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/geodesics-gasless-swaps/skills/geodesics-swaps", + "kind": "skill" + }, + { + "label": "Geodesics CLI on npm", + "href": "https://www.npmjs.com/package/@geodesics-protocol/cli", + "kind": "package" + } + ], + "feedbackPrompts": [ + "Which chain or token pair should the rails support next?", + "What would make the skill easier to drop into your agent stack?", + "Are you building on Robinhood Chain and missing a route you need?" + ] +} diff --git a/showcase/geodesics-gasless-swaps/skills/geodesics-swaps/SKILL.md b/showcase/geodesics-gasless-swaps/skills/geodesics-swaps/SKILL.md new file mode 100644 index 0000000..27ec20d --- /dev/null +++ b/showcase/geodesics-gasless-swaps/skills/geodesics-swaps/SKILL.md @@ -0,0 +1,230 @@ +# Geodesics Swap Skill + +Gasless cross-chain swaps for agents. One command turns tokens the agent holds into tokens it +wants, on the chain it wants, settled into its own wallet in seconds. No gas token needed, no +approval transactions, no funds ever held by Geodesics: the agent's own wallet signs every swap. + +Works for EVM chains (Base, Ethereum, Arbitrum, Optimism, Polygon, BNB, Robinhood Chain) and Solana, in both +directions. + +> **Setup is a one-time user action.** `geodesics init` is interactive, so you (the agent) cannot run +> it. If the credentials below are not configured yet (for example, a command returns an auth error), +> tell the user to run `geodesics init` once in their own terminal, then retry. + +## Install + +``` +npm i -g @geodesics-protocol/cli +``` + +Requires Node >= 20. Verify with `geodesics --help`. Run `geodesics init` once for guided setup +(installs this skill for your AI tool, for the current project or globally for your user, writes +the `.env`, and shows the wallet to fund). + +## Configure + +`geodesics init` walks through everything below interactively, validates each value, and saves the +credentials to a `.env` in the current project by default (or `~/.geodesics/.env` for all projects if +you choose); wallet keys go to the OS keychain instead when one is available, encrypted at rest. +Without a keychain, init asks before saving a key to the `.env` as plain text (a normal pattern for +agent tooling); `geodesics init --allow-plaintext-key` pre-approves this for unattended setups. +Load order for keys: a shell env var wins, then the keychain, then a `.env` value. For all other +variables: a working-directory `.env` wins, then `~/.geodesics/.env`, and real shell env vars +override both. Agents typically skip `init` and set these as environment variables. + +The wallet profile is chosen by the credentials you set. **Own wallet** (any raw key; the wallet +onboards itself at its first swap from each chain, gasless): set the address + raw key, no wallet +id. **Virtuals ACP agent wallet**: setting `AGENT_WALLET_ID` selects it, and the signer key is +then the base64 `MIG…` authorization key from the dashboard. + +| Variable | Required | What it is | +|---|---|---| +| `GEODESICS_API_KEY` | yes | Your Geodesics API key | +| `AGENT_WALLET_ADDRESS` | for EVM swaps | The agent's EVM wallet address (own wallet: derived from the key) | +| `AGENT_SIGNER_PRIVATE_KEY` | yes, unless `init` stored it in the OS keychain | Own wallet: a raw EVM private key (64 hex chars). Virtuals: the swapping signer's base64 `MIG…` key, shown once at creation (see onboarding). As an env var it overrides the keychain copy | +| `AGENT_WALLET_ID` | Virtuals only | The agent wallet's Privy wallet id; setting it selects the Virtuals profile | +| `AGENT_SOLANA_WALLET_ADDRESS` | for swaps to or from Solana | The agent's Solana address | +| `AGENT_SOLANA_SECRET_KEY` | own wallet, Solana-origin swaps | Base58 Solana secret key (the full 64-byte keypair secret) | +| `AGENT_SOLANA_WALLET_ID` | Virtuals, Solana-origin swaps | The Solana Privy wallet id (differs from EVM) | +| `SOLANA_RPC_URL` | optional | Solana RPC (defaults to the public endpoint) | +| `GEODESICS_API_URL` | optional | Defaults to `https://api.geodesics.ai` | + +## One-time onboarding (agent owner) + +**Own wallet**: `geodesics init` creates (or imports) the keys, shows the addresses, and stores +the keys safely; then fund the wallet with the input token on the origin chain (e.g. USDG on +Robinhood Chain or USDC on Base; gas and fees come out of the input). There is no other setup: +the wallet onboards itself during its first swap from each chain, gasless. + +**Virtuals ACP agent wallet**: + +1. **Signer key.** Create the swapping signer on the Virtuals dashboard (Wallet tab, Signers + section, Add Key) and copy the **private key when it is shown at creation**: a long base64 value starting + `MIG`. It is shown only once; afterwards the dashboard displays only the public half + (starting `MFkw`), which will not work here. +2. **Signer policy.** The swapping signer must be allowed to sign non-Virtuals transactions + unattended: give it **No Policy**, or (production-safe) a **custom allowlist** scoped to the + swap contracts. Under the default "Virtuals Only" policy every swap pauses for manual + approval. +3. **Fund the wallet BEFORE the first swap.** The agent needs the input token on the origin + chain (e.g. USDG on Robinhood Chain or USDC on Base). Nothing else: gas and fees come out of + the input. An unfunded wallet fails at activation with a clear error. +4. **Delegation is automatic.** The first swap from a new EVM origin chain onboards the wallet + (own wallet: the swap itself carries a one-time signed authorization; Virtuals: a one-time + activation of a few seconds, paid from the wallet's stable on that chain). No setup step. + +## Commands + +All commands support `--json` (single JSON object on stdout, no progress lines). A `--json` swap +result may carry a `warnings` array: non-fatal notices the agent should read and act on (e.g. a +chain-onboarding step to run before swapping back out). Interactive prompts never appear in +`--json` mode; the manual alternative is reported as a warning instead. + +### Swap + +``` +geodesics swap --token-in usdc --chain-in base --amount-in 25 --token-out virtual --chain-out base +geodesics swap --token-in usdc --chain-in base --amount-in 5 --token-out sol --chain-out solana +geodesics swap --token-in sol --chain-in solana --amount-in 0.1 --token-out usdc --chain-out base +``` + +- `--token-in` / `--token-out`: alias (`usdc`, `virtual`, `weth`, `eth`, `pol`, `bnb`, `usdg`, + `sol`) or a raw token address on that chain. +- `--chain-in` / `--chain-out`: alias (`base`, `ethereum`, `arbitrum`, `optimism`, `polygon`, + `bnb`, `robinhood`, `solana`) or a numeric chain id. +- Chain notes: on BNB, `usdc` is the Binance-Peg token (18 decimals, handled automatically). + Robinhood Chain has no USDC; its stable is `usdg` and its gas token alias is `eth`. Both + directions are live: the first Robinhood-origin swap runs the usual one-time activation, + carried by USDG. +- `--amount-in` takes human units for aliased tokens. For a raw token address, pass + `--amount-raw` in the token's base units instead (decimals are not known for arbitrary tokens). +- `--max` sweeps the entire input-token balance: an EVM token, or an SPL token on Solana origin + (a native gas token like ETH/SOL cannot be swept). Cannot be combined with `--amount-in`/`--amount-raw`. +- `--recipient` overrides delivery (defaults to the agent's own wallet on the destination chain). +- `--slippage-bps` overrides max slippage (defaults per route: tight for stables, wider for + volatile tokens). +- `--dry-run` prices the swap and returns the quote without signing or submitting anything. +- One command does the whole route, including cross-chain. Never chain two swaps yourself unless + an error tells you to. + +The command blocks until the swap settles (usually 5-15 seconds; `--timeout-s` to extend) and +prints the result with `originTxHash` / `deliveryTxHash`. + +First-time destinations are handled automatically: + +- **Own wallet**: no destination preparation exists or is needed. Delivery works on any supported + chain, and the wallet onboards itself whenever it first swaps OUT of a chain, gasless. +- **Virtuals wallet** swapping INTO an EVM chain it has never used runs a one-time activation + flow first: ~1 USDC is piped from the origin chain's stable, the chain is activated, and the + remainder returns to the origin. Adds about a minute and a few cents, once per chain, and + guarantees the delivered assets can always swap back out. Keep ~1 USDC spare on the origin + chain for this. If the swap itself delivers the chain's stable (`usdc`/`usdg`), there is no + pipe: the chain is activated right after settlement instead. +- Swapping INTO Solana while the Solana wallet holds no SOL: delivery works, but swapping back + out later needs ~0.005 SOL. Interactive runs are offered a small USDC-to-SOL pipe first; + `--json` runs skip it and report the exact pipe command as a `warnings` entry, or pass + `--confirm-pipe` to run the pipe automatically. Run it before you plan to swap out of Solana. + +### Withdraw + +``` +geodesics withdraw --chain base --amount 25 --to 0x… +geodesics withdraw --chain base --max --to 0x… --chain-out arbitrum +geodesics withdraw --chain solana --amount 10 --to +``` + +Moves the chain's canonical USD stable (USDC, or its per-chain equivalent: Binance-Peg USDC on +BNB, USDG on Robinhood Chain) to another wallet, gasless. The token is resolved from the chain, +so there is no `--token` flag; the command prints the stable balance at the start. + +- `--to` is required. Same-chain it must be a wallet OTHER than the agent's own (a same-chain + transfer to yourself does nothing and is refused). +- `--chain-out` delivers on another chain instead; the destination chain's canonical stable is + delivered. +- `--amount` takes human units (`--amount-raw` base units, `--max` sweeps the stable balance). +- Same-chain withdrawals are priced with a small transfer fee taken from the amount (the fee is + what makes the transfer gasless); on Solana the wallet's own SOL pays the network fee instead. +- To move any OTHER token to another wallet, swap it or use `geodesics swap --recipient`; only + the chain's stable can be transferred same-chain. +- `--dry-run`, `--slippage-bps` (cross-chain only), `--timeout-s` work as in Swap. + +### Status + +``` +geodesics status --swap-id [--wait] [--timeout-s 300] +``` + +### Balance + +``` +geodesics balance --chain base --token usdc +geodesics balance --chain solana --token sol --json +geodesics balance --chain ethereum --token 0x… --wallet 0x… +``` + +Reads one token's balance on one chain over public RPCs; needs no API key and no signer. +`--chain` takes a chain alias, `--token` a token alias valid on that chain or a raw token +address/mint, `--wallet` overrides the agent wallet for that chain family. `--json` returns +`{ chainId, chain, wallet, token, symbol?, raw, formatted? }` where `raw` is a base-unit decimal +string (`formatted` is human units; absent when a raw address's decimals are unknown). + +Reads automatically fall back across several public RPC providers per chain, so a single +rate-limited endpoint does not fail the command. To pin a dedicated endpoint, set the chain's +env var (`ETH_RPC_URL`, `BASE_RPC_URL`, `ARBITRUM_RPC_URL`, `OPTIMISM_RPC_URL`, +`POLYGON_RPC_URL`, `BNB_RPC_URL`, `ROBINHOOD_RPC_URL`, `SOLANA_RPC_URL`); it is tried first, +with the public endpoints kept as fallback. If every endpoint fails the command exits 1 with an +error; retry, since it does not mean a zero balance. + +### Delegation check / manual activation (rarely needed; swap does this automatically) + +``` +geodesics delegation --chain base +geodesics activate --chain base +``` + +### Slippage + +The server picks a per-route default (about 3% for volatile outputs, 0.5% for stables), so most +swaps need nothing. Override for one swap with `--slippage-bps `, or set a persistent +default: + +``` +geodesics config set slippage 300 # 300 bps = 3%, stored in .geodesics.json +geodesics config show +geodesics config unset slippage # back to the server default +``` + +Precedence: `--slippage-bps` (this swap) > `config set slippage` > server default. When a +non-default slippage is in effect the swap prints a `slippage: ` line. Setting 1000 bps or +higher needs interactive confirmation, or `--yes` in a script. Raising slippage is the usual fix +for a `refunded` swap (the price moved past tolerance, common on volatile or small cross-chain +swaps). + +## Errors and what to do about them + +| Code | Meaning | What the agent should do | +|---|---|---| +| `NEEDS_DELEGATION` | Origin chain not activated yet | Virtuals: run `geodesics activate --chain `, then retry (the swap command normally handles this automatically). Own wallet: should not occur; retry the swap once | +| `UNSUPPORTED_DELEGATION` / "delegated to another provider" | The wallet is already delegated elsewhere on that chain; Geodesics never replaces an existing delegation | Tell the user: swap from a chain where the wallet is free, or use a different wallet | +| `NO_ROUTE` | No route for this pair (or same token on the same chain) | Pick a different output token or chain | +| `NEEDS_LARGER_SIZE` | Amount too small to be economical for this route | Retry with a larger `--amount-in` | +| `SLIPPAGE` | Price moved beyond tolerance | Retry; if it repeats, raise `--slippage-bps` | +| `INSUFFICIENT_BALANCE` | Wallet lacks the input amount | Fund the wallet or lower the amount | +| `NEEDS_SOL_TOPUP` | Solana-origin swap needs a little SOL for network fees | Rerun the swap with `--confirm-pipe` (pipes ~1.5 USDC from Base into SOL automatically), or swap a few USDC into `sol` first and retry | +| `UPSTREAM_TIMEOUT` | Upstream provider timeout | Retry once after a few seconds | +| `TIMEOUT` (with a `swapId`) | Still settling at the wait deadline | Poll `geodesics status --swap-id `; the swap usually still settles | +| "Privy returned an empty signature" | `AGENT_SOLANA_WALLET_ID` is missing or set to the EVM wallet id | Fix the env var (the Solana Privy id is separate from the EVM one) | +| "Activation ... rejected by the gas sponsor" | The wallet holds no stable on that chain; activation is paid from it | Fund the wallet with `usdc` (or `usdg` on Robinhood Chain) there, then retry | +| "AGENT_SIGNER_PRIVATE_KEY looks like ..." | The key does not match the profile | Own wallet: raw hex key and no `AGENT_WALLET_ID`. Virtuals: base64 `MIG…` key with `AGENT_WALLET_ID` set | +| "controls 0x…, not the configured ..." | The stored key and `AGENT_WALLET_ADDRESS` disagree | Tell the user to re-run `geodesics init` (the address is derived from the key) | +| status `refunded` (result, exit 2) | The swap could not fill within slippage tolerance; the input was returned | Retry with a higher `--slippage-bps` (e.g. 300), or `geodesics config set slippage 300` | + +Exit codes: `0` success, `1` error (see JSON `error.code`), `2` the swap ended `failed` or +`refunded` (refunds return the input to the origin wallet). + +## Safety notes + +- Swaps move real funds. Use `--dry-run` to price a swap without executing it. +- An interrupted command does NOT cancel a submitted swap. Re-running the command gets a fresh + quote and can create a SECOND swap; check `geodesics status` (or the swap history) first to + find out what the interrupted run did before retrying. diff --git a/showcase/hatcher-virtuals-acp-workbench/README.md b/showcase/hatcher-virtuals-acp-workbench/README.md new file mode 100644 index 0000000..8464f84 --- /dev/null +++ b/showcase/hatcher-virtuals-acp-workbench/README.md @@ -0,0 +1,57 @@ +# Hatcher Virtuals ACP Workbench + +Hatcher Virtuals ACP Workbench is a managed agent-control surface for using Virtuals from inside Hatcher. + +The demo shows a Hatcher-managed agent moving through the Virtuals integration: + +1. Select Virtuals as an inference provider from the agent configuration page. +2. Open the agent wallet provider area and enable Virtuals access. +3. Set review-first access and budget controls. +4. Search the Virtuals ACP marketplace for provider agents. +5. Select a provider offering and prepare an ACP job draft. +6. Inspect HatcherLabs service packaging for publishing Hatcher-managed capabilities into ACP. + +The workbench is designed to add demand and execution volume to the Virtuals agent economy without replacing Virtuals Console. Hatcher keeps the operator-facing control layer, while Virtuals supplies the Compute and ACP marketplace primitives. + +## Demo Video + +- YouTube: https://youtu.be/DvtjysrHfzs + +The video shows the Hatcher site, the Virtuals provider selection surface, Virtuals access controls, ACP provider search, job-draft preparation, and the HatcherLabs services area. + +## Where The Integration Lives + +- Virtuals inference models: `Hatcher Dashboard -> Agent -> Config -> Provider / Model -> Virtuals` +- ACP jobs, access controls, budget controls, and HatcherLabs services: `Hatcher Dashboard -> Agent -> Wallet -> Virtuals` + +## What The Workbench Does + +- Routes agent inference through Virtuals Compute from Hatcher-managed agents. +- Gives each agent a visible Virtuals access switch before ACP matching or drafting. +- Sets operator-controlled daily and per-job budget limits. +- Searches the Virtuals ACP marketplace for provider agents and offerings. +- Prepares reviewed ACP job drafts that show provider, offering, budget, and command-plan context. +- Packages selected HatcherLabs capabilities as ACP offerings that can be prepared and published through the Virtuals operator path. +- Keeps sensitive API keys, private wallet material, and runtime credentials out of the public UI and public showcase artifacts. + +## Review-First Boundary + +This showcase intentionally demonstrates a review-first flow. The Hatcher UI prepares and displays the ACP job draft before a funded action is executed. Operators can inspect provider identity, offering name, maximum budget, requirements, and command plan before approving any live ACP action. + +The public proof does not include private API keys, private prompts, full internal job records, wallet private material, OTPs, access tokens, or user account data. + +## Package Contents + +- `showcase.json` - card-ready EconomyOS Showcase manifest. +- `soul.md` - public/redacted HatcherLabs agent context and boundaries. +- `examples/prompt.md` - reusable demo prompt for running the workbench flow. +- `examples/result-redacted.md` - redacted result report from the public demo. +- `skills/hatcher-virtuals-acp-workbench/SKILL.md` - reusable operator skill for running this workflow safely. + +## Links + +- Hatcher: https://hatcher.host +- Demo video: https://youtu.be/DvtjysrHfzs +- Virtuals ACP Scan: https://app.virtuals.io/acp/scan + +Built by Hatcher Labs for the Virtuals agent economy. diff --git a/showcase/hatcher-virtuals-acp-workbench/examples/prompt.md b/showcase/hatcher-virtuals-acp-workbench/examples/prompt.md new file mode 100644 index 0000000..86ef063 --- /dev/null +++ b/showcase/hatcher-virtuals-acp-workbench/examples/prompt.md @@ -0,0 +1,30 @@ +# Demo Prompt + +Use the Hatcher Virtuals ACP Workbench to prepare a reviewed ACP job draft. + +Goal: + +- use a Hatcher-managed agent, +- select Virtuals as the inference provider, +- enable Virtuals access, +- search ACP providers for an agent audit or market-research task, +- select one provider offering, +- prepare a draft only, +- and report the provider, offering, maximum budget, requirements, and next approval step. + +Suggested task brief: + +```text +Find a Virtuals ACP provider that can review an agent launch or perform a short market-research task. Prefer providers with clear offerings and low fixed pricing. Prepare a reviewed ACP job draft, but do not fund or submit the job until an operator approves it. +``` + +Expected output: + +- selected provider name, +- selected offering name, +- max budget, +- requirement payload summary, +- Hatcher job-draft status, +- approval gate before live ACP execution. + +Do not include private API keys, wallet private material, OTPs, full access tokens, private prompts, or user account records in the final report. diff --git a/showcase/hatcher-virtuals-acp-workbench/examples/result-redacted.md b/showcase/hatcher-virtuals-acp-workbench/examples/result-redacted.md new file mode 100644 index 0000000..966d2b9 --- /dev/null +++ b/showcase/hatcher-virtuals-acp-workbench/examples/result-redacted.md @@ -0,0 +1,57 @@ +# Hatcher Virtuals ACP Workbench - Redacted Result Report + +## Public Proof + +- Demo video: https://youtu.be/DvtjysrHfzs +- Builder: Hatcher Labs +- Site: https://hatcher.host +- Public ACP explorer: https://app.virtuals.io/acp/scan + +## Flow Captured + +1. Opened a Hatcher-managed agent. +2. Confirmed Virtuals appears as an inference provider in agent configuration. +3. Opened `Wallet -> Virtuals`. +4. Used the Virtuals access and budget-control panel. +5. Searched the Virtuals ACP marketplace for providers. +6. Reviewed ACP provider and offering information. +7. Prepared a review-first job draft rather than executing a funded ACP action immediately. +8. Opened the HatcherLabs services area for service packaging and publish preparation. + +## Redacted Draft Shape + +```json +{ + "client": "Hatcher-managed agent", + "provider": { + "name": "[redacted provider display name from marketplace search]", + "walletAddress": "[redacted public wallet/address in video context]" + }, + "offering": { + "name": "[redacted selected offering]", + "priceType": "fixed-or-marketplace-defined" + }, + "budget": { + "maxPerJobUsd": "[operator-controlled limit]", + "dailyBudgetUsd": "[operator-controlled limit]" + }, + "requirements": { + "task": "Find or review an agent/service candidate from the Virtuals ACP marketplace." + }, + "status": "draft-prepared-for-review", + "approvalGate": "operator approval required before live funding or submission" +} +``` + +## What Is Not Published + +- No private API keys. +- No environment variables. +- No private wallet keys or seed phrases. +- No OTPs, magic links, or account recovery material. +- No full private Hatcher user records. +- No claim that the demo funded or settled a live ACP job. + +## Reviewer Notes + +The demo is intentionally review-first. It proves the Hatcher UI integration, Virtuals provider selection, ACP marketplace search, job-draft preparation, and HatcherLabs service-packaging surface. A later showcase can add live funding and provider delivery proof once the operator decides which HatcherLabs service should be fully published as the first production ACP offering. diff --git a/showcase/hatcher-virtuals-acp-workbench/showcase.json b/showcase/hatcher-virtuals-acp-workbench/showcase.json new file mode 100644 index 0000000..ac6f3a9 --- /dev/null +++ b/showcase/hatcher-virtuals-acp-workbench/showcase.json @@ -0,0 +1,83 @@ +{ + "slug": "hatcher-virtuals-acp-workbench", + "title": "Hatcher Virtuals ACP Workbench", + "tagline": "A Hatcher-managed agent workbench for Virtuals Compute, ACP provider matching, review-first job drafts, and HatcherLabs service packaging.", + "description": "Hatcher Virtuals ACP Workbench shows a Hatcher-managed agent using Virtuals from its control layer: selecting Virtuals as an inference provider, enabling Virtuals access, setting budget controls, searching ACP providers, preparing a reviewed ACP job draft, and packaging HatcherLabs services for the same agent economy. The workflow is intentionally review-first: Hatcher surfaces provider, offering, budget, and command-plan context before an operator approves any funded ACP action.", + "status": "active showcase", + "topic": "agents", + "topics": [ + "agents", + "hatcher", + "virtuals", + "acp", + "compute", + "marketplace" + ], + "builder": { + "name": "Hatcher Labs", + "url": "https://hatcher.host" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/hatcher-virtuals-acp-workbench", + "demo": "https://youtu.be/DvtjysrHfzs", + "video": "https://youtu.be/DvtjysrHfzs", + "share": "https://youtu.be/DvtjysrHfzs", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Hatcher%20Virtuals%20ACP%20Workbench&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20The%20Hatcher%20managed%20ACP%20flow%20is%20clear%0A-%20The%20review-first%20job%20draft%20needs%20more%20proof%0A-%20The%20HatcherLabs%20service%20packaging%20needs%20more%20detail%0A%0ANotes%3A%0A" + }, + "primitives": [ + "wallet", + "acp" + ], + "visual": { + "kind": "youtube demo video", + "eyebrow": "hatcher + virtuals + acp", + "title": "managed acp workbench", + "posterUrl": "https://img.youtube.com/vi/DvtjysrHfzs/maxresdefault.jpg", + "videoLabel": "Watch the Hatcher Virtuals ACP Workbench demo on YouTube" + }, + "skills": [ + { + "name": "hatcher-virtuals-acp-workbench", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/hatcher-virtuals-acp-workbench/skills/hatcher-virtuals-acp-workbench", + "sourcePath": "showcase/hatcher-virtuals-acp-workbench/skills/hatcher-virtuals-acp-workbench", + "summary": "Reusable operator workflow for running a Hatcher-managed Virtuals ACP session: choose Virtuals Compute, enable access, match providers, prepare review-first job drafts, and package HatcherLabs services safely.", + "install": "cp -R showcase/hatcher-virtuals-acp-workbench/skills/hatcher-virtuals-acp-workbench ~/.agents/skills/\ncp -R showcase/hatcher-virtuals-acp-workbench/skills/hatcher-virtuals-acp-workbench ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Hatcher Virtuals ACP Workbench demo video", + "href": "https://youtu.be/DvtjysrHfzs", + "kind": "video" + }, + { + "label": "Showcase package README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/hatcher-virtuals-acp-workbench/README.md", + "kind": "docs" + }, + { + "label": "Reusable workbench skill", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/hatcher-virtuals-acp-workbench/skills/hatcher-virtuals-acp-workbench", + "kind": "skill" + }, + { + "label": "Demo prompt", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/hatcher-virtuals-acp-workbench/examples/prompt.md", + "kind": "docs" + }, + { + "label": "Redacted result report", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/hatcher-virtuals-acp-workbench/examples/result-redacted.md", + "kind": "proof" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/hatcher-virtuals-acp-workbench/soul.md", + "summary": "Public/redacted HatcherLabs agent context, user approval boundaries, and ACP workbench operating rules." + }, + "feedbackPrompts": [ + "Does the demo make the managed ACP provider matching flow clear?", + "Which HatcherLabs service should become the first fully live ACP offering?", + "What additional proof would make review-first ACP job drafting easier to trust?" + ] +} diff --git a/showcase/hatcher-virtuals-acp-workbench/skills/hatcher-virtuals-acp-workbench/SKILL.md b/showcase/hatcher-virtuals-acp-workbench/skills/hatcher-virtuals-acp-workbench/SKILL.md new file mode 100644 index 0000000..9730213 --- /dev/null +++ b/showcase/hatcher-virtuals-acp-workbench/skills/hatcher-virtuals-acp-workbench/SKILL.md @@ -0,0 +1,119 @@ +--- +name: hatcher-virtuals-acp-workbench +description: Run a Hatcher-managed Virtuals ACP session: select Virtuals Compute, enable access, match ACP providers, prepare review-first job drafts, and package HatcherLabs services without exposing secrets or funding jobs without approval. +version: 1.0.0 +author: Hatcher Labs +license: MIT +--- + +# Hatcher Virtuals ACP Workbench + +Use this skill when operating or reviewing the Hatcher x Virtuals integration from a Hatcher-managed agent. + +The skill is designed for review-first ACP work: the agent can search, match, prepare, and package, but live funding, publishing, or production mutation requires explicit operator approval. + +## When To Use + +- A Hatcher operator wants to route an agent through Virtuals-hosted inference. +- A Hatcher operator wants to search the Virtuals ACP marketplace from the agent wallet panel. +- A Hatcher operator wants to prepare an ACP job draft for review before funding or submission. +- A Hatcher operator wants to package a HatcherLabs capability as a candidate ACP service. +- A reviewer wants to verify that a public demo keeps secrets and private account data out of artifacts. + +## When Not To Use + +- Do not use this skill to bypass Virtuals Console, ACP CLI, or Hatcher approval controls. +- Do not execute funded ACP jobs unless the operator explicitly approves provider, offering, requirements, and maximum spend. +- Do not publish HatcherLabs services as ACP offerings without operator approval. +- Do not expose API keys, access tokens, OTPs, wallet private keys, seed phrases, private prompts, runtime logs, or private user records. +- Do not claim job completion, escrow, settlement, or provider delivery unless the proof artifact shows that exact step. + +## Required Inputs + +- Hatcher account access with permission to open the target agent. +- Target Hatcher agent name or ID. +- Task brief for the ACP provider search. +- Maximum per-job budget and daily budget. +- Whether the operator wants draft-only, live job execution, or HatcherLabs service publishing. +- Public-safe proof target: video, screenshot, redacted report, or package README. + +## Tools And Credentials + +- Hatcher web UI at `https://hatcher.host`. +- Optional Virtuals ACP explorer at `https://app.virtuals.io/acp/scan`. +- Hatcher-managed Virtuals server key configured server-side by Hatcher. +- No user should paste private API keys, seed phrases, or access tokens into the prompt or public artifacts. + +## Workflow + +1. Open the target Hatcher agent. +2. Go to `Config -> Provider / Model`. +3. Select `Virtuals` as the provider and choose the appropriate Virtuals-hosted model. +4. Go to `Wallet -> Virtuals`. +5. Turn on Virtuals access if it is off. +6. Set daily and per-job budget controls. +7. Use `Find help` to describe the task brief. +8. Run provider matching or marketplace search. +9. Inspect provider names, offerings, pricing, and fit. +10. Select one provider offering. +11. Prepare a job draft. +12. Stop for operator review before any funded ACP action. +13. If packaging HatcherLabs services, use the Hatcher services area to preview the publish payload before publishing. +14. Produce a redacted report with provider, offering, budget, requirements summary, and approval status. + +## Approval Gates + +Stop and ask for explicit approval before: + +- enabling access on a production agent, +- increasing budget limits, +- creating or funding a live ACP job, +- accepting an ACP provider job, +- publishing a HatcherLabs service, +- changing production model/provider configuration, +- or publishing proof that includes sensitive or private context. + +Approval must name the provider/offering or service, maximum spend, and whether the operator wants draft-only or live execution. + +## Stop Conditions + +Stop without executing live actions if: + +- the provider identity, offering, or pricing is unclear, +- the selected provider does not match the requested task, +- the budget differs from operator authorization, +- Hatcher access or Virtuals access is unavailable, +- the UI exposes private account or credential data that would be captured in public proof, +- an ACP command would fund, submit, publish, or mutate production state without approval, +- or any output would reveal API keys, private wallet material, OTPs, access tokens, private prompts, or private account records. + +## Validation + +For a public showcase package: + +```bash +node scripts/validate-showcase.mjs +``` + +For runtime evidence, verify: + +- the demo shows Virtuals as a provider in Hatcher, +- the demo shows the Virtuals wallet panel, +- access and budget controls are visible, +- ACP provider search or matching is visible, +- the job is presented as a reviewed draft unless live execution proof is included, +- and all sensitive material is redacted. + +## Output Contract + +Return: + +- target Hatcher agent, +- selected Virtuals model or provider state, +- ACP search query, +- selected provider and offering, +- budget limits, +- draft status or publish-preview status, +- explicit approval gate before live execution, +- public proof link or redacted artifact path, +- and any reviewer caveats. diff --git a/showcase/hatcher-virtuals-acp-workbench/soul.md b/showcase/hatcher-virtuals-acp-workbench/soul.md new file mode 100644 index 0000000..1d16447 --- /dev/null +++ b/showcase/hatcher-virtuals-acp-workbench/soul.md @@ -0,0 +1,36 @@ +# HatcherLabs Agent Context + +HatcherLabs is the public Hatcher agent used to demonstrate a managed Virtuals ACP workflow from the Hatcher control layer. + +## Purpose + +The agent is used to show how a Hatcher-managed runtime can: + +- use Virtuals as an inference provider, +- search the Virtuals ACP marketplace, +- prepare review-first ACP job drafts, +- expose Hatcher-managed capabilities as service packages, +- and keep operator approval in front of live funded actions. + +## Public Boundaries + +- Do not publish private API keys, environment variables, access tokens, OTPs, magic links, wallet private keys, seed phrases, or private account records. +- Do not claim an ACP job was funded, escrowed, or completed unless the proof artifact shows that exact step. +- Treat draft creation as review-first preparation, not final settlement. +- Keep provider matching explainable: provider name, offering, budget, requirements, and draft plan should be visible before approval. +- Keep Hatcher user data and internal runtime logs out of public showcase artifacts unless explicitly redacted. + +## Approval Gates + +Explicit operator approval is required before: + +- enabling Virtuals access for a production agent, +- increasing daily or per-job budget limits, +- creating or funding a live ACP job, +- publishing a HatcherLabs service as an ACP offering, +- exposing a new public proof artifact, +- or changing production agent configuration. + +## Review Preference + +The showcase favors concrete evidence over broad claims: a public video, redacted result report, visible Hatcher UI surfaces, inspectable manifest, and reusable skill. diff --git a/showcase/hydro-embodied-verify/assets/poster.jpg b/showcase/hydro-embodied-verify/assets/poster.jpg new file mode 100644 index 0000000..f585bb8 Binary files /dev/null and b/showcase/hydro-embodied-verify/assets/poster.jpg differ diff --git a/showcase/hydro-embodied-verify/examples/verify-report.md b/showcase/hydro-embodied-verify/examples/verify-report.md new file mode 100644 index 0000000..768d7a1 --- /dev/null +++ b/showcase/hydro-embodied-verify/examples/verify-report.md @@ -0,0 +1,82 @@ +# Redacted Verdict Report — Hydro Embodied-Data Verifier + +Two clips run through Hydro's live AI quality gate (`supabase/functions/reward` +in the [Hydro repo](https://github.com/hydroboticsdotco/hydrobotics)). Frames +are sampled from each upload and scored by a `gpt-4o-mini` vision model against +the task instructions. Contributor ids, storage paths, and wallet addresses are +redacted; the verdicts are the model's real output shape. + +--- + +## Case 1 — Accepted + +- **Task instructions:** "Pick up the cup, pour water into it, and place it back + on the table." +- **Input:** first-person clip, ~14s, 3 frames sampled. +- **Contribution id:** `contrib_****` (redacted) + +**Model verdict** + +```json +{ + "approved": true, + "score": 82, + "reason": "The hand reaches for the cup, pours water in, and returns it to the table. Full hand-object interaction is visible with stable framing and adequate lighting." +} +``` + +- **verdict:** `approved` (score 82 >= threshold 55) +- **outcome:** contribution credited in $HYDRO (off-chain balance, claimable at + token launch). + +--- + +## Case 2 — Rejected + +- **Task instructions:** "Fold a shirt completely, from flat to folded." +- **Input:** first-person clip, ~9s, 3 frames sampled. +- **Contribution id:** `contrib_****` (redacted) + +**Model verdict** + +```json +{ + "approved": false, + "score": 18, + "reason": "The shirt is picked up and moved but never folded; the task is not completed within the clip. Motion is also blurry in two of three frames." +} +``` + +- **verdict:** `rejected` (fails task completion and quality threshold) +- **outcome:** no credit issued. + +--- + +## Case 3 — Needs review + +- **Task instructions:** "Plug the cable into the wall socket." +- **Input:** first-person clip, ~6s, frames partially corrupted. +- **Contribution id:** `contrib_****` (redacted) + +**Model verdict** + +```json +{ + "approved": false, + "score": 0, + "reason": "Frames could not be reliably decoded; the socket and cable are not clearly visible. Not enough evidence to confirm task completion." +} +``` + +- **verdict:** `needs_review` (low confidence / bad input, not a fabricated + pass) — held for human review. + +--- + +## Notes + +- The verdict shape (`{approved, score, reason}`) is exactly what an ACP + evaluator would consume to accept or reject an embodied-video deliverable and + settle escrow. +- No secrets, keys, wallet material, or contributor identity appear in any + verdict output. diff --git a/showcase/hydro-embodied-verify/showcase.json b/showcase/hydro-embodied-verify/showcase.json new file mode 100644 index 0000000..d5ca23f --- /dev/null +++ b/showcase/hydro-embodied-verify/showcase.json @@ -0,0 +1,72 @@ +{ + "slug": "hydro-embodied-verify", + "title": "Hydro Embodied-Data Verifier", + "tagline": "Scores a first-person task video against its instructions and returns an approve/reject verdict with a 0-100 quality score and a reason", + "description": "Hydro runs every crowdsourced human demonstration clip through an AI quality gate: a vision model checks that the video genuinely performs the stated task, then returns a structured {approved, score, reason} verdict. Only verified clips are credited in $HYDRO, which keeps the embodied-data marketplace spam-resistant. This package ships that verification workflow as a reusable skill plus a redacted verdict report from the live pipeline. On-chain ACP Evaluator settlement is planned; this card is a hidden preview.", + "status": "hidden preview", + "topic": "agents", + "topics": ["verification", "embodied-ai", "data", "robotics", "vision"], + "builder": { + "name": "Hydrobotics", + "url": "https://hydrobotics.co" + }, + "links": { + "repo": "https://github.com/hydroboticsdotco/hydrobotics", + "demo": "https://hydrobotics.co", + "share": "https://hydrobotics.co", + "feedback": "https://github.com/hydroboticsdotco/hydrobotics/issues/new?title=Feedback%3A%20Hydro%20Embodied-Data%20Verifier&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20Useful%20as%20an%20ACP%20evaluator%0A-%20Needs%20a%20public%20verification%20endpoint%0A-%20Rubric%20should%20cover%20more%20dimensions%0A%0ANotes%3A%0A" + }, + "primitives": ["wallet", "email", "token"], + "visual": { + "kind": "live product page", + "eyebrow": "AI quality gate", + "title": "embodied-data verification", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/hydro-embodied-verify/assets/poster.jpg" + }, + "skills": [ + { + "name": "hydro-embodied-verify", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/hydro-embodied-verify/skills/hydro-embodied-verify", + "sourcePath": "showcase/hydro-embodied-verify/skills/hydro-embodied-verify", + "summary": "Reusable verification workflow that scores a first-person task video against its instructions with a vision model and returns a bounded {approved, score, reason} verdict. Designed to plug into the ACP Evaluator slot for embodied-video deliverables.", + "install": "cp -R showcase/hydro-embodied-verify/skills/hydro-embodied-verify ~/.agents/skills/\ncp -R showcase/hydro-embodied-verify/skills/hydro-embodied-verify ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Live product page - Hydro Data Verification", + "href": "https://hydrobotics.co", + "kind": "demo" + }, + { + "label": "Redacted verdict report from the live pipeline", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/hydro-embodied-verify/examples/verify-report.md", + "kind": "proof" + }, + { + "label": "Hydro app + reward function source", + "href": "https://github.com/hydroboticsdotco/hydrobotics", + "kind": "repo" + }, + { + "label": "Verification skill source", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/hydro-embodied-verify/skills/hydro-embodied-verify", + "kind": "skill" + }, + { + "label": "Verifier soul - rubric and verdict semantics", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/hydro-embodied-verify/soul.md", + "kind": "docs" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/hydro-embodied-verify/soul.md", + "summary": "Embodied-data verifier identity, scoring rubric, and approve/reject/needs-review verdict semantics." + }, + "feedbackPrompts": [ + "Would ACP benefit from an embodied-video evaluator that scores task completion, not just on-chain actions?", + "Should the verdict expose per-dimension rubric scores rather than a single 0-100 number?", + "What would you need before wiring this verifier into the ACP Evaluator slot with escrow settlement?" + ], + "hidden": true +} diff --git a/showcase/hydro-embodied-verify/skills/hydro-embodied-verify/SKILL.md b/showcase/hydro-embodied-verify/skills/hydro-embodied-verify/SKILL.md new file mode 100644 index 0000000..64f8c17 --- /dev/null +++ b/showcase/hydro-embodied-verify/skills/hydro-embodied-verify/SKILL.md @@ -0,0 +1,95 @@ +--- +name: hydro-embodied-verify +description: Score a first-person (egocentric) task video against its instructions with a vision model and return a bounded {approved, score, reason} verdict. Use to gate crowdsourced embodied-data uploads or to evaluate an embodied-video deliverable. +--- + +# Hydro Embodied-Data Verifier + +Judge whether a short first-person video genuinely performs a stated real-world +task (e.g. "pick up the cup, pour water, place it back"), and return a single +bounded verdict. This is the quality gate behind Hydro's embodied-data +marketplace, packaged so any agent can reuse it — including as the evaluator for +an embodied-video deliverable on the Agent Commerce Protocol (ACP). + +## When to use this skill + +- You need to decide if a demonstration clip actually completes the task it + claims, before paying or crediting the contributor. +- You are named as the evaluator on an ACP job whose deliverable is a + first-person task video, and you must accept or reject it. +- You want a consistent, spam-resistant quality score for embodied-video data. + +## When NOT to use this skill + +- The deliverable is not a task video (text, code, on-chain action) — use a + matching evaluator instead. +- You need to custody funds or move tokens. This skill only produces a verdict; + settlement is handled by the caller / ACP escrow. +- You need identity, personal data, or biometric analysis of the person in the + clip. Out of scope and not permitted. + +## Inputs + +- `video_url` — a readable URL (or local path) to the clip to judge. +- `task_instructions` — the exact task the clip is supposed to perform. +- `threshold` (optional, default 55) — minimum 0-100 score required to approve. + +## Tools, credentials, preconditions + +- A vision-capable model API key (e.g. an OpenAI `gpt-4o-mini` vision key) set in + the environment as `OPENAI_API_KEY`. No other credentials are required. +- Frame extraction: 3 evenly-spaced frames are sampled from the clip and passed + to the model with the task instructions. No full-video upload is needed. +- The model is asked to return strict JSON: `{ "approved": bool, "score": int, + "reason": string }`. + +## Approval gates + +- This skill performs **no spending, posting, account creation, deployment, or + production mutation**. It only reads a video and returns a verdict. +- If used inside an ACP job to release or refund escrow, the escrow action is a + separate step owned by the caller and requires explicit authorization of the + job id and settlement rail before it runs. + +## Stop conditions and handoff + +- Stop and return `needs_review` if frames cannot be extracted, the model output + is not valid JSON, or the model expresses low confidence. Do not fabricate a + pass. +- If the task instructions are missing or empty, stop and ask for them rather + than guessing. + +## Procedure + +1. Extract 3 representative frames from `video_url`. +2. Prompt the vision model with the frames + `task_instructions`, asking it to + judge task completion and quality against the rubric in `soul.md`. +3. Parse the strict-JSON response into `{approved, score, reason}`. +4. Apply the decision rule below. + +## Validation checks + +- Response must parse as JSON with `approved` (bool), `score` (int 0-100), and a + non-empty `reason` string; otherwise → `needs_review`. +- `score` is clamped to 0-100. +- `approved` is only honored when `score >= threshold`; a high-score reject or a + low-score approve is downgraded to `needs_review`. + +## Output contract + +Return exactly: + +```json +{ + "approved": true, + "score": 82, + "reason": "Hands pick up the cup, pour water, and set it back; stable framing, clear lighting.", + "verdict": "approved" +} +``` + +- `verdict` is one of `approved` | `rejected` | `needs_review`. +- On any failure to judge confidently, return `verdict: "needs_review"` with + `approved: false` and a reason explaining what was missing. +- Never include API keys, wallet material, or raw contributor identity in the + output. diff --git a/showcase/hydro-embodied-verify/soul.md b/showcase/hydro-embodied-verify/soul.md new file mode 100644 index 0000000..faaeead --- /dev/null +++ b/showcase/hydro-embodied-verify/soul.md @@ -0,0 +1,65 @@ +# Hydro Embodied-Data Verifier — Soul + +Hydro is the quality gate for crowdsourced embodied data. It does not trust an +upload because it exists — it checks that the video genuinely performs the task +that was asked, and only then credits the contributor in $HYDRO. The same gate +is designed to serve as an evaluator for embodied-video deliverables on the +Agent Commerce Protocol (Base). + +## Origin + +Embodied-AI training needs real first-person human demonstrations (pour water, +fold a shirt, plug in a cable). Crowdsourcing that data at scale invites spam: +blank clips, wrong tasks, unusable footage. Hydro closes the gap with an AI +quality gate so buyers get clean, labeled, task-specific video and contributors +are paid only for useful work. + +## Operational Identity + +The verifier receives a first-person clip plus the task instructions it claims +to fulfill. It extracts frames, runs a vision model against the task, and emits +a single bounded verdict. It is a judge, not a data provider: it does not record +clips or hold funds. In an ACP context it fits the first-class Evaluator role — +named on a job to decide whether an embodied-video deliverable is accepted. + +## Scoring Rubric + +Each clip is scored across four dimensions, combined into a 0-100 score: + +- **Visual & technical quality** — clarity, stability, lighting, framing. +- **Task completeness & relevance** — the full hand-object interaction the task + asked for is actually performed, start to finish. +- **Diversity & scene realism** — a real environment and natural execution, not + a staged or degenerate clip. +- **Value for robot learning** — richness and training utility of the footage. + +## Verdict Semantics + +- `approved` — clip clearly performs the task and clears the quality threshold → + contribution is credited (in ACP terms: accept the deliverable / release + escrow). +- `rejected` — clip does not perform the task, or fails quality → no credit (in + ACP terms: reject the deliverable / refund the buyer). +- `needs_review` — the model is not confident enough to decide (ambiguous or + corrupted input) → held for human review rather than a fabricated pass. + +## Guardrails + +- **Judge the task, not the intent.** A verdict must be grounded in what the + frames actually show, with a short human-readable reason. +- **Never a fabricated pass.** When the clip cannot be confidently judged, return + `needs_review` and withhold credit. +- **No secrets in the verdict.** Outputs carry only the verdict, score, and + reason — never keys, wallet material, or raw contributor identity. + +## Review Preference + +Inspectable proof over claims: the task prompt, the frames considered, and the +`{approved, score, reason}` the model returned. The redacted verdict report in +this package shows both an accepted and a rejected clip from the live pipeline. + +## Status + +MVP1 accounts rewards off-chain (an in-app $HYDRO balance, claimable at token +launch). On-chain ACP Evaluator settlement with escrow is planned; this Showcase +card is a hidden preview until that path is live. diff --git a/showcase/joysender-by-celebration-hub/README.md b/showcase/joysender-by-celebration-hub/README.md new file mode 100644 index 0000000..4b6a2a5 --- /dev/null +++ b/showcase/joysender-by-celebration-hub/README.md @@ -0,0 +1,34 @@ +# JoySender by Celebration Hub + +JoySender demonstrates an ACP provider workflow for social celebrations. A buyer chooses +a recipient, occasion, safe visual style, and delivery mode; an isolated Hermes worker +creates one Celebration Hub gift; the provider submits its public gift URL as the +deliverable. + +## Boundaries + +- Explicit user approval is mandatory. +- One job creates at most one gift. +- Supported styles are AI, Draw, and NexArt. +- Supported delivery modes are classic offchain gift URLs and collectible gifts minted as NFTs on Base. +- X and Farcaster handles are independently resolved; optional numeric IDs are verified, + never trusted blindly. +- Public-feed posting, token transfers, and arbitrary wallet actions are rejected. +- The public package has no access to production social or wallet credentials. + +## Proof status + +The package includes deterministic mock evidence plus a redacted, completed +owner-controlled production pilot. English showcase media is available in [`media/`](media/). +The manifest is ready for public Showcase review; publication still depends on upstream +approval and merge. + +## Showcase media + +- `media/cover.png` - 1600x900 cover. +- `media/acp.png` - structured ACP request entering the approval flow. +- `media/brief.png` - recipient, platform, style, and delivery brief. +- `media/approval.png` - approval-first delivery review. +- `media/classic.png` - completed classic gift delivery. +- `media/nft.png` - completed NFT on Base gift delivery. +- `media/joysender-demo-en.mp4` - 21-second English demo, 1920x1080. diff --git a/showcase/joysender-by-celebration-hub/media/README.md b/showcase/joysender-by-celebration-hub/media/README.md new file mode 100644 index 0000000..d549440 --- /dev/null +++ b/showcase/joysender-by-celebration-hub/media/README.md @@ -0,0 +1,21 @@ +# JoySender showcase media + +English, web-oriented showcase assets for JoySender by Celebration Hub. + +## Final assets + +| File | Purpose | Size | +| --- | --- | --- | +| `cover.png` | Primary showcase cover | 1600x900 | +| `acp.png` | Structured ACP request and approval entry | 1600x900 | +| `brief.png` | Recipient and delivery brief | 1600x900 | +| `approval.png` | Approval-first workflow | 1600x900 | +| `classic.png` | Completed classic gift | 1600x900 | +| `nft.png` | Completed NFT on Base gift | 1600x900 | +| `joysender-demo-en.mp4` | Short English demo | 1920x1080, about 21s | + +The screenshots use completed public Celebration Hub gift deliveries to the +owner-controlled `duckfacts.eth` / FID `217261` profile. No Telegram UI, technical logs, +credentials, or private ACP payloads are included. + +`showcase-scenes.html` is the deterministic source for the cover and framed screenshots. diff --git a/showcase/joysender-by-celebration-hub/media/acp.png b/showcase/joysender-by-celebration-hub/media/acp.png new file mode 100644 index 0000000..dc62ccb Binary files /dev/null and b/showcase/joysender-by-celebration-hub/media/acp.png differ diff --git a/showcase/joysender-by-celebration-hub/media/approval.png b/showcase/joysender-by-celebration-hub/media/approval.png new file mode 100644 index 0000000..59dd3ac Binary files /dev/null and b/showcase/joysender-by-celebration-hub/media/approval.png differ diff --git a/showcase/joysender-by-celebration-hub/media/brief.png b/showcase/joysender-by-celebration-hub/media/brief.png new file mode 100644 index 0000000..412190b Binary files /dev/null and b/showcase/joysender-by-celebration-hub/media/brief.png differ diff --git a/showcase/joysender-by-celebration-hub/media/celebration-hub-icon.png b/showcase/joysender-by-celebration-hub/media/celebration-hub-icon.png new file mode 100644 index 0000000..7cf8f04 Binary files /dev/null and b/showcase/joysender-by-celebration-hub/media/celebration-hub-icon.png differ diff --git a/showcase/joysender-by-celebration-hub/media/classic.png b/showcase/joysender-by-celebration-hub/media/classic.png new file mode 100644 index 0000000..9b1f61b Binary files /dev/null and b/showcase/joysender-by-celebration-hub/media/classic.png differ diff --git a/showcase/joysender-by-celebration-hub/media/cover.png b/showcase/joysender-by-celebration-hub/media/cover.png new file mode 100644 index 0000000..0e7af52 Binary files /dev/null and b/showcase/joysender-by-celebration-hub/media/cover.png differ diff --git a/showcase/joysender-by-celebration-hub/media/duckfacts-pfp.png b/showcase/joysender-by-celebration-hub/media/duckfacts-pfp.png new file mode 100644 index 0000000..debba2e Binary files /dev/null and b/showcase/joysender-by-celebration-hub/media/duckfacts-pfp.png differ diff --git a/showcase/joysender-by-celebration-hub/media/joysender-demo-en.mp4 b/showcase/joysender-by-celebration-hub/media/joysender-demo-en.mp4 new file mode 100644 index 0000000..b3512fd Binary files /dev/null and b/showcase/joysender-by-celebration-hub/media/joysender-demo-en.mp4 differ diff --git a/showcase/joysender-by-celebration-hub/media/joysender-mascot.png b/showcase/joysender-by-celebration-hub/media/joysender-mascot.png new file mode 100644 index 0000000..52b3894 Binary files /dev/null and b/showcase/joysender-by-celebration-hub/media/joysender-mascot.png differ diff --git a/showcase/joysender-by-celebration-hub/media/nft.png b/showcase/joysender-by-celebration-hub/media/nft.png new file mode 100644 index 0000000..cb7b668 Binary files /dev/null and b/showcase/joysender-by-celebration-hub/media/nft.png differ diff --git a/showcase/joysender-by-celebration-hub/media/raw-delivered-classic.png b/showcase/joysender-by-celebration-hub/media/raw-delivered-classic.png new file mode 100644 index 0000000..4160e77 Binary files /dev/null and b/showcase/joysender-by-celebration-hub/media/raw-delivered-classic.png differ diff --git a/showcase/joysender-by-celebration-hub/media/raw-delivered-nft.png b/showcase/joysender-by-celebration-hub/media/raw-delivered-nft.png new file mode 100644 index 0000000..1c22699 Binary files /dev/null and b/showcase/joysender-by-celebration-hub/media/raw-delivered-nft.png differ diff --git a/showcase/joysender-by-celebration-hub/media/showcase-scenes.html b/showcase/joysender-by-celebration-hub/media/showcase-scenes.html new file mode 100644 index 0000000..4429d1c --- /dev/null +++ b/showcase/joysender-by-celebration-hub/media/showcase-scenes.html @@ -0,0 +1,213 @@ + + + + + + JoySender showcase + + + +
+
Celebration Hub
JoySender by Celebration Hub
+
+
+
Personal celebration delivery
+

Turn a clear request into a gift worth opening.

+

Resolve a Farcaster username or X handle. When a platform ID is provided, JoySender verifies that it matches before delivery.

+
AIDrawNexArtClassicNFT on Base
+
+
JoySender
Approval-first delivery
+
+ +
+ + + + + + + + + + + + + + diff --git a/showcase/joysender-by-celebration-hub/proof/mock-run.md b/showcase/joysender-by-celebration-hub/proof/mock-run.md new file mode 100644 index 0000000..10f0251 --- /dev/null +++ b/showcase/joysender-by-celebration-hub/proof/mock-run.md @@ -0,0 +1,39 @@ +# Mock workflow proof + +This proof is generated without Virtuals credentials, wallet access, social credentials, +or a live Celebration Hub write. It demonstrates the exact event-to-deliverable contract +used before a hidden owner-controlled production activation. + +## Command + +```text +npm run mock +``` + +## Expected redacted output + +```json +{ + "submitted": true, + "giftUrl": "https://celebration-hub.xyz/share-greeting/mock-demo", + "submission": { + "ok": true, + "jobId": "showcase-demo-1", + "chainId": "8453", + "deliverable": "https://celebration-hub.xyz/share-greeting/mock-demo" + } +} +``` + +## What this proves + +- Provider events are normalized from ACP NDJSON. +- Requirement JSON must pass the bounded classic/Base NFT gift contract. +- The Hermes job id is deterministic and polled to a terminal state. +- One deliverable URL is submitted and duplicate events are suppressed. + +## What it does not prove + +It does not claim a completed ACP escrow or live gift delivery. The separate redacted +production-pilot proof records that lifecycle; this mock remains the reproducible, +credential-free bridge demonstration. diff --git a/showcase/joysender-by-celebration-hub/proof/production-pilot.md b/showcase/joysender-by-celebration-hub/proof/production-pilot.md new file mode 100644 index 0000000..0b85cb6 --- /dev/null +++ b/showcase/joysender-by-celebration-hub/proof/production-pilot.md @@ -0,0 +1,84 @@ +# Completed ACP pilot proof + +This redacted proof records owner-controlled hidden production jobs. No session token, +private key, signer material, HMAC secret, environment file, or private instruction is +included. + +## Current acceptance results + +### Draw / classic + +- ACP job: `70234` on Base (`8453`) +- Price funded: `0.01 USDC` +- Recipient: independently resolved and allowlisted Farcaster FID `217261` +- Style and delivery: `draw` / `classic` +- Deliverable: +- Terminal ACP status: `completed` + +### AI / Base NFT + +- ACP job: `70235` on Base (`8453`) +- Price funded: `0.01 USDC` +- Recipient: independently resolved and allowlisted Farcaster FID `217261` +- Style and delivery: `ai` / `base_nft` +- Deliverable: +- Terminal ACP status: `completed` + +Both deliverable pages returned HTTP 200 with an image preview after completion. Both +jobs followed the full lifecycle below without public-feed publication. + +## Earlier classic pilot + +- ACP job: `68228` on Base (`8453`) +- Offering: `Send a Celebration Gift` +- Price funded: `0.01 USDC` +- Provider: JoySender (`0x085e...ff15`) +- Recipient: allowlisted Farcaster FID `217261` +- Delivery: private classic visual gift, not published to the public feed +- Deliverable: +- Terminal ACP status: `completed` + +## Verified lifecycle + +```text +job.created +requirement +budget.set (0.01 USDC) +job.funded (0.01 USDC) +job.submitted (Celebration Hub gift URL) +job.completed +``` + +The buyer funded the job with Base USDC. After evaluator approval, the provider received +the ACP payout. The Celebration Hub operational payment was independently bounded to one +ACP operation and one daily pilot allowance. + +## Controls exercised + +- The request required `userApproved: true`. +- The pilot supplied a Farcaster FID and the provider verified the recipient against the + private allowlist. The current contract additionally resolves the handle independently + and rejects a supplied FID or X user id when it does not match. +- The recipient had to match the private pilot allowlist. +- The earlier pilot used the curated classic visual mode. The current acceptance jobs + additionally prove generated Draw/classic and AI/Base NFT delivery. +- The same bounded contract accepts `nexart`; its normalization, preflight, and visual checks + are covered by automated and signed preflight tests rather than an additional paid job. +- Public-feed publication, token instructions, and arbitrary URLs were rejected. +- The bridge used a localhost-only HMAC gateway and deterministic idempotency keys. +- Live execution and spend flags were returned to `0` after the completed job. + +## Recovery evidence + +An earlier owner-controlled attempt failed before any gift payment because recipient +resolution was incomplete. Its ACP escrow was rejected and fully returned. The contract +now requires a platform and handle, resolves that handle through the provider API, verifies +an optional numeric ID, and then applies the private recipient allowlist. No retry was +performed after a submitted payment side effect. + +A later acceptance attempt (`70200`) expired while an operation-key defect was being +diagnosed. The defect duplicated the `gift:` prefix and stopped before the operational +gift transaction. The runtime now normalizes an already-scoped ACP operation key and has +a regression test for that boundary. A recovery created a private gift only after the +ACP job had already expired, so it was not claimed as an ACP deliverable and no second ACP +payment was requested. Jobs `70234` and `70235` are the post-fix acceptance evidence. diff --git a/showcase/joysender-by-celebration-hub/showcase.json b/showcase/joysender-by-celebration-hub/showcase.json new file mode 100644 index 0000000..54d3c95 --- /dev/null +++ b/showcase/joysender-by-celebration-hub/showcase.json @@ -0,0 +1,64 @@ +{ + "slug": "joysender-by-celebration-hub", + "title": "JoySender by Celebration Hub", + "tagline": "Delivers approved classic or NFT on Base celebration gifts to Farcaster and X recipients through an idempotent ACP workflow", + "description": "JoySender turns a structured ACP job into one off-feed Celebration Hub AI, Draw, or NexArt gift and returns a public share URL as the deliverable. The public bridge is isolated from production wallet and social credentials, while the private worker independently resolves Farcaster or X handles, verifies optional platform IDs, requires explicit approval, applies scoped spend limits, and uses no-retry handling for ambiguous side effects. The package includes a completed owner-controlled ACP pilot and English showcase media.", + "status": "validated pilot", + "topic": "agents", + "topics": ["celebrations", "social gifting", "farcaster", "x", "idempotency"], + "builder": { + "name": "Celebration Hub", + "url": "https://celebration-hub.xyz/about" + }, + "links": { + "repo": "https://github.com/Altagers/joysender-acp-showcase", + "share": "https://celebration-hub.xyz/about", + "feedback": "https://github.com/Altagers/joysender-acp-showcase/issues", + "demo": "https://celebration-hub.xyz/feed", + "video": "https://raw.githubusercontent.com/Altagers/joysender-acp-showcase/main/showcase/joysender-by-celebration-hub/media/joysender-demo-en.mp4" + }, + "primitives": ["acp"], + "visual": { + "kind": "agent workflow", + "eyebrow": "ACP social gifting", + "title": "One approved job, one celebration gift", + "posterUrl": "https://raw.githubusercontent.com/Altagers/joysender-acp-showcase/main/showcase/joysender-by-celebration-hub/media/cover.png", + "videoUrl": "https://raw.githubusercontent.com/Altagers/joysender-acp-showcase/main/showcase/joysender-by-celebration-hub/media/joysender-demo-en.mp4", + "videoLabel": "Watch the 21-second JoySender demo" + }, + "skills": [ + { + "name": "joysender-celebration-gift", + "href": "https://github.com/Altagers/joysender-acp-showcase/tree/main/showcase/joysender-by-celebration-hub/skills/joysender-celebration-gift", + "summary": "Prepare and validate a bounded ACP request for one Farcaster or X celebration gift.", + "install": "Copy the skill folder into your agent skills directory and invoke it before creating the ACP job.", + "sourcePath": "showcase/joysender-by-celebration-hub/skills/joysender-celebration-gift" + } + ], + "artifacts": [ + { + "label": "Completed ACP pilot proof", + "href": "https://github.com/Altagers/joysender-acp-showcase/blob/main/showcase/joysender-by-celebration-hub/proof/production-pilot.md", + "kind": "proof" + }, + { + "label": "Security architecture", + "href": "https://github.com/Altagers/joysender-acp-showcase/blob/main/docs/ARCHITECTURE.md", + "kind": "documentation" + }, + { + "label": "JoySender showcase video", + "href": "https://raw.githubusercontent.com/Altagers/joysender-acp-showcase/main/showcase/joysender-by-celebration-hub/media/joysender-demo-en.mp4", + "kind": "video" + } + ], + "soul": { + "href": "https://github.com/Altagers/joysender-acp-showcase/blob/main/showcase/joysender-by-celebration-hub/soul.md", + "summary": "Public operating principles for a celebration agent that asks before acting and never improvises financial operations." + }, + "feedbackPrompts": [ + "Was the approval and recipient scope clear before the gift was created?", + "Did the delivered gift URL make the result easy to inspect and share?", + "Which celebration workflow should come next: birthday context or event preparation?" + ] +} diff --git a/showcase/joysender-by-celebration-hub/skills/joysender-celebration-gift/SKILL.md b/showcase/joysender-by-celebration-hub/skills/joysender-celebration-gift/SKILL.md new file mode 100644 index 0000000..b9deabe --- /dev/null +++ b/showcase/joysender-by-celebration-hub/skills/joysender-celebration-gift/SKILL.md @@ -0,0 +1,112 @@ +--- +name: joysender-celebration-gift +description: Prepare a bounded ACP request for one approved Celebration Hub gift to a Farcaster or X recipient. +--- + +# JoySender Celebration Gift + +Use this skill when a user explicitly asks to create one social celebration gift through +the JoySender ACP offering. + +## Tools, credentials, and preconditions + +- Use the official Virtuals ACP CLI for the ACP job lifecycle. +- The provider bridge must already be configured with its ACP agent id and a signed, + localhost-only Hermes gateway. The skill never asks for those credentials. +- The user must identify one Farcaster or X recipient and approve one final normalized + requirement. Do not proceed from an inferred recipient or a draft message. + +## Required inputs + +- Platform: `farcaster` or `x`. +- Recipient platform and handle. A Farcaster FID or numeric X user id is optional evidence, + not something to guess. +- Occasion: birthday, celebration, milestone, or custom. +- Style: `ai`, `draw`, or `nexart`. +- Delivery: `classic` for an offchain gift, or `base_nft` for a Base collectible gift. +- Short message and a visual prompt. The prompt is required for every style so the + provider can validate and reproduce the approved visual intent. +- Explicit confirmation that the user wants the gift created now. + +## Approval gate + +Show the normalized recipient, message, style, and delivery to the user before creating +the ACP job. Set `userApproved: true` only after the user confirms that exact request. +Changing the recipient, visual mode, message, or delivery requires a new confirmation. + +## Prepare the requirement + +Return a JSON object matching this shape: + +```json +{ + "platform": "farcaster", + "recipient": { "handle": "example.eth" }, + "occasion": "birthday", + "style": "draw", + "message": "Happy birthday!", + "prompt": "A joyful birthday cake with violet confetti", + "delivery": "classic", + "publicFeed": false, + "userApproved": true +} +``` + +## Stop conditions + +Stop and ask the user when the recipient is ambiguous, the user has not approved the +final action, or the request asks for token transfers, public posting, multiple +recipients, unsafe content, arbitrary links, or direct wallet/key operations. + +Do not substitute a guessed FID or X user id. The provider resolves the handle through +the platform API, maps it to a canonical Celebration Hub recipient, and rejects a supplied +id when it does not match. Do not convert between `classic` and `base_nft` after approval. +Do not retry a job after an ambiguous payment, mint, or delivery result. + +Choose exactly one visual mode: + +- `ai`: a polished generated image. Always provide a concrete visual prompt with subject, + mood, colors, and occasion. +- `draw`: a personal sketch or hand-drawn note. Use simple drawable objects and composition. +- `nexart`: more stylized generative artwork. Use when the user explicitly asks for NexArt + or a highly stylized visual. + +Do not silently change the requested mode. If the request says only "a gift" and gives no +visual preference, propose `ai`, show the normalized requirement, and obtain confirmation +before setting `userApproved: true`. If the user does not choose delivery, use `classic`. +Use `base_nft` only when the user explicitly asks for an NFT, collectible, or onchain Base +gift. Before a gift job is created, Hermes runs a signed, read-only preflight that +independently resolves the recipient and confirms the selected style and delivery. The +worker verifies the selected visual before funding or minting the gift. + +## Recipient examples + +Farcaster by handle: + +```json +{ "platform": "farcaster", "recipient": { "handle": "duckfacts.eth" } } +``` + +X by handle: + +```json +{ "platform": "x", "recipient": { "handle": "celesteanglm" } } +``` + +Include `platformUserId` only when it comes from authoritative platform context. The +provider still resolves the handle independently and rejects a mismatch. + +## Verify the deliverable + +The provider deliverable must be a Celebration Hub HTTPS gift URL. Report the normalized +platform, handle, canonical Celebration Hub FID, recipient resolution status, selected +style, delivery, and delivered status. +Confirm that the URL resolves and its visual is available. +Never print ACP credentials, HMAC secrets, environment +variables, social tokens, wallet material, cookies, or internal service output. + +## Output and handoff + +Return the final Celebration Hub URL and the normalized recipient, style, and delivery. +If the provider reports `ambiguous` or `failed_final`, stop and hand the job to an operator; +do not create a replacement job or ask the user to pay again. diff --git a/showcase/joysender-by-celebration-hub/soul.md b/showcase/joysender-by-celebration-hub/soul.md new file mode 100644 index 0000000..4afcd47 --- /dev/null +++ b/showcase/joysender-by-celebration-hub/soul.md @@ -0,0 +1,14 @@ +# JoySender public operating principles + +JoySender helps people remember and celebrate meaningful moments without pretending that +every action should be autonomous. + +It asks for explicit approval before creating a gift, states what will be delivered, and +stops when the recipient cannot be resolved safely. It does not infer permission to mint, +transfer tokens, publish private content, or contact additional people. + +JoySender treats duplicate events as the same job. When a payment or delivery result is +ambiguous, it pauses for review instead of trying again and risking a duplicate gift. + +This file is public context only. It intentionally excludes private prompts, credentials, +wallet information, account data, operational endpoints, and internal moderation rules. diff --git a/showcase/kairune-verifiable-trust/README.md b/showcase/kairune-verifiable-trust/README.md new file mode 100644 index 0000000..5248328 --- /dev/null +++ b/showcase/kairune-verifiable-trust/README.md @@ -0,0 +1,24 @@ +# Kairune — Verifiable Agent Trust Layer + +The trust layer for AI agents that spend. Kairune computes a deterministic trust +score (0–1000) from an agent's behavior history and grants or revokes spending +permission by tier. + +**Showcased upgrade — verifiable attestations:** issuers register Ed25519 keys +and sign each attestation; the server verifies every signature; the scoring +engine weights verified attestations fully and discounts unsigned ones (0.25×). +Backward compatible — existing unsigned submissions still work, recorded as +`unverified`. + +## Proof +- Animated demo: `assets/kairune-verify-demo.mp4` +- Live console: https://kairune.online/app +- API meta (shows `signature_algorithm: ed25519`): https://kairune.online/api/meta +- Example trust card: https://kairune.online/a/voyager-07 +- Source: https://github.com/kairunedev/Kairune + +## EconomyOS primitives +ACP job (4 paid offerings on Robinhood Chain via Virtuals), Agent Token +($KAIRUNE), and a smart-contract Agent Wallet for the seller bot. + +Builder: Kairune · https://x.com/usekairune · Virtuals: https://app.virtuals.io/virtuals/100623 diff --git a/showcase/kairune-verifiable-trust/assets/kairune-verify-demo.mp4 b/showcase/kairune-verifiable-trust/assets/kairune-verify-demo.mp4 new file mode 100644 index 0000000..505f19f Binary files /dev/null and b/showcase/kairune-verifiable-trust/assets/kairune-verify-demo.mp4 differ diff --git a/showcase/kairune-verifiable-trust/assets/poster.png b/showcase/kairune-verifiable-trust/assets/poster.png new file mode 100644 index 0000000..69d705d Binary files /dev/null and b/showcase/kairune-verifiable-trust/assets/poster.png differ diff --git a/showcase/kairune-verifiable-trust/showcase.json b/showcase/kairune-verifiable-trust/showcase.json new file mode 100644 index 0000000..fe8010a --- /dev/null +++ b/showcase/kairune-verifiable-trust/showcase.json @@ -0,0 +1,86 @@ +{ + "slug": "kairune-verifiable-trust", + "title": "Kairune — Verifiable Agent Trust Layer", + "tagline": "Every agent gets a verifiable, cryptographically-signed trust score — then scoped spend by tier.", + "description": "Kairune is the trust layer for AI agents that spend. It computes a deterministic trust score (0-1000) from an agent's behavior history and grants or revokes spending permission by tier. The showcased upgrade makes the score's inputs verifiable: issuers register Ed25519 keys and sign each attestation, the server verifies every signature, and the scoring engine weights verified attestations fully while discounting unsigned ones (0.25x). Exposed as ACP offerings (lookup score, register agent, record attestation, full report) on Robinhood Chain via Virtuals.", + "status": "live", + "topic": "commerce", + "topics": ["identity", "commerce", "acp"], + "hidden": false, + "builder": { + "name": "Kairune", + "url": "https://x.com/usekairune" + }, + "links": { + "repo": "https://github.com/kairunedev/Kairune", + "demo": "https://kairune.online/app", + "video": "https://x.com/usekairune/status/2075911860516528241", + "share": "https://x.com/usekairune/status/2075911860516528241", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Kairune%20Verifiable%20Agent%20Trust%20Layer&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20Issuer%20identity%20should%20be%20verified%20on-chain%0A-%20Attestation%20kinds%20for%20agent-to-agent%20commerce%0A-%20Replay-protection%20window%20worth%20the%20friction%3F%0A%0ANotes%3A%0A" + }, + "primitives": ["acp", "token", "wallet"], + "visual": { + "kind": "animated demo", + "eyebrow": "live api + acp", + "title": "verifiable attestations", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/kairune-verifiable-trust/assets/poster.png", + "videoUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/kairune-verifiable-trust/assets/kairune-verify-demo.mp4", + "videoLabel": "Watch the 0:06 demo on X" + }, + "skills": [ + { + "name": "kairune-verify-agent-trust", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/kairune-verifiable-trust/skills/kairune-verify-agent-trust", + "sourcePath": "showcase/kairune-verifiable-trust/skills/kairune-verify-agent-trust", + "summary": "Check a counterparty agent's Kairune trust score, tier, verified/unverified attestation counts, and suggested daily ceiling before granting spend, accepting a paid job, or wiring a payment. Live API, ACP, and evidence-review modes.", + "install": "cp -R showcase/kairune-verifiable-trust/skills/kairune-verify-agent-trust ~/.agents/skills/" + } + ], + "artifacts": [ + { + "label": "X demo video", + "href": "https://x.com/usekairune/status/2075911860516528241", + "kind": "video" + }, + { + "label": "Skill source — verify agent trust before granting spend", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/kairune-verifiable-trust/skills/kairune-verify-agent-trust", + "kind": "skill" + }, + { + "label": "Animated demo (mp4)", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/kairune-verifiable-trust/assets/kairune-verify-demo.mp4", + "kind": "video" + }, + { + "label": "Live console", + "href": "https://kairune.online/app", + "kind": "proof" + }, + { + "label": "API meta (signature_algorithm: ed25519, unverified_weight_factor: 0.25)", + "href": "https://kairune.online/api/meta", + "kind": "proof" + }, + { + "label": "Example public trust card", + "href": "https://kairune.online/a/voyager-07", + "kind": "proof" + }, + { + "label": "Public repo", + "href": "https://github.com/kairunedev/Kairune", + "kind": "proof" + }, + { + "label": "Verifiable attestations PR", + "href": "https://github.com/kairunedev/Kairune/pull/1", + "kind": "proof" + } + ], + "feedbackPrompts": [ + "Should issuer identities be verified on-chain rather than by API key?", + "What attestation kinds matter most for agent-to-agent commerce?", + "Would a replay-protection window on signed submissions be worth the added friction?" + ] +} diff --git a/showcase/kairune-verifiable-trust/skills/kairune-verify-agent-trust/SKILL.md b/showcase/kairune-verifiable-trust/skills/kairune-verify-agent-trust/SKILL.md new file mode 100644 index 0000000..7b89151 --- /dev/null +++ b/showcase/kairune-verifiable-trust/skills/kairune-verify-agent-trust/SKILL.md @@ -0,0 +1,170 @@ +--- +name: kairune-verify-agent-trust +description: Check a Kairune trust score, tier, verified-attestation counts, and suggested daily spend ceiling for a counterparty agent before granting spend, accepting a job, or wiring a payment. Use live API mode when HTTP access is available, ACP mode to buy the check as a mediated job, or evidence-review mode when given a share card URL. +--- + +# Kairune — Verify Agent Trust Before Granting Spend + +## Overview + +Use this skill to decide whether a counterparty agent is trustworthy enough to +grant spend, accept a paid job from, or wire a payment to. Kairune returns a +deterministic trust score (0–1000), a tier, the count of verified vs unverified +attestations behind the score, and a suggested daily spend ceiling for that tier. + +Three modes: + +- **Live API mode**: query the free public Kairune REST API over HTTPS. +- **ACP mode**: buy the `Lookup Trust Score` or `Full Trust Report` offering as a + mediated ACP job on Robinhood Chain via Virtuals. +- **Evidence review mode**: given a public trust-card URL (`/a/:handle`) or a + prior report, judge whether the counterparty meets the caller's threshold. + +This is a decision-gating skill. The caller's stated threshold and spend cap are +the source of truth; stop and defer to the caller when the data does not clearly +clear the bar. + +## When to use / when not to use + +- **Use it** before granting a spending permission to another agent, before + accepting a paid ACP job from an unknown provider, or before increasing an + existing counterparty's ceiling. +- **Do not use it** as the sole control for high-value or irreversible transfers, + or to make legal/financial guarantees. A trust score is a signal, not a + promise. It does not replace escrow, spend caps, or human approval for large + amounts. + +## Required inputs, tools, credentials, preconditions + +- **Input**: the counterparty's Kairune handle or agent id; the caller's minimum + acceptable tier (or score) and the spend amount being considered. +- **Tools**: an HTTPS client (`curl`/fetch) for live API mode; an ACP client for + ACP mode. +- **Credentials**: none for reads — the Kairune API is free and public. ACP mode + needs a funded ACP wallet to pay the offering fee. +- **Precondition**: network access to `https://kairune.online` (live/evidence + modes) or a working ACP client (ACP mode). + +## Required Rules + +- Treat the trust score as advisory input to the caller's own policy, never as an + authorization by itself. +- Always compare the returned `suggested_daily_ceiling` against the amount the + caller actually intends to grant, and cap at the lower of the two. +- Prefer agents whose score is backed by **verified** attestations; treat a score + built mostly on `unverified` attestations as weaker (Kairune already discounts + unverified data at 0.25×). +- Re-check just before granting; scores change as behavior is attested. +- Never fabricate or infer a score that was not returned by Kairune. + +## Stop Conditions + +Stop and defer to the caller (return `review` or `deny`) when: + +- The agent is not found, or its `status` is `suspended`. +- The returned tier is below the caller's minimum acceptable tier. +- The intended amount exceeds the tier's `suggested_daily_ceiling`. +- The score is driven overwhelmingly by `unverified` attestations and the caller + required verified backing. +- Kairune is unreachable or returns an error — never assume a passing score on + failure. + +## Live API Command Pattern + +Reads are free and unauthenticated: + +```bash +# Score, tier, breakdown (verified/unverified counts), suggested ceiling +curl -s https://kairune.online/api/agents/ + +# Scoring metadata (tier thresholds, unverified_weight_factor, algorithm) +curl -s https://kairune.online/api/meta + +# Public share card (human-readable) +# https://kairune.online/a/ +``` + +Relevant fields on the agent detail: `score`, `tier`, `label`, +`suggested_daily_ceiling`, `status`, and `breakdown.verifiedCount` / +`breakdown.unverifiedCount`. + +## ACP Command Pattern + +When you prefer a mediated, paid job instead of a direct read, buy an offering +from the Kairune provider agent on Virtuals (Robinhood Chain): + +- `Lookup Trust Score` — `{"handle_or_id":"voyager-07"}` → score, tier, label, + suggested_daily_ceiling, share_url. +- `Full Trust Report` — `{"handle_or_id":"voyager-07"}` → agent, attestations[], + permissions[], share_url. + +Provider agent: https://app.virtuals.io/virtuals/100623 + +## Workflow + +1. Read the caller's minimum tier/score, the intended spend amount, and whether + verified backing is required. +2. Resolve the counterparty by handle or id (live API mode) or open the matching + ACP offering (ACP mode). +3. Fetch the agent detail; confirm `status` is `active`. +4. Read `score`, `tier`, `label`, `suggested_daily_ceiling`, and the + verified/unverified attestation counts. +5. Apply the caller's policy: tier ≥ minimum, amount ≤ `suggested_daily_ceiling`, + and verified-backing requirement if set. +6. Return a decision: `allow`, `review`, or `deny`, with the capped amount and a + one-line reason. +7. If `allow`, grant only up to the capped amount and re-check before any future + increase. + +## Evidence Review Workflow + +1. Open the provided `/a/:handle` share card or supplied report. +2. Confirm handle, score, tier, and verified/unverified counts match the claim. +3. Compare against the caller's threshold and intended amount. +4. Return `pass`, `fail`, or `uncertain` with the exact missing evidence. + +## Evidence and Redaction Rules + +- The trust data (score, tier, counts) is public and safe to include. +- Never include or request API keys, issuer private keys, raw signatures, or any + `.env` values. Kairune's API already omits signatures and issuer secrets from + responses; keep it that way in any quoted output. + +## Validation Checklist + +- [ ] Counterparty resolved and `status == active`. +- [ ] Tier ≥ caller's minimum acceptable tier. +- [ ] Intended amount ≤ `suggested_daily_ceiling` (else capped). +- [ ] Verified-backing requirement satisfied if the caller set one. +- [ ] Decision, capped amount, and reason returned. +- [ ] No secrets in output. + +## Output Contract + +Return a single JSON object: + +```json +{ + "handle": "voyager-07", + "score": 512, + "tier": 2, + "label": "ESTABLISHED", + "suggested_daily_ceiling": 150, + "verified_count": 18, + "unverified_count": 3, + "decision": "allow", + "granted_ceiling": 100, + "reason": "Tier 2 >= min tier 2; amount 100 <= ceiling 150; mostly verified." +} +``` + +`decision` is one of `allow`, `review`, `deny`. `granted_ceiling` is the lower of +the caller's intended amount and `suggested_daily_ceiling`. + +## References + +- Live console: https://kairune.online/app +- API metadata: https://kairune.online/api/meta +- Example trust card: https://kairune.online/a/voyager-07 +- Example prompt: `examples/prompt.md` +- Redacted result: `examples/result-redacted.md` diff --git a/showcase/kairune-verifiable-trust/skills/kairune-verify-agent-trust/examples/prompt.md b/showcase/kairune-verifiable-trust/skills/kairune-verify-agent-trust/examples/prompt.md new file mode 100644 index 0000000..fb92e54 --- /dev/null +++ b/showcase/kairune-verifiable-trust/skills/kairune-verify-agent-trust/examples/prompt.md @@ -0,0 +1,12 @@ +# Example prompt — verify agent trust before granting spend + +> I'm about to grant `voyager-07` a $100/day compute spending permission. +> Before I do, check its Kairune trust score. Only allow if it's at least +> tier 2 (ESTABLISHED) and the score is mostly backed by verified attestations. +> Cap the grant at Kairune's suggested daily ceiling for its tier. Return your +> decision as JSON. + +Expected behavior: the agent fetches `https://kairune.online/api/agents/voyager-07`, +confirms `status == active`, checks tier ≥ 2 and verified vs unverified counts, +caps the grant at `min(100, suggested_daily_ceiling)`, and returns an +`allow`/`review`/`deny` decision with a one-line reason. diff --git a/showcase/kairune-verifiable-trust/skills/kairune-verify-agent-trust/examples/result-redacted.md b/showcase/kairune-verifiable-trust/skills/kairune-verify-agent-trust/examples/result-redacted.md new file mode 100644 index 0000000..2321cab --- /dev/null +++ b/showcase/kairune-verifiable-trust/skills/kairune-verify-agent-trust/examples/result-redacted.md @@ -0,0 +1,47 @@ +# Redacted result — verify agent trust before granting spend + +**Request:** grant `voyager-07` a $100/day compute permission; require tier ≥ 2 +and mostly-verified backing; cap at Kairune's suggested ceiling. + +**Live API call (free, public):** + +``` +GET https://kairune.online/api/agents/voyager-07 +``` + +**Relevant fields returned (trimmed, public data):** + +```json +{ + "agent": { + "handle": "voyager-07", + "status": "active", + "score": 512, + "tier": 2, + "label": "ESTABLISHED", + "suggested_daily_ceiling": 150, + "breakdown": { "verifiedCount": 18, "unverifiedCount": 3 } + } +} +``` + +**Decision:** + +```json +{ + "handle": "voyager-07", + "score": 512, + "tier": 2, + "label": "ESTABLISHED", + "suggested_daily_ceiling": 150, + "verified_count": 18, + "unverified_count": 3, + "decision": "allow", + "granted_ceiling": 100, + "reason": "Tier 2 >= min tier 2; amount 100 <= ceiling 150; 18 verified vs 3 unverified." +} +``` + +**Notes:** No credentials were used (reads are public). No API keys, signatures, +or private key material appear in the request or response. Values above are +illustrative of the live shape; exact numbers change as new attestations land. diff --git a/showcase/kernal/README.md b/showcase/kernal/README.md new file mode 100644 index 0000000..952f9df --- /dev/null +++ b/showcase/kernal/README.md @@ -0,0 +1,50 @@ +# KERNAL + +The on-chain execution skill layer for AI agents on Base. + +KERNAL is an on-chain agent skill registry that runs as a live provider agent in Virtuals EconomyOS. It exposes a catalog of intelligence and signal skills any agent can hire through the Agent Commerce Protocol (ACP): each skill reads on-chain and market data, analyzes it, and returns a structured deliverable against an on-chain escrow. + +## What's in this package + +- `showcase.json` — the showcase manifest +- `skills/` — 11 reusable SKILL.md files, one per KERNAL offering +- `examples/` — a real hire prompt and a redacted result from the `alpha_digest` skill +- `soul.md` — public, redacted agent context for the KERNAL provider agent + +## The skill catalog + +Intelligence (read-only analysis): +- `wallet-digest` — wallet holdings, activity, PnL, risk flags +- `token-alert` — price/volume anomaly detection with a verdict +- `gas-tracker` — Base gas analysis with transact-now-or-wait guidance +- `defi-monitor` — LP/pool health and impermanent loss risk +- `arbitrage-scanner` — cross-DEX profit analysis net of fees + +Signals (analysis; the client executes and keeps custody): +- `sniper-signal` — new launch entry signal (honeypot, liquidity, sizing) +- `copy-trade-signal` — smart-money mirroring strategy +- `yield-signal` — compound timing across Aerodrome and Curve +- `rebalance-signal` — portfolio rebalance trade plan +- `mev-audit` — MEV exposure audit and protection routing + +Plus recurring daily alpha and wallet-watch subscriptions. + +## Virtuals usage + +- Live provider agent in EconomyOS with 11 offerings + 1 resource on ACP +- Inference routed through Virtuals Compute +- $KRN is the core token on Base, gating premium skills, staking, and execution fees + +## How to hire + +Any agent can hire a KERNAL skill through ACP: open a job against the offering, fund the escrow, receive the structured deliverable, approve to settle. All KERNAL offerings are currently analysis/signal only — no client fund custody. + +## Links + +- Site: https://www.gitkernal.app +- Provider agent: https://app.virtuals.io/acp/agents/019ee5a2-9b66-720d-acd6-f2b2f902a142 +- $KRN (Base): 0x974B53861d975E727305298D2718849c43046ba3 + +--- + +*KERNAL · gitkernal.app · $KRN · Base · Live on Virtuals ACP* diff --git a/showcase/kernal/examples/prompt.md b/showcase/kernal/examples/prompt.md new file mode 100644 index 0000000..6854149 --- /dev/null +++ b/showcase/kernal/examples/prompt.md @@ -0,0 +1,22 @@ +# Example: Hiring the alpha_digest skill via ACP + +This is a real hire prompt used to commission KERNAL's `alpha_digest` offering through a client agent on the Virtuals Agent Commerce Protocol. + +## Client agent instruction + +``` +Find the KERNAL agent on ACP and hire its alpha_digest job. +Track the tokens ETH, BTC, and KRN, including whale wallet activity. +Fund the job and return the ranked digest when it's done. +``` + +## What happens + +1. The client agent browses ACP and locates the KERNAL provider agent. +2. It opens a job against the `alpha_digest` offering (fixed fee: 2 USDC, SLA: 5 min). +3. It funds the job — only the service fee is escrowed; no principal funds are transferred (`requiredFunds: false`). +4. KERNAL runs the skill: it reads on-chain price and volume for the requested tokens, pulls whale wallet activity, synthesizes Crypto Twitter narrative, and ranks the opportunities. +5. KERNAL submits the structured deliverable. +6. The client reviews and approves; the escrow releases the fee to KERNAL. + +The full output shape is shown in `result-redacted.md`. diff --git a/showcase/kernal/examples/result-redacted.md b/showcase/kernal/examples/result-redacted.md new file mode 100644 index 0000000..6b43493 --- /dev/null +++ b/showcase/kernal/examples/result-redacted.md @@ -0,0 +1,52 @@ +# Example result: alpha_digest (redacted) + +A representative `alpha_digest` deliverable for the input tokens ETH, BTC, KRN. Wallet addresses and any account-identifying detail are redacted; structure and field shape are unchanged. + +```json +{ + "generated_at": "2026-06-2X T 0X:00:00Z", + "market_narrative": "Base ecosystem attention concentrated on agent-commerce tokens this cycle; broad risk tone neutral-to-positive with rotation into on-chain AI names.", + "token_signals": [ + { + "token": "ETH", + "price_read": "range-bound over the window", + "volume_read": "average", + "signal": "neutral" + }, + { + "token": "BTC", + "price_read": "mild uptrend", + "volume_read": "slightly elevated", + "signal": "neutral-bullish" + }, + { + "token": "KRN", + "price_read": "outperforming ecosystem average", + "volume_read": "elevated", + "signal": "bullish, watch for mean reversion" + } + ], + "whale_activity": [ + { + "wallet": "0x____REDACTED____", + "action": "accumulation", + "token": "KRN", + "note": "steady adds over the window; no distribution detected" + } + ], + "ranked_opportunities": [ + { + "rank": 1, + "thesis": "KRN momentum with confirmed on-chain accumulation", + "risk_flag": "elevated volatility; size accordingly" + }, + { + "rank": 2, + "thesis": "BTC steady bid, low-risk directional lean", + "risk_flag": "low" + } + ] +} +``` + +This deliverable is analysis only. Any execution based on it is performed by the client, who retains full control of capital. No signing authority or fund custody is ever requested by KERNAL. diff --git a/showcase/kernal/kernal-skill.jpg b/showcase/kernal/kernal-skill.jpg new file mode 100644 index 0000000..072e78d Binary files /dev/null and b/showcase/kernal/kernal-skill.jpg differ diff --git a/showcase/kernal/showcase.json b/showcase/kernal/showcase.json new file mode 100644 index 0000000..aa66cbb --- /dev/null +++ b/showcase/kernal/showcase.json @@ -0,0 +1,150 @@ +{ + "slug": "kernal", + "title": "KERNAL", + "tagline": "Hires out on-chain execution skills to any agent on Base through ACP, from wallet intelligence to sniper and yield signals, each delivered against an on-chain escrow", + "description": "KERNAL is an on-chain agent skill registry that operates as a live provider agent in EconomyOS. It exposes intelligence and signal skills any agent can hire through ACP: wallet intelligence, token and gas monitoring, DeFi position health, arbitrage scanning, sniper, copy-trade, yield and rebalance signals, MEV audits, and recurring daily alpha subscriptions. Each skill reads on-chain and market data, analyzes it, and returns a structured deliverable against an on-chain escrow. KERNAL runs inference through Virtuals Compute; $KRN is the core token on Base gating premium skills, staking, and execution fees.", + "status": "live", + "topic": "commerce", + "topics": [ + "agent-commerce", + "defi", + "trading", + "intelligence", + "base" + ], + "hidden": false, + "builder": { + "name": "Jamesdwitya", + "url": "https://x.com/Jxmes09A" + }, + "links": { + "repo": "https://github.com/gitkernal/kernal-site", + "share": "https://x.com/gitkernal/status/2068779790979719466", + "feedback": "https://x.com/gitkernal", + "demo": "https://www.gitkernal.app", + "video": "https://x.com/gitkernal/status/2068772322920784343" + }, + "primitives": [ + "acp", + "wallet", + "token" + ], + "visual": { + "kind": "card", + "eyebrow": "Agent Skill Registry", + "title": "On-chain execution skills, hireable by any agent", + "videoLabel": "Watch the demo on X", + "videoUrl": "https://video.twimg.com/amplify_video/2072064643498221568/vid/avc1/1334x818/bryV8Sq6yevhUt1W.mp4?tag=28", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/kernal/kernal-skill.jpg" + }, + "skills": [ + { + "name": "alpha-digest", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/kernal/skills/alpha-digest/SKILL.md", + "summary": "Ranked daily intelligence digest for Base combining Crypto Twitter narrative, whale wallet activity, and on-chain price signals. Analysis only, no fund custody.", + "install": "Hire the alpha_digest offering from the KERNAL agent on ACP, or run it at gitkernal.app", + "sourcePath": "showcase/kernal/skills/alpha-digest" + }, + { + "name": "wallet-digest", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/kernal/skills/wallet-digest/SKILL.md", + "summary": "Full on-chain wallet intelligence for any Base address: holdings, activity, PnL, and risk flags. Analysis only.", + "install": "Hire the wallet_digest offering from the KERNAL agent on ACP, or run it at gitkernal.app", + "sourcePath": "showcase/kernal/skills/wallet-digest" + }, + { + "name": "token-alert", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/kernal/skills/token-alert/SKILL.md", + "summary": "Price and volume anomaly detection for a single token against a threshold, returning a clear alert verdict. Analysis only.", + "install": "Hire the token_alert offering from the KERNAL agent on ACP, or run it at gitkernal.app", + "sourcePath": "showcase/kernal/skills/token-alert" + }, + { + "name": "gas-tracker", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/kernal/skills/gas-tracker/SKILL.md", + "summary": "Base gas analysis with a transact-now-or-wait recommendation. Advisory only.", + "install": "Hire the gas_tracker offering from the KERNAL agent on ACP, or run it at gitkernal.app", + "sourcePath": "showcase/kernal/skills/gas-tracker" + }, + { + "name": "defi-monitor", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/kernal/skills/defi-monitor/SKILL.md", + "summary": "LP and pool health analysis with impermanent loss risk modeled across price scenarios. Analysis only.", + "install": "Hire the defi_monitor offering from the KERNAL agent on ACP, or run it at gitkernal.app", + "sourcePath": "showcase/kernal/skills/defi-monitor" + }, + { + "name": "arbitrage-scanner", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/kernal/skills/arbitrage-scanner/SKILL.md", + "summary": "Cross-DEX arbitrage profitability analysis between Uniswap v3 and Aerodrome, net of gas and flash loan fees. Analysis only.", + "install": "Hire the arbitrage_scanner offering from the KERNAL agent on ACP, or run it at gitkernal.app", + "sourcePath": "showcase/kernal/skills/arbitrage-scanner" + }, + { + "name": "sniper-signal", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/kernal/skills/sniper-signal/SKILL.md", + "summary": "New pool launch entry signal: honeypot check, liquidity, sizing, slippage, and gas strategy. You execute, keeping custody.", + "install": "Hire the sniper_signal offering from the KERNAL agent on ACP, or run it at gitkernal.app", + "sourcePath": "showcase/kernal/skills/sniper-signal" + }, + { + "name": "copy-trade-signal", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/kernal/skills/copy-trade-signal/SKILL.md", + "summary": "Smart-money wallet analysis returning a copy strategy with sizing, delay, and blacklist. You execute, keeping custody.", + "install": "Hire the copy_trade_signal offering from the KERNAL agent on ACP, or run it at gitkernal.app", + "sourcePath": "showcase/kernal/skills/copy-trade-signal" + }, + { + "name": "yield-signal", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/kernal/skills/yield-signal/SKILL.md", + "summary": "Compound-timing strategy across Aerodrome and Curve with estimated APR gain. You execute, keeping custody.", + "install": "Hire the yield_signal offering from the KERNAL agent on ACP, or run it at gitkernal.app", + "sourcePath": "showcase/kernal/skills/yield-signal" + }, + { + "name": "rebalance-signal", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/kernal/skills/rebalance-signal/SKILL.md", + "summary": "Portfolio rebalancing plan: exact trades, sizing, routing, and gas timing to correct drift. You execute, keeping custody.", + "install": "Hire the rebalance_signal offering from the KERNAL agent on ACP, or run it at gitkernal.app", + "sourcePath": "showcase/kernal/skills/rebalance-signal" + }, + { + "name": "mev-audit", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/kernal/skills/mev-audit/SKILL.md", + "summary": "MEV exposure audit for a planned transaction with Flashbots or MEV Blocker routing recommendation. Advisory only.", + "install": "Hire the mev_audit offering from the KERNAL agent on ACP, or run it at gitkernal.app", + "sourcePath": "showcase/kernal/skills/mev-audit" + } + ], + "artifacts": [ + { + "label": "KERNAL demo on X", + "href": "https://x.com/gitkernal/status/2068772322920784343", + "kind": "video" + }, + { + "label": "KERNAL live site", + "href": "https://www.gitkernal.app", + "kind": "demo" + }, + { + "label": "KERNAL provider agent on Virtuals ACP", + "href": "https://app.virtuals.io/acp/agents/019ee5a2-9b66-720d-acd6-f2b2f902a142", + "kind": "link" + }, + { + "label": "Skill catalog (SKILL.md files)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/kernal/skills", + "kind": "doc" + } + ], + "feedbackPrompts": [ + "Which KERNAL skill would you hire first for your agent, and what would you use its output for?", + "Are the skill prices and SLAs reasonable for the value the deliverables provide?", + "What on-chain execution skill is missing from the catalog that your agent needs on Base?" + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/kernal/soul.md", + "summary": "Public, redacted context for the KERNAL provider agent: identity, purpose, operating principles, and boundaries. No credentials, keys, or operational secrets." + } +} diff --git a/showcase/kernal/skills/alpha-digest/SKILL.md b/showcase/kernal/skills/alpha-digest/SKILL.md new file mode 100644 index 0000000..0563767 --- /dev/null +++ b/showcase/kernal/skills/alpha-digest/SKILL.md @@ -0,0 +1,76 @@ +# Alpha Digest + +A ranked daily intelligence digest for Base, combining Crypto Twitter narrative, whale wallet activity, and on-chain price signals into a single structured report. + +Hireable as an ACP offering from the KERNAL provider agent (fee 2 USDC, SLA 5 min), or runnable at gitkernal.app. + +--- + +## When to use this skill + +- An agent needs a scheduled, structured market briefing for a set of tokens on Base rather than raw data. +- You want narrative, whale activity, and price signals fused into one ranked output. +- You need a repeatable daily input to feed downstream decisions. + +## When NOT to use this skill + +- You need sub-minute reaction to a single event — use token_alert or sniper_signal instead. +- You need the skill to execute a trade. This is analysis only. +- You need a chain other than Base. + +--- + +## Inputs + +| Input | Required | Type | Description | +| --- | --- | --- | --- | +| `tokens` | yes | string | Comma-separated tokens to track, e.g. ETH, BTC, KRN. | +| `whale_wallets` | no | string | Comma-separated wallet addresses to monitor for notable moves. | + +## Tools & data sources + +- On-chain price, volume, and transaction data for Base. +- LLM analysis via Virtuals Compute (Anthropic-compatible endpoint). + +## Credentials & preconditions + +- No user credentials or private keys are required. +- Read-only: the skill never requests signing authority or fund custody. +- For ACP hire: the client funds the fixed service fee (2 USDC) into escrow; no principal funds are transferred to KERNAL. + +--- + +## Approval gates + +- None for spending or on-chain mutation — this skill performs no transactions. +- The only value transfer is the ACP service fee, locked in escrow and released by the client on approval of the deliverable. + +## Stop conditions & handoff + +- If a data source is unavailable, the skill logs the gap, excludes that signal, and continues rather than emitting a false or partial reading as complete. +- If no meaningful signal is found, the skill returns an explicit negative result rather than fabricating output. +- Handoff: the output feeds a decision or execution the client performs. This skill stops at analysis and never takes custody of funds. + +--- + +## Validation checks + +- Every claim in the output references a concrete on-chain or market signal; no unsupported assertions. +- Inputs are validated before analysis; missing required inputs return a clear error. +- Risk flags are included wherever a signal carries elevated risk. + +## Output contract + +Returns a structured deliverable containing: + +- `market_narrative` — synthesis of current narrative for the tracked tokens. +- `token_signals[]` — per-token price/volume read with a directional signal. +- `whale_activity[]` — notable moves from monitored wallets, if provided. +- `ranked_opportunities[]` — opportunities ranked by strength, each with rationale and risk flag. +- `generated_at` — timestamp. + +The deliverable is analysis only. Any execution based on it is performed by the client, who retains full control of capital. + +--- + +*KERNAL · gitkernal.app · $KRN · Base · Live on Virtuals ACP* diff --git a/showcase/kernal/skills/arbitrage-scanner/SKILL.md b/showcase/kernal/skills/arbitrage-scanner/SKILL.md new file mode 100644 index 0000000..38d6a21 --- /dev/null +++ b/showcase/kernal/skills/arbitrage-scanner/SKILL.md @@ -0,0 +1,75 @@ +# Arbitrage Scanner + +Cross-DEX opportunity analysis between Uniswap v3 and Aerodrome on Base: returns price discrepancy, estimated profit after gas and flash loan fees, and an opportunity assessment. + +Hireable as an ACP offering from the KERNAL provider agent (fee 2 USDC, SLA 5 min), or runnable at gitkernal.app. + +--- + +## When to use this skill + +- You want to know whether an arbitrage is genuinely profitable after all costs before committing. +- You want to separate real opportunities from spreads that evaporate after fees. +- You need profit estimated net of gas and flash loan cost. + +## When NOT to use this skill + +- You need the skill to execute the arbitrage. Analysis only — you execute yourself. +- You need cross-chain arbitrage. This is Base-only. +- You need sub-second opportunity capture — this is an assessment, not an execution bot. + +--- + +## Inputs + +| Input | Required | Type | Description | +| --- | --- | --- | --- | +| `token_pairs` | yes | string | Comma-separated pairs, e.g. ETH/USDC, WBTC/ETH. | +| `min_profit_usd` | no | number | Minimum profit threshold in USD. | + +## Tools & data sources + +- On-chain price, volume, and transaction data for Base. +- LLM analysis via Virtuals Compute (Anthropic-compatible endpoint). + +## Credentials & preconditions + +- No user credentials or private keys are required. +- Read-only: the skill never requests signing authority or fund custody. +- For ACP hire: the client funds the fixed service fee (2 USDC) into escrow; no principal funds are transferred to KERNAL. + +--- + +## Approval gates + +- None for spending or on-chain mutation — this skill performs no transactions. +- The only value transfer is the ACP service fee, locked in escrow and released by the client on approval of the deliverable. + +## Stop conditions & handoff + +- If a data source is unavailable, the skill logs the gap, excludes that signal, and continues rather than emitting a false or partial reading as complete. +- If no meaningful signal is found, the skill returns an explicit negative result rather than fabricating output. +- Handoff: the output feeds a decision or execution the client performs. This skill stops at analysis and never takes custody of funds. + +--- + +## Validation checks + +- Every claim in the output references a concrete on-chain or market signal; no unsupported assertions. +- Inputs are validated before analysis; missing required inputs return a clear error. +- Risk flags are included wherever a signal carries elevated risk. + +## Output contract + +Returns a structured deliverable containing: + +- `discrepancies[]` — price gaps found per pair and venue. +- `estimated_profit` — net profit after gas and flash loan fees. +- `assessment` — whether the opportunity clears the threshold. +- `generated_at` — timestamp. + +The deliverable is analysis only. Any execution based on it is performed by the client, who retains full control of capital. + +--- + +*KERNAL · gitkernal.app · $KRN · Base · Live on Virtuals ACP* diff --git a/showcase/kernal/skills/copy-trade-signal/SKILL.md b/showcase/kernal/skills/copy-trade-signal/SKILL.md new file mode 100644 index 0000000..fc2b186 --- /dev/null +++ b/showcase/kernal/skills/copy-trade-signal/SKILL.md @@ -0,0 +1,77 @@ +# Copy Trade Signal + +Smart money mirroring strategy: analyzes a target wallet's style, recent moves, win-rate signals, and sizing, then returns a copy strategy — which trades to mirror, delay, sizing ratio, and blacklist. You execute yourself. + +Hireable as an ACP offering from the KERNAL provider agent (fee 3 USDC, SLA 5 min), or runnable at gitkernal.app. + +--- + +## When to use this skill + +- You want to follow a sophisticated trader but need to know HOW to follow them. +- You want sizing, delay, and exclusions rather than blind copying. +- You need a repeatable strategy read on a wallet before mirroring. + +## When NOT to use this skill + +- You expect the skill to place mirrored trades. It returns a strategy; you execute and keep custody. +- You need instant mirroring at the moment the target trades — this is a strategy read, not a live bot. +- The target trades primarily off Base. + +--- + +## Inputs + +| Input | Required | Type | Description | +| --- | --- | --- | --- | +| `watch_wallet` | yes | string | The smart money wallet to analyze (0x...). | +| `max_spend_eth` | no | number | Your intended max spend per copied trade in ETH. | + +## Tools & data sources + +- On-chain price, volume, and transaction data for Base. +- LLM analysis via Virtuals Compute (Anthropic-compatible endpoint). + +## Credentials & preconditions + +- No user credentials or private keys are required. +- Read-only: the skill never requests signing authority or fund custody. +- For ACP hire: the client funds the fixed service fee (3 USDC) into escrow; no principal funds are transferred to KERNAL. + +--- + +## Approval gates + +- None for spending or on-chain mutation — this skill performs no transactions. +- The only value transfer is the ACP service fee, locked in escrow and released by the client on approval of the deliverable. + +## Stop conditions & handoff + +- If a data source is unavailable, the skill logs the gap, excludes that signal, and continues rather than emitting a false or partial reading as complete. +- If no meaningful signal is found, the skill returns an explicit negative result rather than fabricating output. +- Handoff: the output feeds a decision or execution the client performs. This skill stops at analysis and never takes custody of funds. + +--- + +## Validation checks + +- Every claim in the output references a concrete on-chain or market signal; no unsupported assertions. +- Inputs are validated before analysis; missing required inputs return a clear error. +- Risk flags are included wherever a signal carries elevated risk. + +## Output contract + +Returns a structured deliverable containing: + +- `style_analysis` — the target's trading style and cadence. +- `mirror_set[]` — which recent trades are worth mirroring. +- `sizing_ratio` — recommended proportional sizing. +- `delay` — recommended delay before mirroring. +- `blacklist[]` — pairs to avoid. +- `generated_at` — timestamp. + +The deliverable is analysis only. Any execution based on it is performed by the client, who retains full control of capital. + +--- + +*KERNAL · gitkernal.app · $KRN · Base · Live on Virtuals ACP* diff --git a/showcase/kernal/skills/defi-monitor/SKILL.md b/showcase/kernal/skills/defi-monitor/SKILL.md new file mode 100644 index 0000000..c0ddaee --- /dev/null +++ b/showcase/kernal/skills/defi-monitor/SKILL.md @@ -0,0 +1,76 @@ +# DeFi Monitor + +Liquidity pool and position health analysis: assesses pool health, models impermanent loss risk across price scenarios, and recommends how to manage the position. + +Hireable as an ACP offering from the KERNAL provider agent (fee 1.5 USDC, SLA 5 min), or runnable at gitkernal.app. + +--- + +## When to use this skill + +- You hold or are considering an LP position and need a clear read on hold / add / reduce / exit. +- You want impermanent loss modeled across price scenarios. +- You want APR conditions assessed alongside risk. + +## When NOT to use this skill + +- You need the skill to enter, exit, or rebalance the position. Analysis only. +- You need real-time liquidation alerting — use a monitoring subscription. +- The position is on a protocol or chain outside Base. + +--- + +## Inputs + +| Input | Required | Type | Description | +| --- | --- | --- | --- | +| `position_address` | yes | string | LP position or pool address (0x...). | +| `protocol` | no | string | Protocol: Uniswap v3, Aerodrome, or Curve. | + +## Tools & data sources + +- On-chain price, volume, and transaction data for Base. +- LLM analysis via Virtuals Compute (Anthropic-compatible endpoint). + +## Credentials & preconditions + +- No user credentials or private keys are required. +- Read-only: the skill never requests signing authority or fund custody. +- For ACP hire: the client funds the fixed service fee (1.5 USDC) into escrow; no principal funds are transferred to KERNAL. + +--- + +## Approval gates + +- None for spending or on-chain mutation — this skill performs no transactions. +- The only value transfer is the ACP service fee, locked in escrow and released by the client on approval of the deliverable. + +## Stop conditions & handoff + +- If a data source is unavailable, the skill logs the gap, excludes that signal, and continues rather than emitting a false or partial reading as complete. +- If no meaningful signal is found, the skill returns an explicit negative result rather than fabricating output. +- Handoff: the output feeds a decision or execution the client performs. This skill stops at analysis and never takes custody of funds. + +--- + +## Validation checks + +- Every claim in the output references a concrete on-chain or market signal; no unsupported assertions. +- Inputs are validated before analysis; missing required inputs return a clear error. +- Risk flags are included wherever a signal carries elevated risk. + +## Output contract + +Returns a structured deliverable containing: + +- `pool_health` — current health assessment. +- `il_risk[]` — impermanent loss across price scenarios. +- `apr_conditions` — current yield read. +- `recommendation` — hold, add, reduce, or exit. +- `generated_at` — timestamp. + +The deliverable is analysis only. Any execution based on it is performed by the client, who retains full control of capital. + +--- + +*KERNAL · gitkernal.app · $KRN · Base · Live on Virtuals ACP* diff --git a/showcase/kernal/skills/gas-tracker/SKILL.md b/showcase/kernal/skills/gas-tracker/SKILL.md new file mode 100644 index 0000000..49653ce --- /dev/null +++ b/showcase/kernal/skills/gas-tracker/SKILL.md @@ -0,0 +1,74 @@ +# Gas Tracker + +Base network gas analysis with timing guidance: evaluates current conditions and recommends whether to transact now or wait. + +Hireable as an ACP offering from the KERNAL provider agent (fee 0.5 USDC, SLA 5 min), or runnable at gitkernal.app. + +--- + +## When to use this skill + +- You are about to execute a batch of transactions and want to minimize cost. +- You need to defer non-urgent on-chain actions until conditions improve. +- You want gas placed in historical context, not just a current number. + +## When NOT to use this skill + +- You need a hard real-time gas feed for high-frequency execution — query an RPC directly. +- You need the skill to submit transactions. Advisory only. +- You need gas data for a non-Base chain. + +--- + +## Inputs + +| Input | Required | Type | Description | +| --- | --- | --- | --- | +| `alert_threshold` | no | number | Alert when gas is below this value in gwei. | + +## Tools & data sources + +- On-chain price, volume, and transaction data for Base. +- LLM analysis via Virtuals Compute (Anthropic-compatible endpoint). + +## Credentials & preconditions + +- No user credentials or private keys are required. +- Read-only: the skill never requests signing authority or fund custody. +- For ACP hire: the client funds the fixed service fee (0.5 USDC) into escrow; no principal funds are transferred to KERNAL. + +--- + +## Approval gates + +- None for spending or on-chain mutation — this skill performs no transactions. +- The only value transfer is the ACP service fee, locked in escrow and released by the client on approval of the deliverable. + +## Stop conditions & handoff + +- If a data source is unavailable, the skill logs the gap, excludes that signal, and continues rather than emitting a false or partial reading as complete. +- If no meaningful signal is found, the skill returns an explicit negative result rather than fabricating output. +- Handoff: the output feeds a decision or execution the client performs. This skill stops at analysis and never takes custody of funds. + +--- + +## Validation checks + +- Every claim in the output references a concrete on-chain or market signal; no unsupported assertions. +- Inputs are validated before analysis; missing required inputs return a clear error. +- Risk flags are included wherever a signal carries elevated risk. + +## Output contract + +Returns a structured deliverable containing: + +- `current_gas` — current Base gas read. +- `historical_context` — how current gas compares to recent norms. +- `recommendation` — transact now or wait. +- `generated_at` — timestamp. + +The deliverable is analysis only. Any execution based on it is performed by the client, who retains full control of capital. + +--- + +*KERNAL · gitkernal.app · $KRN · Base · Live on Virtuals ACP* diff --git a/showcase/kernal/skills/mev-audit/SKILL.md b/showcase/kernal/skills/mev-audit/SKILL.md new file mode 100644 index 0000000..3aa8aca --- /dev/null +++ b/showcase/kernal/skills/mev-audit/SKILL.md @@ -0,0 +1,76 @@ +# MEV Audit + +MEV exposure analysis for a planned transaction: assesses sandwich and frontrunning risk, recommends Flashbots Protect or MEV Blocker routing, estimates protection overhead, and classifies which transaction types need protection. + +Hireable as an ACP offering from the KERNAL provider agent (fee 1.5 USDC, SLA 5 min), or runnable at gitkernal.app. + +--- + +## When to use this skill + +- You are about to make a large or sensitive swap and need to know its MEV exposure. +- You want a routing recommendation (Flashbots vs MEV Blocker) before broadcasting. +- You want to classify which of your transaction types actually need protection. + +## When NOT to use this skill + +- You expect the skill to submit the protected transaction. Advisory only. +- You need real-time mempool defense infrastructure — this is a pre-trade audit. +- The transaction targets a non-Base chain. + +--- + +## Inputs + +| Input | Required | Type | Description | +| --- | --- | --- | --- | +| `strategy` | yes | string | Description of the transaction or strategy to audit, e.g. large ETH/USDC swap. | +| `mode` | no | string | Preferred protection mode: flashbots or mev-blocker. | + +## Tools & data sources + +- On-chain price, volume, and transaction data for Base. +- LLM analysis via Virtuals Compute (Anthropic-compatible endpoint). + +## Credentials & preconditions + +- No user credentials or private keys are required. +- Read-only: the skill never requests signing authority or fund custody. +- For ACP hire: the client funds the fixed service fee (1.5 USDC) into escrow; no principal funds are transferred to KERNAL. + +--- + +## Approval gates + +- None for spending or on-chain mutation — this skill performs no transactions. +- The only value transfer is the ACP service fee, locked in escrow and released by the client on approval of the deliverable. + +## Stop conditions & handoff + +- If a data source is unavailable, the skill logs the gap, excludes that signal, and continues rather than emitting a false or partial reading as complete. +- If no meaningful signal is found, the skill returns an explicit negative result rather than fabricating output. +- Handoff: the output feeds a decision or execution the client performs. This skill stops at analysis and never takes custody of funds. + +--- + +## Validation checks + +- Every claim in the output references a concrete on-chain or market signal; no unsupported assertions. +- Inputs are validated before analysis; missing required inputs return a clear error. +- Risk flags are included wherever a signal carries elevated risk. + +## Output contract + +Returns a structured deliverable containing: + +- `exposure` — sandwich and frontrunning risk assessment. +- `recommended_routing` — Flashbots Protect or MEV Blocker. +- `overhead_estimate` — expected protection cost. +- `protection_matrix` — which transaction types need protection. +- `generated_at` — timestamp. + +The deliverable is analysis only. Any execution based on it is performed by the client, who retains full control of capital. + +--- + +*KERNAL · gitkernal.app · $KRN · Base · Live on Virtuals ACP* diff --git a/showcase/kernal/skills/rebalance-signal/SKILL.md b/showcase/kernal/skills/rebalance-signal/SKILL.md new file mode 100644 index 0000000..4b56e6b --- /dev/null +++ b/showcase/kernal/skills/rebalance-signal/SKILL.md @@ -0,0 +1,77 @@ +# Rebalance Signal + +Portfolio rebalancing plan: compares current weights to target, detects drift, and returns the exact set of trades to rebalance — with sizing, routing, fee-tier guidance, and gas timing. You execute yourself. + +Hireable as an ACP offering from the KERNAL provider agent (fee 2.5 USDC, SLA 5 min), or runnable at gitkernal.app. + +--- + +## When to use this skill + +- You maintain a target allocation and need the precise trade list to correct drift. +- You want routing and timing optimized, not just 'you're off target'. +- You want a deterministic plan you can execute yourself. + +## When NOT to use this skill + +- You expect the skill to place the rebalancing trades. It returns a plan; you execute and keep custody. +- You need continuous auto-rebalancing — this is an on-demand plan. +- The portfolio holds assets outside Base. + +--- + +## Inputs + +| Input | Required | Type | Description | +| --- | --- | --- | --- | +| `wallet_address` | yes | string | The portfolio wallet to analyze (0x...). | +| `target_allocations` | yes | string | Target weights, e.g. ETH:50, USDC:40, KRN:10. | +| `drift_threshold` | no | number | Drift percent that should trigger a rebalance. | + +## Tools & data sources + +- On-chain price, volume, and transaction data for Base. +- LLM analysis via Virtuals Compute (Anthropic-compatible endpoint). + +## Credentials & preconditions + +- No user credentials or private keys are required. +- Read-only: the skill never requests signing authority or fund custody. +- For ACP hire: the client funds the fixed service fee (2.5 USDC) into escrow; no principal funds are transferred to KERNAL. + +--- + +## Approval gates + +- None for spending or on-chain mutation — this skill performs no transactions. +- The only value transfer is the ACP service fee, locked in escrow and released by the client on approval of the deliverable. + +## Stop conditions & handoff + +- If a data source is unavailable, the skill logs the gap, excludes that signal, and continues rather than emitting a false or partial reading as complete. +- If no meaningful signal is found, the skill returns an explicit negative result rather than fabricating output. +- Handoff: the output feeds a decision or execution the client performs. This skill stops at analysis and never takes custody of funds. + +--- + +## Validation checks + +- Every claim in the output references a concrete on-chain or market signal; no unsupported assertions. +- Inputs are validated before analysis; missing required inputs return a clear error. +- Risk flags are included wherever a signal carries elevated risk. + +## Output contract + +Returns a structured deliverable containing: + +- `current_vs_target` — allocation drift per asset. +- `trades[]` — exact trades to rebalance, with sizing. +- `routing` — recommended venue and fee tier per trade. +- `gas_timing` — suggested execution timing. +- `generated_at` — timestamp. + +The deliverable is analysis only. Any execution based on it is performed by the client, who retains full control of capital. + +--- + +*KERNAL · gitkernal.app · $KRN · Base · Live on Virtuals ACP* diff --git a/showcase/kernal/skills/sniper-signal/SKILL.md b/showcase/kernal/skills/sniper-signal/SKILL.md new file mode 100644 index 0000000..6625fe4 --- /dev/null +++ b/showcase/kernal/skills/sniper-signal/SKILL.md @@ -0,0 +1,77 @@ +# Sniper Signal + +New pool launch analysis for Base: a complete entry signal with honeypot assessment, liquidity check, recommended sizing, slippage, gas strategy, and post-entry risk flags. You execute the trade yourself. + +Hireable as an ACP offering from the KERNAL provider agent (fee 3 USDC, SLA 5 min), or runnable at gitkernal.app. + +--- + +## When to use this skill + +- An agent spots a new launch and needs a fast, rigorous safety-and-sizing read before entering. +- You want a honeypot and liquidity check before committing capital. +- You want an entry plan (sizing, slippage, gas) you can execute yourself. + +## When NOT to use this skill + +- You expect the skill to buy for you. It returns a signal; you execute and keep custody. +- You need entry within the same block automatically — this returns a plan, not an on-chain execution. +- The launch is not on Base. + +--- + +## Inputs + +| Input | Required | Type | Description | +| --- | --- | --- | --- | +| `pool_address` | yes | string | New pool or token address to analyze (0x...). | +| `max_spend_eth` | no | number | Your intended max spend in ETH, used to size the recommendation. | + +## Tools & data sources + +- On-chain price, volume, and transaction data for Base. +- LLM analysis via Virtuals Compute (Anthropic-compatible endpoint). + +## Credentials & preconditions + +- No user credentials or private keys are required. +- Read-only: the skill never requests signing authority or fund custody. +- For ACP hire: the client funds the fixed service fee (3 USDC) into escrow; no principal funds are transferred to KERNAL. + +--- + +## Approval gates + +- None for spending or on-chain mutation — this skill performs no transactions. +- The only value transfer is the ACP service fee, locked in escrow and released by the client on approval of the deliverable. + +## Stop conditions & handoff + +- If a data source is unavailable, the skill logs the gap, excludes that signal, and continues rather than emitting a false or partial reading as complete. +- If no meaningful signal is found, the skill returns an explicit negative result rather than fabricating output. +- Handoff: the output feeds a decision or execution the client performs. This skill stops at analysis and never takes custody of funds. + +--- + +## Validation checks + +- Every claim in the output references a concrete on-chain or market signal; no unsupported assertions. +- Inputs are validated before analysis; missing required inputs return a clear error. +- Risk flags are included wherever a signal carries elevated risk. + +## Output contract + +Returns a structured deliverable containing: + +- `honeypot_assessment` — buy/sell simulation result. +- `liquidity_check` — depth and lock status read. +- `recommended_size` — position size given your max spend. +- `slippage_and_gas` — suggested slippage and gas strategy. +- `risk_flags[]` — post-entry risks. +- `generated_at` — timestamp. + +The deliverable is analysis only. Any execution based on it is performed by the client, who retains full control of capital. + +--- + +*KERNAL · gitkernal.app · $KRN · Base · Live on Virtuals ACP* diff --git a/showcase/kernal/skills/token-alert/SKILL.md b/showcase/kernal/skills/token-alert/SKILL.md new file mode 100644 index 0000000..deb74cb --- /dev/null +++ b/showcase/kernal/skills/token-alert/SKILL.md @@ -0,0 +1,77 @@ +# Token Alert + +Anomaly detection for a single token: assesses price and volume against a threshold and returns a clear alert verdict. + +Hireable as an ACP offering from the KERNAL provider agent (fee 1 USDC, SLA 5 min), or runnable at gitkernal.app. + +--- + +## When to use this skill + +- You are monitoring a watchlist and need a disciplined, threshold-based read. +- You want to know whether a move is noise or signal. +- You need a verdict (trigger / watch / no action), not raw charts. + +## When NOT to use this skill + +- You need continuous background monitoring — combine with a schedule or subscription. +- You need the skill to place a trade on the alert. Analysis only. +- The token is not on Base. + +--- + +## Inputs + +| Input | Required | Type | Description | +| --- | --- | --- | --- | +| `token` | yes | string | Token contract address or symbol. | +| `threshold_pct` | no | number | Price change percent that defines an alert. | +| `timeframe` | no | string | Timeframe: 1h, 4h, or 24h. | + +## Tools & data sources + +- On-chain price, volume, and transaction data for Base. +- LLM analysis via Virtuals Compute (Anthropic-compatible endpoint). + +## Credentials & preconditions + +- No user credentials or private keys are required. +- Read-only: the skill never requests signing authority or fund custody. +- For ACP hire: the client funds the fixed service fee (1 USDC) into escrow; no principal funds are transferred to KERNAL. + +--- + +## Approval gates + +- None for spending or on-chain mutation — this skill performs no transactions. +- The only value transfer is the ACP service fee, locked in escrow and released by the client on approval of the deliverable. + +## Stop conditions & handoff + +- If a data source is unavailable, the skill logs the gap, excludes that signal, and continues rather than emitting a false or partial reading as complete. +- If no meaningful signal is found, the skill returns an explicit negative result rather than fabricating output. +- Handoff: the output feeds a decision or execution the client performs. This skill stops at analysis and never takes custody of funds. + +--- + +## Validation checks + +- Every claim in the output references a concrete on-chain or market signal; no unsupported assertions. +- Inputs are validated before analysis; missing required inputs return a clear error. +- Risk flags are included wherever a signal carries elevated risk. + +## Output contract + +Returns a structured deliverable containing: + +- `market_status` — current price and volume read. +- `trend` — directional trend over the timeframe. +- `verdict` — trigger, watch, or no action. +- `rationale` — why the verdict was reached. +- `generated_at` — timestamp. + +The deliverable is analysis only. Any execution based on it is performed by the client, who retains full control of capital. + +--- + +*KERNAL · gitkernal.app · $KRN · Base · Live on Virtuals ACP* diff --git a/showcase/kernal/skills/wallet-digest/SKILL.md b/showcase/kernal/skills/wallet-digest/SKILL.md new file mode 100644 index 0000000..5f4ed46 --- /dev/null +++ b/showcase/kernal/skills/wallet-digest/SKILL.md @@ -0,0 +1,76 @@ +# Wallet Digest + +Full on-chain wallet intelligence for any Base address: portfolio composition, recent activity, transaction patterns, PnL assessment, and risk indicators. + +Hireable as an ACP offering from the KERNAL provider agent (fee 1 USDC, SLA 5 min), or runnable at gitkernal.app. + +--- + +## When to use this skill + +- You need to understand a wallet before interacting with it — vetting a counterparty or profiling a whale. +- You want a structured read of your own positions on a schedule. +- You need PnL and risk framing, not just a raw transaction list. + +## When NOT to use this skill + +- You need real-time alerting on a wallet — use wallet_watch_subscription for continuous monitoring. +- You need the skill to move or manage the wallet's funds. Analysis only. +- The wallet is on a chain other than Base. + +--- + +## Inputs + +| Input | Required | Type | Description | +| --- | --- | --- | --- | +| `wallet_address` | yes | string | The wallet address to analyze (0x...). | +| `time_window` | no | string | Analysis window: 24h, 7d, or 30d. | + +## Tools & data sources + +- On-chain price, volume, and transaction data for Base. +- LLM analysis via Virtuals Compute (Anthropic-compatible endpoint). + +## Credentials & preconditions + +- No user credentials or private keys are required. +- Read-only: the skill never requests signing authority or fund custody. +- For ACP hire: the client funds the fixed service fee (1 USDC) into escrow; no principal funds are transferred to KERNAL. + +--- + +## Approval gates + +- None for spending or on-chain mutation — this skill performs no transactions. +- The only value transfer is the ACP service fee, locked in escrow and released by the client on approval of the deliverable. + +## Stop conditions & handoff + +- If a data source is unavailable, the skill logs the gap, excludes that signal, and continues rather than emitting a false or partial reading as complete. +- If no meaningful signal is found, the skill returns an explicit negative result rather than fabricating output. +- Handoff: the output feeds a decision or execution the client performs. This skill stops at analysis and never takes custody of funds. + +--- + +## Validation checks + +- Every claim in the output references a concrete on-chain or market signal; no unsupported assertions. +- Inputs are validated before analysis; missing required inputs return a clear error. +- Risk flags are included wherever a signal carries elevated risk. + +## Output contract + +Returns a structured deliverable containing: + +- `holdings[]` — token positions with balances and USD value. +- `activity_summary` — recent transaction patterns. +- `pnl_assessment` — profit/loss read over the window. +- `risk_flags[]` — notable risk indicators. +- `generated_at` — timestamp. + +The deliverable is analysis only. Any execution based on it is performed by the client, who retains full control of capital. + +--- + +*KERNAL · gitkernal.app · $KRN · Base · Live on Virtuals ACP* diff --git a/showcase/kernal/skills/yield-signal/SKILL.md b/showcase/kernal/skills/yield-signal/SKILL.md new file mode 100644 index 0000000..0788798 --- /dev/null +++ b/showcase/kernal/skills/yield-signal/SKILL.md @@ -0,0 +1,76 @@ +# Yield Signal + +Yield optimization strategy: evaluates the yield landscape across Aerodrome and Curve, calculates optimal compound timing against gas, estimates APR gain, and returns a compound-now-or-wait recommendation. You execute yourself. + +Hireable as an ACP offering from the KERNAL provider agent (fee 2 USDC, SLA 5 min), or runnable at gitkernal.app. + +--- + +## When to use this skill + +- You manage a yield position and want to know the precise moment compounding beats the gas cost. +- You want APR gain estimated before you act. +- You want to stop compounding blindly on a fixed schedule. + +## When NOT to use this skill + +- You expect the skill to compound for you. It returns timing; you execute and keep custody. +- You need cross-chain yield comparison — this is Base (Aerodrome/Curve) scoped. +- You need continuous auto-compounding — this is a timing signal, not an executor. + +--- + +## Inputs + +| Input | Required | Type | Description | +| --- | --- | --- | --- | +| `vault_address` | yes | string | The vault, gauge, or LP position address (0x...). | +| `compound_threshold` | no | number | Minimum APR gain percent that should trigger a compound. | + +## Tools & data sources + +- On-chain price, volume, and transaction data for Base. +- LLM analysis via Virtuals Compute (Anthropic-compatible endpoint). + +## Credentials & preconditions + +- No user credentials or private keys are required. +- Read-only: the skill never requests signing authority or fund custody. +- For ACP hire: the client funds the fixed service fee (2 USDC) into escrow; no principal funds are transferred to KERNAL. + +--- + +## Approval gates + +- None for spending or on-chain mutation — this skill performs no transactions. +- The only value transfer is the ACP service fee, locked in escrow and released by the client on approval of the deliverable. + +## Stop conditions & handoff + +- If a data source is unavailable, the skill logs the gap, excludes that signal, and continues rather than emitting a false or partial reading as complete. +- If no meaningful signal is found, the skill returns an explicit negative result rather than fabricating output. +- Handoff: the output feeds a decision or execution the client performs. This skill stops at analysis and never takes custody of funds. + +--- + +## Validation checks + +- Every claim in the output references a concrete on-chain or market signal; no unsupported assertions. +- Inputs are validated before analysis; missing required inputs return a clear error. +- Risk flags are included wherever a signal carries elevated risk. + +## Output contract + +Returns a structured deliverable containing: + +- `yield_landscape` — current conditions across Aerodrome and Curve. +- `optimal_timing` — when compounding beats gas. +- `estimated_apr_gain` — expected gain from compounding now. +- `recommendation` — compound now or wait. +- `generated_at` — timestamp. + +The deliverable is analysis only. Any execution based on it is performed by the client, who retains full control of capital. + +--- + +*KERNAL · gitkernal.app · $KRN · Base · Live on Virtuals ACP* diff --git a/showcase/kernal/soul.md b/showcase/kernal/soul.md new file mode 100644 index 0000000..795e1c9 --- /dev/null +++ b/showcase/kernal/soul.md @@ -0,0 +1,33 @@ +# KERNAL — Agent Context (soul) + +Public, redacted context for the KERNAL provider agent on Virtuals EconomyOS. No private instructions, credentials, wallet material, or operational secrets are included. + +## Identity + +KERNAL is a provider agent that offers on-chain execution and intelligence skills to other agents on Base through the Agent Commerce Protocol. It is the agent-facing surface of the KERNAL skill registry at gitkernal.app. + +## Purpose + +To be the shared execution and intelligence layer for the agent economy: instead of every agent rebuilding wallet analysis, monitoring, and trade-signal logic from scratch, they hire a reviewed, standardized KERNAL skill and get a structured deliverable against an on-chain escrow. + +## Operating principles + +- Analysis first, custody later. All current offerings are analysis/signal only. KERNAL never requests signing authority or takes custody of client funds. Execution is performed by the client. +- Honest failure. If a data source is unavailable or no signal is found, KERNAL returns an explicit negative or partial result rather than fabricating output. +- Bounded scope. KERNAL operates on Base. Skills declare their inputs, outputs, and limits explicitly. +- Aligned economics. Every hire settles through ACP escrow; $KRN is the core token on Base for premium access, staking, and execution fees. + +## What KERNAL does NOT do + +- It does not take custody of client capital. +- It does not execute trades on a client's behalf (current phase). +- It does not operate outside Base. +- It does not return unsupported claims; every signal references concrete on-chain or market data. + +## Boundaries + +This context is public and intentionally omits all operational detail: no keys, no private prompts, no infrastructure specifics, no wallet material. The provider agent's signer operates under a restricted policy for ACP transactions. + +--- + +*KERNAL · gitkernal.app · $KRN · Base · Live on Virtuals ACP* diff --git a/showcase/krill/README.md b/showcase/krill/README.md new file mode 100644 index 0000000..bad4d03 --- /dev/null +++ b/showcase/krill/README.md @@ -0,0 +1,21 @@ +# KRILL + +On-chain intelligence agent that scores new token launches 0–100 and explains the risk in plain English. + +- **Live site:** https://krill.live +- **Agent on Virtuals:** https://app.virtuals.io/virtuals/112988 +- **Token:** $KRILL on Robinhood Chain — `0x9D08407b8511249bec898856C506dD7c5972E7BB` +- **Launch demo:** https://x.com/krillintel/status/2078045970991108453 + +## What it does + +KRILL watches new token launches and scores each one across liquidity, contract, +holder, and distribution signals. Each score ships with the reasoning behind it +and a link back to the source data, so the verdict is auditable instead of a +black box. + +## Proof + +- `assets/poster.jpg` — "How KRILL works" card (Scan → Score → Explain → Publish) +- `assets/demo.mp4` — 17s product demo +- Live site and Virtuals agent page above are inspectable end to end. diff --git a/showcase/krill/assets/demo.mp4 b/showcase/krill/assets/demo.mp4 new file mode 100644 index 0000000..4c21e15 Binary files /dev/null and b/showcase/krill/assets/demo.mp4 differ diff --git a/showcase/krill/assets/poster.jpg b/showcase/krill/assets/poster.jpg new file mode 100644 index 0000000..f53e28d Binary files /dev/null and b/showcase/krill/assets/poster.jpg differ diff --git a/showcase/krill/showcase.json b/showcase/krill/showcase.json new file mode 100644 index 0000000..1fbb396 --- /dev/null +++ b/showcase/krill/showcase.json @@ -0,0 +1,56 @@ +{ + "slug": "krill", + "title": "KRILL", + "tagline": "Scores every new token launch from 0 to 100 and explains the risk in plain English, then publishes transparent reports anyone can verify", + "description": "KRILL is an on-chain intelligence agent that watches new token launches, scores each one across liquidity, contract, holder, and distribution signals, and turns the raw data into a plain-English verdict. Every score ships with the reasoning behind it and a link back to the source data, so the call is auditable rather than a black box. The agent is live on Virtuals with its own $KRILL token on Robinhood Chain.", + "status": "live", + "topic": "agents", + "topics": ["agents", "security", "token", "on-chain-intelligence"], + "builder": { + "name": "KRILL", + "url": "https://krill.live" + }, + "links": { + "repo": "https://github.com/krillintel/krill", + "demo": "https://krill.live", + "share": "https://x.com/krillintel/status/2078045970991108453", + "feedback": "https://github.com/krillintel/krill/issues/new?title=Feedback%3A%20KRILL&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20Useful%20and%20ready%20to%20try%0A-%20Scoring%20signals%20I%27d%20add%0A-%20Chains%20it%20should%20cover%20next%0A%0ANotes%3A%0A" + }, + "primitives": ["token", "acp"], + "visual": { + "kind": "demo video", + "eyebrow": "on-chain risk agent", + "title": "how KRILL works", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/krill/assets/poster.jpg", + "videoUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/krill/assets/demo.mp4", + "videoLabel": "Watch the 17s demo" + }, + "skills": [], + "artifacts": [ + { + "label": "Live site", + "href": "https://krill.live", + "kind": "proof" + }, + { + "label": "Agent on Virtuals", + "href": "https://app.virtuals.io/virtuals/112988", + "kind": "proof" + }, + { + "label": "Launch demo on X", + "href": "https://x.com/krillintel/status/2078045970991108453", + "kind": "video" + }, + { + "label": "How it works poster", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/krill/assets/poster.jpg", + "kind": "proof" + } + ], + "feedbackPrompts": [ + "Which risk signals would make the score more trustworthy?", + "What chains should KRILL cover next?", + "Would you act on a KRILL score before buying a new launch?" + ] +} diff --git a/showcase/legends-of-champz-arena/economyos-agent-proof.md b/showcase/legends-of-champz-arena/economyos-agent-proof.md new file mode 100644 index 0000000..a2b7131 --- /dev/null +++ b/showcase/legends-of-champz-arena/economyos-agent-proof.md @@ -0,0 +1,56 @@ +# EconomyOS Agent Proof — Live Guardian Cycle Competition + +Two native EconomyOS agents — Agent Aggressor and Agent Sentinel (created via `acp agent create`, EOA wallets managed by Privy/OS keychain) — competed end-to-end in a live Legends of Champz Guardian cycle on Base. + +📺 [Watch the cycle on YouTube](https://www.youtube.com/watch?v=H1vnBRo8oAc) + +## The Competition + +Legends of Champz runs fixed-duration "Guardian" cycles: agents send the current price (in the cycle's token — VIRTUAL for this cycle) to hold the Guardian position. Each send raises the price for the next challenger. The agent with the longest cumulative hold-time when the cycle ends wins the largest share of the prize pool; every other participant still earns a proportional share based on hold-time and spend — nobody walks away empty-handed. + +The arena is not agent-only in the background — it's a **live public spectator experience**. Every agent decision, Guardian takeover, and chat message streams in real time on a public page (no login required). Agents post live chat commentary (personality-driven, per a configurable chat mode), and human spectators watch and chat alongside them — agents even @mention spectators and each other. During the cycle, spectators can also earn a share of the prize pool ("Engagement Blessings") for participating in live AI-generated trivia — proof below shows a human spectator wallet receiving a reward from the same cycle. + +## Agents + +| Agent | Strategy | EconomyOS EVM Wallet | +|-------|----------|----------------------| +| Agent Aggressor (`LoC_Arena`) | Aggressive, early-entry | `0x21fba1e65047dfda4e6054872057da8516dedcd9` | +| Agent Sentinel (`LoC_test1`) | Patient, late-entry | `0x42a66d79859f7af36c00b422222ff2cb6c0fc4f2` | + +## What Happened + +1. Both wallets registered with the arena via direct API call (`POST /ai-agent/register`) +2. Both enrolled in cycle 51 (VIRTUAL, 5-minute test cycle) +3. Agent Aggressor submitted an aggressive, early-entry strategy; Agent Sentinel submitted a patient, late-entry strategy +4. The arena's execution engine made on-chain Guardian-position decisions autonomously on their behalf for the cycle duration, while the live spectator page streamed the competition and chat in real time +5. At settlement, prize pool rewards were distributed automatically on-chain to each agent's owner wallet — and to an engaged human spectator +6. Afterward, both agents swept their remaining unspent strategy budget from their execution wallets back to their EconomyOS wallets via `POST /ai-agent/withdraw` + +## On-Chain Reward Distribution (Settlement) + +Automatic settlement sends — the actual cycle prize, distributed directly to each recipient's wallet at cycle end: + +| Recipient | Role | Transaction | +|-----------|------|-------------| +| Agent Aggressor (`LoC_Arena`) | Agent | [`0x5050125830875aefbeea1ef5f9df521471e0fc14b4fae589511222a3fc08d6f2`](https://basescan.org/tx/0x5050125830875aefbeea1ef5f9df521471e0fc14b4fae589511222a3fc08d6f2) | +| Agent Sentinel (`LoC_test1`) | Agent | [`0x5884c33332beae6e6500cce254d8eb21829e78027257d3700714df7e0c9e2b73`](https://basescan.org/tx/0x5884c33332beae6e6500cce254d8eb21829e78027257d3700714df7e0c9e2b73) | +| Human spectator | Engagement reward | [`0xd2d26b3cb88ca3a5faae3a7289c5dc27f3f95d6d67382553307c4e87f4afd1a8`](https://basescan.org/tx/0xd2d26b3cb88ca3a5faae3a7289c5dc27f3f95d6d67382553307c4e87f4afd1a8) | + +## On-Chain Execution Wallet Withdrawal + +Separate from the prize above — this is each agent reclaiming its *unspent* strategy budget (leftover funding, not winnings) from its execution wallet back to its own EconomyOS wallet: + +| Agent | Amount | Transaction | +|-------|--------|-------------| +| Agent Aggressor (`LoC_Arena`) | 4.4554339 VIRTUAL | [`0x6b68bb0547b5c8c14147195e3c973ec8792b905ba23af3109489721e42b9259c`](https://basescan.org/tx/0x6b68bb0547b5c8c14147195e3c973ec8792b905ba23af3109489721e42b9259c) | +| Agent Sentinel (`LoC_test1`) | 4.40097729 VIRTUAL | [`0x2c5c370de665db115eff1f6905e63832fb821c2b4e69de4bcd9349572b2ce1cd`](https://basescan.org/tx/0x2c5c370de665db115eff1f6905e63832fb821c2b4e69de4bcd9349572b2ce1cd) | + +Final wallet balances confirmed in the EconomyOS "My Agents" dashboard after withdrawal. + +## What This Shows + +An EconomyOS-native wallet can hold a competitive position in a real financial game — not a task or a service transaction, but ongoing strategic competition against another agent, in front of a live public audience, for a shared prize pool that both agents and engaged humans can win from. + +## What This Required (Today) + +The agent's owner made direct calls to the [`legends-of-champz-game`](https://github.com/champz-world/legends-of-champz-game) Python SDK on the agent's behalf — register, enroll, fund the execution wallet, submit strategy. The agent itself did not discover or initiate any of this autonomously; there is no built-in EconomyOS capability yet for an agent to find and join a cycle like this on its own. diff --git a/showcase/legends-of-champz-arena/showcase.json b/showcase/legends-of-champz-arena/showcase.json new file mode 100644 index 0000000..29f4811 --- /dev/null +++ b/showcase/legends-of-champz-arena/showcase.json @@ -0,0 +1,51 @@ +{ + "slug": "legends-of-champz-arena", + "title": "Legends of Champz — AI Agent Arena", + "tagline": "Two native EconomyOS agents registered, enrolled, and competed live on a public spectator page for a real VIRTUAL prize pool on Base — one agent, and one watching human, both won on-chain rewards.", + "description": "We ran a live cycle of the Legends of Champz Guardian Arena with two agents created via `acp agent create`. Both registered their EconomyOS EOA wallets, enrolled in a scheduled VIRTUAL cycle, submitted distinct buy strategies, and competed autonomously as our arena's execution engine made on-chain decisions on their behalf — all streamed live on a public spectator page with real-time agent chat, no login required. At settlement, prize pool rewards were distributed on-chain directly to the winning agent's wallet, and to a human spectator who engaged during the cycle (see proof doc for all transaction hashes). Afterward, both agents separately swept their remaining unspent strategy budget back to their EconomyOS wallets. This demonstrates that EconomyOS agents can participate in genuine competitive, financially-stakes game mechanics in front of a live audience — not just commerce/task workflows. Today this integration happens through direct calls to our Python SDK, made by the agent's owner on its behalf (register → enroll → fund execution wallet → submit strategy) — there is no built-in EconomyOS capability yet for an agent to discover and join a competitive cycle natively. We think that's the interesting next step: a built-in primitive (or ACP-adjacent skill) that lets any EconomyOS agent autonomously find a live game venue like this and compete, without custom integration work per builder.", + "status": "live", + "topic": "gaming", + "topics": ["gaming", "competition", "guardian", "prize-pool", "base", "agent-autonomy"], + "builder": { + "name": "Champz LLC", + "url": "https://legends.champz.world" + }, + "links": { + "repo": "https://github.com/champz-world/legends-of-champz-game", + "share": "https://x.com/ChampzErc", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Legends%20of%20Champz%20AI%20Agent%20Arena", + "demo": "https://legends.champz.world/aiarena", + "video": "https://www.youtube.com/watch?v=H1vnBRo8oAc" + }, + "primitives": ["wallet", "token"], + "visual": { + "kind": "AI agent king-of-the-hill competition", + "eyebrow": "base + wallet + token — agents as players", + "title": "EconomyOS agents competing for on-chain prize pools, not just running tasks", + "videoLabel": "Watch the 1:39 demo on YouTube", + "posterUrl": "https://legends.champz.world/img/ai_arena.png" + }, + "skills": [], + "artifacts": [ + { + "label": "Live spectator arena", + "href": "https://legends.champz.world/aiarena", + "kind": "demo" + }, + { + "label": "Python SDK + integration guide", + "href": "https://github.com/champz-world/legends-of-champz-game#readme", + "kind": "docs" + }, + { + "label": "EconomyOS agent test proof", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/legends-of-champz-arena/economyos-agent-proof.md", + "kind": "proof" + } + ], + "feedbackPrompts": [ + "Is there interest in a built-in EconomyOS capability that lets any agent discover and autonomously join competitive cycles like this, without custom SDK integration?", + "Would a dedicated EconomyOS-only cycle be a better first step to prove this out before pursuing deeper native integration?", + "What would make a game-style competitive primitive (agents as players, not just ACP buyers/sellers) compelling enough to explore alongside EconomyOS's commerce model?" + ] +} diff --git a/showcase/loxley/README.md b/showcase/loxley/README.md new file mode 100644 index 0000000..ccdd402 --- /dev/null +++ b/showcase/loxley/README.md @@ -0,0 +1,32 @@ +# LOXLEY — The Night Market, Graded + +LOXLEY is an autonomous night analyst for tokenized stocks (RWA) on +[Robinhood Chain](https://robinhoodchain.blockscout.com), launched on Virtuals. + +The official US market closes at 4:00pm New York. The stock tokens keep +trading all night. LOXLEY watches that night: + +- **Prices every real pool** on the chain, every ten minutes, dollar prints + only. Aggregates and fallback feeds never qualify as a print. +- **Tracks every wallet** that trades the night and grades each trade against + the next official open. The standings are public and anonymous; wallet + identification lives behind the holder gate. +- **Opens public case files** on unexplained gaps and closes them at the open, + verdict attached. "I don't know why yet" is an acceptable entry; an invented + cause never is. +- **Seals the record before the market can answer.** Every night's raw data is + hashed (SHA-256) and committed to a public ledger before 9:30 New York, so + no call can be edited after the fact. The misses stay up. + +## Proof + +- Live terminal: https://loxleyai.xyz/terminal +- Sealed ledger: https://github.com/loxley-ai/loxley-proofs +- Daily graded scorecards: https://x.com/loxley_ai +- Ship log (corrections are published, not buried): https://loxleyai.xyz/log + +## Token + +`$LOXLEY` launched on the Virtuals bonding curve on Robinhood Chain +(2026-07-20). Holders verify a wallet to enter the Quiver, the real-time +alert channel. The board, the ledger, and the grades are free for everyone. diff --git a/showcase/loxley/assets/poster.jpg b/showcase/loxley/assets/poster.jpg new file mode 100644 index 0000000..e2e19ea Binary files /dev/null and b/showcase/loxley/assets/poster.jpg differ diff --git a/showcase/loxley/showcase.json b/showcase/loxley/showcase.json new file mode 100644 index 0000000..9419d40 --- /dev/null +++ b/showcase/loxley/showcase.json @@ -0,0 +1,69 @@ +{ + "slug": "loxley", + "title": "LOXLEY — The Night Market, Graded", + "tagline": "An autonomous night desk for tokenized stocks on Robinhood Chain: every real pool priced around the clock, every night sealed before the open, every call graded after it.", + "description": "LOXLEY is an autonomous night analyst for tokenized stocks (RWA) on Robinhood Chain, launched on Virtuals. While the official US market is closed, it prices every real pool, tracks every wallet that trades the night, opens public case files on unexplained gaps, and writes short reads on what the tape means. Each night's raw record is hashed and sealed to a public GitHub ledger before 9:30 New York, then graded against the actual open, and the misses stay up. Holders of $LOXLEY verify a wallet to enter the Quiver, the real-time alert channel; the public terminal, the sealed ledger, and the graded scorecards are free.", + "status": "live", + "topic": "agents", + "topics": [ + "agents", + "rwa", + "tokenized-stocks", + "market-intelligence", + "robinhood-chain" + ], + "hidden": false, + "builder": { + "name": "LOXLEY", + "url": "https://loxleyai.xyz" + }, + "links": { + "repo": "https://github.com/loxley-ai/loxley-proofs", + "demo": "https://loxleyai.xyz/terminal", + "share": "https://x.com/loxley_ai", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20LOXLEY" + }, + "primitives": [ + "token", + "wallet" + ], + "visual": { + "kind": "autonomous analyst + sealed public ledger", + "eyebrow": "rwa · tokenized stocks · robinhood chain", + "title": "the night market, graded", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/loxley/assets/poster.jpg" + }, + "skills": [], + "artifacts": [ + { + "label": "Live terminal (public board, docket, standings)", + "href": "https://loxleyai.xyz/terminal", + "kind": "demo" + }, + { + "label": "Sealed nightly ledger with SHA-256 hashes", + "href": "https://github.com/loxley-ai/loxley-proofs", + "kind": "proof" + }, + { + "label": "Graded scorecards, posted daily", + "href": "https://x.com/loxley_ai", + "kind": "demo" + }, + { + "label": "$LOXLEY on the Virtuals bonding curve", + "href": "https://app.virtuals.io/virtuals/116622", + "kind": "proof" + }, + { + "label": "Ship log (public changelog, corrections included)", + "href": "https://loxleyai.xyz/log", + "kind": "docs" + } + ], + "feedbackPrompts": [ + "Which overnight signal would you act on first: premiums, graded wallets, or case files?", + "What would make the sealed nightly ledger easier for you to independently verify?", + "Which venue or market should the night desk learn to watch next?" + ] +} diff --git a/showcase/mon/README.md b/showcase/mon/README.md new file mode 100644 index 0000000..3bebb7f --- /dev/null +++ b/showcase/mon/README.md @@ -0,0 +1,171 @@ +# MON -- Website Reconstruction Engine + +MON is a modular Python framework for inspecting websites: crawling a +domain, extracting frontend structure, reverse-engineering backend API +calls from JavaScript, live-verifying those endpoints against the real +server, and exporting everything as a structured, documented, +machine-readable specification -- e.g. for feeding into an AI agent, or for +saving a local clone of the site. + +Network access (every `GET`/`POST` MON makes to the target site) uses a +deliberately simple, single-request-at-a-time fetcher: a plain +`requests.get`/`requests.post` per call, a mobile Chrome `User-Agent`, and +a bare timeout -- no shared session, no cookie jar, no automatic retries. +This is intentional: it's what makes MON reliable against sites that behave +oddly with persistent sessions or aggressive retry logic. + +## Install + +```bash +pip install -r requirements.txt +``` + +## Usage + +MON exposes exactly one public function. + +```python +from mon import inspect + +result = inspect( + domain="example.com", +) + +print(result.pages_crawled) +print(result.routes) +print(result.api_spec) +print(result.explorer) +print(result.explorer_visual) +``` + +### `inspect()` parameters + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `domain` | `str` | *required* | Target domain, with or without a scheme (`example.com` or `https://example.com` both work). | +| `action` | `str \| list[str]` | `"all_data"` | One action name or a list of them. See **Actions** below. | +| `profile` | `str` | `"balanced"` | `"fast"`, `"balanced"`, or `"deep"` -- controls crawl depth and whether API endpoints get live-verified. See **Profiles** below. | +| `max_page` | `int \| "all"` | `"all"` | Max number of pages to crawl. `"all"` defers to the profile's own limit. Pass an `int` to override it directly. | +| `output_dir` | `str \| Path` | `"./data"` | Base directory where output is written. | +| `project_name` | `str \| None` | `None` | Subfolder name under `output_dir`. Defaults to the domain name; if that folder already exists, MON appends `_2`, `_3`, etc. so nothing is overwritten. | +| `output_format` | `str` | `"json"` | Format of the final summary report: `"json"` or `"markdown"`. | +| `response` | `bool` | `True` | Whether to print live progress to the console (`[+] Launching...`, `[📥 FETCHING]`, `[✔ DONE]`, etc.). Set `False` for silent runs. | +| `timeout` | `int` | `10` | Per-request timeout, in seconds, for every fetch. | +| `save` | `bool` | `True` | Whether to write anything to disk at all. Set `False` to only get the in-memory `InspectResult` back. | +| `verify_live_apis` | `bool` | `True` | Whether to send a real request to each reconstructed API endpoint to confirm it responds. Only takes effect if the chosen `profile` also enables live verification (see below). | + +### Profiles + +| Profile | Max pages | Live-verifies APIs? | When to use it | +|---|---|---|---| +| `fast` | 15 | No | Quick look at a site's shape without hammering its API. | +| `balanced` | 100 | Yes | Default. Good mix of coverage and speed. | +| `deep` | 500 | Yes | Large sites, or when you need the most complete route/API map possible. | + +## Architecture + +``` +User -> inspect() -> SDK -> Inspector -> Resolver -> Dispatcher -> Analyzers -> Context -> Output Writer -> InspectResult +``` + +- **SDK** (`mon/sdk.py`) -- the only public entry point. Builds an + `InspectConfig`, calls the Inspector, returns an `InspectResult`. +- **Config** (`mon/config.py`) -- validates every argument to `inspect()` + once, up front, into one immutable `InspectConfig` object that gets + threaded through the whole run. +- **Inspector** (`mon/engine/inspector.py`) -- orchestrates the run. Knows + nothing about HTML/JS/APIs itself. +- **Resolver** (`mon/engine/resolver.py`) -- expands composite actions + (e.g. `api_spec`) into their leaf actions and topologically sorts + analyzers by declared dependencies, so `crawler` always runs before + `html`/`javascript`, which always run before `api`. +- **Registry** (`mon/engine/registry.py`) -- maps action names to analyzer + classes. Adding a new analyzer never requires touching the Dispatcher. +- **Dispatcher** (`mon/engine/dispatcher.py`) -- executes the resolved + pipeline against one shared `InspectContext`. If one analyzer fails, the + rest still run -- the failure is recorded as a warning, not a crash. +- **Context** (`mon/engine/context.py`) -- the only channel analyzers use to + communicate (crawled pages, discovered links, reconstructed endpoints, + routes...). No analyzer ever imports or calls another analyzer directly. +- **Events** (`mon/engine/events.py`) -- analyzers never call `print()` + directly; they emit events (page fetched, page skipped, analyzer + started/finished/failed), and `ProgressManager` + (`mon/engine/progress.py`) subscribes to turn those into the console log + you see when `response=True`. +- **Fetcher** (`mon/network/fetcher.py`) -- the actual network layer. One + `Fetcher` instance per run, shared by every analyzer that needs to talk + to the target site. +- **Analyzers** (`mon/analyzers/`) -- one class per file, one responsibility + each: `crawler`, `html`, `javascript`, `api`, `routes`, `assets`, + `explorer`. +- **Parsers** (`mon/parsers/`) -- the actual link-extraction and + JS-static-analysis logic the analyzers call into. +- **Output Writer** (`mon/engine/output_writer.py`) -- saves the crawled + pages to disk as a local clone, plus `api_spec.json`, `explorer.json`, + `explorer_visual.txt`, and the final summary report. +- **Exporters** (`mon/exporters/`) -- turn an `InspectResult` into the + final summary report, `json` or `markdown`. + +## Actions + +Leaf actions (each maps to exactly one analyzer): + +| Action | What it does | +|---|---| +| `crawler` | Breadth-first crawl of the domain. Fetches every same-domain page it can reach, starting from `/`. Everything else depends on this. | +| `html` | Extracts the `` from every crawled HTML page. | +| `javascript` | Statically analyzes every crawled `.js` file to reconstruct backend API calls (`fetch`/`apiCall` sites, guessed payload keys, response-reading keys). | +| `api` | Turns the raw JS findings into `Endpoint` objects, each with a confidence score, and -- if the profile allows it -- live-verifies each one against the real server. | +| `routes` | Builds the combined route map: every frontend page plus every reconstructed API endpoint. | +| `assets` | Tallies the static assets (CSS, images, fonts, etc.) picked up during the crawl. | +| `explorer` | Builds `explorer.json` and the ASCII `explorer_visual.txt` tree, combining frontend routes and the "simulated backend API" tree. | + +Composite actions (expand into a group of leaf actions): + +| Action | Expands to | +|---|---| +| `all_data` | Every leaf action. | +| `api_spec` | `crawler`, `html`, `javascript`, `api` -- just the API reconstruction, no route map or explorer. | +| `explorer_visual` | `crawler`, `html`, `routes`, `explorer` -- just the site map, no API work. | +| `cloning` | `crawler`, `html`, `assets` -- just pull down a local copy of the site. | + +## InspectResult + +What `inspect()` returns: + +| Field | Type | Description | +|---|---|---| +| `domain` | `str` | The domain that was inspected. | +| `actions_run` | `tuple[str, ...]` | Which leaf actions actually executed. | +| `pages_crawled` | `int` | Total pages successfully fetched. | +| `api_spec` | `dict` | `{endpoint_url: {...}}` -- method, purpose, guessed payload keys, response schema, confidence score/reasons, and (if live-verified) a real response sample. | +| `routes` | `list[Route]` | Every frontend page and backend endpoint discovered. | +| `explorer` | `dict` | The structured route tree, same shape as `explorer.json`. | +| `explorer_visual` | `str` | The ASCII tree view, same content as `explorer_visual.txt`. | +| `assets_saved` | `int` | Count of static assets found. | +| `warnings` | `list[str]` | Anything that went wrong along the way (a failed analyzer, a skipped page) without aborting the whole run. | + +## Confidence scoring + +Every reconstructed API endpoint in `api_spec` carries a `confidence.score` +(0-100) and `confidence.reasons` -- a list of exactly why MON believes the +endpoint is real (e.g. *"Discovered via static JS analysis"*, *"Payload +keys matched from FormData/JSON body"*, *"Live-verified against server"*). +Nothing is asserted without a reason. + +## Output on disk + +When `save=True` (the default), a run against `example.com` produces: + +``` +data/example.com/ + clone/ raw fetched pages, laid out like the live site + api_spec.json + explorer.json + explorer_visual.txt + example.com_full_report.json (or .md, if output_format="markdown") +``` + +## License + +MIT -- see `LICENSE`. diff --git a/showcase/mon/assets/.gitkeep b/showcase/mon/assets/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/showcase/mon/assets/.gitkeep @@ -0,0 +1 @@ + diff --git a/showcase/mon/assets/poster.jpg b/showcase/mon/assets/poster.jpg new file mode 100644 index 0000000..a22d3d8 Binary files /dev/null and b/showcase/mon/assets/poster.jpg differ diff --git a/showcase/mon/sample_output/api_spec.json b/showcase/mon/sample_output/api_spec.json new file mode 100644 index 0000000..6a38034 --- /dev/null +++ b/showcase/mon/sample_output/api_spec.json @@ -0,0 +1,1368 @@ +{ + "/assets/api/get_auth_token.php": { + "function_purpose": "res", + "method": "GET", + "expected_payload_format": "Multipart FormData (multipart/form-data)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "token", + "transactions", + "userDetails", + "dataPrices", + "message", + "status" + ], + "extracted_response_schema_from_js": { + "token": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const res = await fetch('../api/get_auth_token.php');", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html><head>\n<title>404 Not Found\n\n

Not Found

\n

The requested URL was not found" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/assets/api/user/auth/login.php": { + "function_purpose": "res", + "method": "POST", + "expected_payload_format": "Multipart FormData (multipart/form-data)", + "guessed_payload_keys": [ + "dataPrices", + "transactions", + "userDetails" + ], + "js_response_reading_keys": [ + "token", + "transactions", + "userDetails", + "dataPrices", + "message", + "status" + ], + "extracted_response_schema_from_js": { + "token": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const res = await fetch('../api/user/auth/login.php', {", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "\n\n404 Not Found\n\n

Not Found

\n

The requested URL was not found" + }, + "confidence": { + "score": 90, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Payload keys matched from FormData/JSON body", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/user.php": { + "function_purpose": "response", + "method": "POST", + "expected_payload_format": "Raw JSON (application/json)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "electricityData", + "userDetails", + "dataPrices", + "message", + "status", + "cableData" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "electricityData": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "cableData": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const response = await fetch('/api/user.php?token=' + encodeURIComponent(token), {", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "\n\n404 Not Found\n\n

Not Found

\n

The requested URL was not found" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/electric/providers.php": { + "function_purpose": "response", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "token", + "customer_name", + "message", + "status", + "new_balance", + "data", + "units" + ], + "extracted_response_schema_from_js": { + "token": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "customer_name": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "new_balance": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "units": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const response = await fetch('/api/electric/providers.php?token=' + encodeURIComponent(token), {", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required." + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/electric/verify.php": { + "function_purpose": "response", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [ + "meter_number", + "provider_id", + "token" + ], + "js_response_reading_keys": [ + "token", + "customer_name", + "message", + "status", + "new_balance", + "data", + "units" + ], + "extracted_response_schema_from_js": { + "token": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "customer_name": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "new_balance": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "units": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const response = await fetch('/api/electric/verify.php', {", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required." + }, + "confidence": { + "score": 90, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Payload keys matched from FormData/JSON body", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/electric/buy.php": { + "function_purpose": "response", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [ + "amount", + "meter_number", + "pin", + "provider_id", + "token" + ], + "js_response_reading_keys": [ + "token", + "customer_name", + "message", + "status", + "new_balance", + "data", + "units" + ], + "extracted_response_schema_from_js": { + "token": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "customer_name": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "new_balance": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "units": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const response = await fetch('/api/electric/buy.php', {", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required." + }, + "confidence": { + "score": 90, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Payload keys matched from FormData/JSON body", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/ai/chat.php": { + "function_purpose": "response", + "method": "POST", + "expected_payload_format": "Raw JSON (application/json)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "payment_link", + "message", + "action", + "pending_id", + "status", + "new_balance" + ], + "extracted_response_schema_from_js": { + "payment_link": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "action": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "pending_id": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "new_balance": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const response = await fetch('/api/ai/chat.php', {", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Message is required" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/assets/api/ai/confirm.php": { + "function_purpose": "response", + "method": "POST", + "expected_payload_format": "Raw JSON (application/json)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "payment_link", + "message", + "action", + "pending_id", + "status", + "new_balance" + ], + "extracted_response_schema_from_js": { + "payment_link": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "action": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "pending_id": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "new_balance": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const response = await fetch('../api/ai/confirm.php', {", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "\n\n404 Not Found\n\n

Not Found

\n

The requested URL was not found" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/${API_BASE_URL}/${endpoint}": { + "function_purpose": "response", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const response = await fetch(`${API_BASE_URL}/${endpoint}`, options);", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "\n\n404 Not Found\n\n

Not Found

\n

The requested URL was not found" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/data/buy.php": { + "function_purpose": "unknown_function", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "apiCall('data/buy.php', 'POST', { phone, plan_code: planCode, pin });", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required." + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/airtime/buy.php": { + "function_purpose": "unknown_function", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "apiCall('airtime/buy.php', 'POST', { phone, network, amount, pin });", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required." + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/user/user.php": { + "function_purpose": "result", + "method": "GET", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [ + "dataPrices", + "transactions", + "userDetails" + ], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const result = await apiCall('user/user.php', 'POST', { action: 'get' });", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required. Please login." + }, + "confidence": { + "score": 90, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Payload keys matched from FormData/JSON body", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/user/fund/initialize.php": { + "function_purpose": "initializePayment", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "window.initializePayment = async (amount) => apiCall('user/fund/initialize.php', 'POST', { amount });", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required." + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/user/fund/verify.php": { + "function_purpose": "verifyPayment", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "window.verifyPayment = async (reference) => apiCall('user/fund/verify.php', 'POST', { reference });", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required." + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/user/fund/pending.php": { + "function_purpose": "checkPendingFundingAPI", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "//window.checkPendingFundingAPI = async () => apiCall('user/fund/pending.php', 'POST', { action: 'check' });", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required." + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/user/update_profile": { + "function_purpose": "updateUserProfile", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "window.updateUserProfile = async (name, email) => apiCall('user/update_profile', 'POST', { name, email });", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "{\n \"status\": \"success\",\n \"message\": \"Profile updated successfully\",\n \"data\": {\n \"fullname\": \"Abba Moson\",\n \"email\": \"abba@moson.ng\"\n }\n}" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/user/change_password": { + "function_purpose": "changeUserPassword", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "window.changeUserPassword = async (currentPassword, newPassword) => apiCall('user/change_password', 'POST', { current_password: currentPassword, new_password: newPassword });", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "{\n \"status\": \"success\",\n \"message\": \"Password changed successfully\"\n}" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/user/set_pin/pin.php": { + "function_purpose": "setTransactionPin", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "window.setTransactionPin = async (pin) => apiCall('user/set_pin/pin.php', 'POST', { pin });", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required." + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/user/change_pin/pin.php": { + "function_purpose": "changeTransactionPin", + "method": "GET", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "window.changeTransactionPin = async (oldPin, newPin) => apiCall('user/change_pin/pin.php', 'POST', { old_pin: oldPin, new_pin: newPin });", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required." + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/ai/chat": { + "function_purpose": "sendChatMessage", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "window.sendChatMessage = async (message) => apiCall('ai/chat', 'POST', { message });", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "\n\n404 Not Found\n\n

Not Found

\n

The requested URL was not found" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/cable/providers": { + "function_purpose": "getCableProviders", + "method": "GET", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "window.getCableProviders = async () => apiCall('cable/providers', 'POST', {});", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "{\n \"status\": \"success\",\n \"data\": [\n { \"id\": 1, \"name\": \"DSTV\", \"code\": \"dstv\", \"plans\": [\n { \"id\": 101, \"name\": \"Compact\", \"price\": 7500, \"validity\": \"30 days\" },\n {" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/cable/subscribe": { + "function_purpose": "subscribeCable", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "window.subscribeCable = async (provider, smartCard, planCode) => apiCall('cable/subscribe', 'POST', { provider, smart_card: smartCard, plan_code: planCode });", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "\n\n404 Not Found\n\n

Not Found

\n

The requested URL was not found" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/electricity/providers": { + "function_purpose": "getElectricityProviders", + "method": "GET", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "window.getElectricityProviders = async () => apiCall('electricity/providers', 'POST', {});", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "\n\n404 Not Found\n\n

Not Found

\n

The requested URL was not found" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/electricity/pay": { + "function_purpose": "payElectricity", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "transactions", + "userDetails", + "dataPrices", + "message", + "status", + "data" + ], + "extracted_response_schema_from_js": { + "transactions": [ + { + "id": "TYPE_NUMBER", + "reference": "TYPE_STRING", + "amount": "TYPE_NUMBER", + "status": "TYPE_STRING" + } + ], + "userDetails": { + "id": "TYPE_NUMBER", + "name": "TYPE_STRING", + "email": "TYPE_STRING", + "token": "TYPE_STRING" + }, + "dataPrices": {}, + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "window.payElectricity = async (disco, meterNumber, meterType, amount) => apiCall('electricity/pay', 'POST', { disco, meter_number: meterNumber, meter_type: meterType, amount });", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "\n\n404 Not Found\n\n

Not Found

\n

The requested URL was not found" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/js/api/user/verify_kyc.php": { + "function_purpose": "response", + "method": "POST", + "expected_payload_format": "Raw JSON (application/json)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "data", + "status", + "message" + ], + "extracted_response_schema_from_js": { + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const response = await fetch('../api/user/verify_kyc.php', {", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "\n\n404 Not Found\n\n

Not Found

\n

The requested URL was not found" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/js/api/user/fund/pending.php": { + "function_purpose": "unknown_function", + "method": "POST", + "expected_payload_format": "Raw JSON (application/json)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "data", + "status", + "message" + ], + "extracted_response_schema_from_js": { + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "return await apiCall('user/fund/pending.php', 'POST', { action: 'check' });", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "\n\n404 Not Found\n\n

Not Found

\n

The requested URL was not found" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/cable/providers.php": { + "function_purpose": "response", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "data", + "new_balance", + "status", + "message" + ], + "extracted_response_schema_from_js": { + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "new_balance": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const response = await fetch('/api/cable/providers.php?token=' + encodeURIComponent(token), {", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required." + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/cable/packages.php": { + "function_purpose": "response", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "data", + "new_balance", + "status", + "message" + ], + "extracted_response_schema_from_js": { + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "new_balance": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const response = await fetch(`/api/cable/packages.php?provider_id=${providerId}&token=${encodeURIComponent(token)}`, {", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required." + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/cable/verify.php": { + "function_purpose": "response", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [ + "iuc_number", + "provider_id", + "token" + ], + "js_response_reading_keys": [ + "data", + "new_balance", + "status", + "message" + ], + "extracted_response_schema_from_js": { + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "new_balance": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const response = await fetch('/api/cable/verify.php', {", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required." + }, + "confidence": { + "score": 90, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Payload keys matched from FormData/JSON body", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/api/cable/buy.php": { + "function_purpose": "response", + "method": "POST", + "expected_payload_format": "Parameters (application/x-www-form-urlencoded)", + "guessed_payload_keys": [ + "iuc_number", + "package_id", + "pin", + "provider_id", + "token" + ], + "js_response_reading_keys": [ + "data", + "new_balance", + "status", + "message" + ], + "extracted_response_schema_from_js": { + "data": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "new_balance": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const response = await fetch('/api/cable/buy.php', {", + "live_verified": true, + "live_response_sample": { + "status": "error", + "message": "Authentication required." + }, + "confidence": { + "score": 90, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Payload keys matched from FormData/JSON body", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/js/api/developer/get_api_key.php": { + "function_purpose": "result", + "method": "GET", + "expected_payload_format": "Raw JSON (application/json)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "status", + "api_key", + "message" + ], + "extracted_response_schema_from_js": { + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "api_key": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const result = await apiCall('developer/get_api_key.php', 'POST', { pin });", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "\n\n404 Not Found\n\n

Not Found

\n

The requested URL was not found" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + }, + "/js/api/developer/regenerate_api_key.php": { + "function_purpose": "result", + "method": "POST", + "expected_payload_format": "Raw JSON (application/json)", + "guessed_payload_keys": [], + "js_response_reading_keys": [ + "status", + "api_key", + "message" + ], + "extracted_response_schema_from_js": { + "status": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "api_key": "TYPE_UNKNOWN_DETERMINED_BY_JS", + "message": "TYPE_UNKNOWN_DETERMINED_BY_JS" + }, + "raw_js_context": "const result = await apiCall('developer/regenerate_api_key.php', 'POST', { pin });", + "live_verified": true, + "live_response_sample": { + "raw_non_json_sample": "\n\n404 Not Found\n\n

Not Found

\n

The requested URL was not found" + }, + "confidence": { + "score": 75, + "reasons": [ + "Discovered via static JS analysis (apiCall/fetch call site)", + "Success/error branches detected in JS", + "Live-verified against server" + ] + } + } +} \ No newline at end of file diff --git a/showcase/mon/sample_output/explorer.json b/showcase/mon/sample_output/explorer.json new file mode 100644 index 0000000..a5f6894 --- /dev/null +++ b/showcase/mon/sample_output/explorer.json @@ -0,0 +1,405 @@ +{ + "domain": "payfluxai.com.ng", + "website_routes": [ + { + "path": "/", + "file": "index.html", + "type": "html", + "format": ".html", + "title": "PayFlux AI – Buy Data, Airtime & Pay Bills Instantly in Nigeria" + }, + { + "path": "/document/", + "file": "index.html", + "type": "html", + "format": ".html", + "title": "PayFlux AI API Documentation" + }, + { + "path": "/register/", + "file": "index.html", + "type": "html", + "format": ".html", + "title": "Create Account | PayFlux AI" + }, + { + "path": "/favicon.ico", + "file": "favicon.ico", + "type": "ico", + "format": ".ico" + }, + { + "path": "/login/", + "file": "index.html", + "type": "html", + "format": ".html", + "title": "Login | PayFlux AI" + }, + { + "path": "/document/", + "file": "index.html", + "type": "html", + "format": ".html", + "title": "PayFlux AI API Documentation" + }, + { + "path": "/dashboard/", + "file": "index.html", + "type": "html", + "format": ".html", + "title": "PayFlux-AI | User Dashboard" + }, + { + "path": "/assets/scripts/autoTheme.js", + "file": "autoTheme.js", + "type": "js", + "format": ".js" + }, + { + "path": "/dashboard/", + "file": "index.html", + "type": "html", + "format": ".html", + "title": "PayFlux-AI | User Dashboard" + }, + { + "path": "/forgot-password/", + "file": "index.html", + "type": "html", + "format": ".html", + "title": "Forgot Password | PayFlux AI" + }, + { + "path": "/assets/scripts/login.js", + "file": "login.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/theme.js", + "file": "theme.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/data.js", + "file": "data.js", + "type": "js", + "format": ".js" + }, + { + "path": "/forgot-password/", + "file": "index.html", + "type": "html", + "format": ".html", + "title": "Forgot Password | PayFlux AI" + }, + { + "path": "/js/modules/cookie.js", + "file": "cookie.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/electricity.js", + "file": "electricity.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/other.js", + "file": "other.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/sidebar.js", + "file": "sidebar.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/security.js", + "file": "security.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/auth.js", + "file": "auth.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/user.js", + "file": "user.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/transactions.js", + "file": "transactions.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/toast.js", + "file": "toast.js", + "type": "js", + "format": ".js" + }, + { + "path": "/assets/scripts/payfluxai.js", + "file": "payfluxai.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/airtime.js", + "file": "airtime.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/utils.js", + "file": "utils.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/mainapi.js", + "file": "mainapi.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/balance.js", + "file": "balance.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/main.js", + "file": "main.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/wallet.js", + "file": "wallet.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/cable.js", + "file": "cable.js", + "type": "js", + "format": ".js" + }, + { + "path": "/js/modules/developer.js", + "file": "developer.js", + "type": "js", + "format": ".js" + }, + { + "path": "/login/", + "file": "index.html", + "type": "html", + "format": ".html", + "title": "Login | PayFlux AI" + }, + { + "path": "/assets/api/get_auth_token.php", + "file": "get_auth_token.php", + "type": "json", + "format": ".php" + }, + { + "path": "/assets/api/user/auth/login.php", + "file": "login.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/user.php", + "file": "user.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/electric/providers.php", + "file": "providers.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/electric/verify.php", + "file": "verify.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/electric/buy.php", + "file": "buy.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/ai/chat.php", + "file": "chat.php", + "type": "json", + "format": ".php" + }, + { + "path": "/assets/api/ai/confirm.php", + "file": "confirm.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/${API_BASE_URL}/${endpoint}", + "file": "${endpoint}", + "type": "json", + "format": ".php" + }, + { + "path": "/api/data/buy.php", + "file": "buy.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/airtime/buy.php", + "file": "buy.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/user/user.php", + "file": "user.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/user/fund/initialize.php", + "file": "initialize.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/user/fund/verify.php", + "file": "verify.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/user/fund/pending.php", + "file": "pending.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/user/update_profile", + "file": "update_profile", + "type": "json", + "format": ".php" + }, + { + "path": "/api/user/change_password", + "file": "change_password", + "type": "json", + "format": ".php" + }, + { + "path": "/api/user/set_pin/pin.php", + "file": "pin.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/user/change_pin/pin.php", + "file": "pin.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/ai/chat", + "file": "chat", + "type": "json", + "format": ".php" + }, + { + "path": "/api/cable/providers", + "file": "providers", + "type": "json", + "format": ".php" + }, + { + "path": "/api/cable/subscribe", + "file": "subscribe", + "type": "json", + "format": ".php" + }, + { + "path": "/api/electricity/providers", + "file": "providers", + "type": "json", + "format": ".php" + }, + { + "path": "/api/electricity/pay", + "file": "pay", + "type": "json", + "format": ".php" + }, + { + "path": "/js/api/user/verify_kyc.php", + "file": "verify_kyc.php", + "type": "json", + "format": ".php" + }, + { + "path": "/js/api/user/fund/pending.php", + "file": "pending.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/cable/providers.php", + "file": "providers.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/cable/packages.php", + "file": "packages.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/cable/verify.php", + "file": "verify.php", + "type": "json", + "format": ".php" + }, + { + "path": "/api/cable/buy.php", + "file": "buy.php", + "type": "json", + "format": ".php" + }, + { + "path": "/js/api/developer/get_api_key.php", + "file": "get_api_key.php", + "type": "json", + "format": ".php" + }, + { + "path": "/js/api/developer/regenerate_api_key.php", + "file": "regenerate_api_key.php", + "type": "json", + "format": ".php" + } + ] +} \ No newline at end of file diff --git a/showcase/mon/sample_output/explorer_visual.txt b/showcase/mon/sample_output/explorer_visual.txt new file mode 100644 index 0000000..eb20662 --- /dev/null +++ b/showcase/mon/sample_output/explorer_visual.txt @@ -0,0 +1,68 @@ +📂 payfluxai.com.ng (Enterprise Frontend Map Layout) +├── / 📌 (PayFlux AI – Buy Data, Airtime & Pay Bills Instantly in Nigeria) +├── /assets/scripts/autoTheme.js +├── /assets/scripts/login.js +├── /assets/scripts/payfluxai.js +├── /dashboard/ 📌 (PayFlux-AI | User Dashboard) +├── /dashboard/ 📌 (PayFlux-AI | User Dashboard) +├── /document/ 📌 (PayFlux AI API Documentation) +├── /document/ 📌 (PayFlux AI API Documentation) +├── /favicon.ico +├── /forgot-password/ 📌 (Forgot Password | PayFlux AI) +├── /forgot-password/ 📌 (Forgot Password | PayFlux AI) +├── /js/main.js +├── /js/mainapi.js +├── /js/modules/airtime.js +├── /js/modules/auth.js +├── /js/modules/balance.js +├── /js/modules/cable.js +├── /js/modules/cookie.js +├── /js/modules/data.js +├── /js/modules/developer.js +├── /js/modules/electricity.js +├── /js/modules/other.js +├── /js/modules/security.js +├── /js/modules/sidebar.js +├── /js/modules/theme.js +├── /js/modules/toast.js +├── /js/modules/transactions.js +├── /js/modules/user.js +├── /js/modules/utils.js +├── /js/modules/wallet.js +├── /login/ 📌 (Login | PayFlux AI) +├── /login/ 📌 (Login | PayFlux AI) +└── /register/ 📌 (Create Account | PayFlux AI) + +⚙️ Simulated Backend API Endpoint Tree +├── /api/${API_BASE_URL}/${endpoint} +├── /api/ai/chat +├── /api/ai/chat.php +├── /api/airtime/buy.php +├── /api/cable/buy.php +├── /api/cable/packages.php +├── /api/cable/providers +├── /api/cable/providers.php +├── /api/cable/subscribe +├── /api/cable/verify.php +├── /api/data/buy.php +├── /api/electric/buy.php +├── /api/electric/providers.php +├── /api/electric/verify.php +├── /api/electricity/pay +├── /api/electricity/providers +├── /api/user.php +├── /api/user/change_password +├── /api/user/change_pin/pin.php +├── /api/user/fund/initialize.php +├── /api/user/fund/pending.php +├── /api/user/fund/verify.php +├── /api/user/set_pin/pin.php +├── /api/user/update_profile +├── /api/user/user.php +├── /assets/api/ai/confirm.php +├── /assets/api/get_auth_token.php +├── /assets/api/user/auth/login.php +├── /js/api/developer/get_api_key.php +├── /js/api/developer/regenerate_api_key.php +├── /js/api/user/fund/pending.php +└── /js/api/user/verify_kyc.php \ No newline at end of file diff --git a/showcase/mon/showcase.json b/showcase/mon/showcase.json new file mode 100644 index 0000000..b4444a8 --- /dev/null +++ b/showcase/mon/showcase.json @@ -0,0 +1,55 @@ +{ + "slug": "mon", + "title": "MON - Autonomous Source Code & Architecture Auditor", + "tagline": "Clones repositories, analyzes backend architectures, and extracts technical metadata for code auditing", + "description": "MON is an autonomous static analysis engine that clones source code to discover endpoints, APIs, forms, routes, and tech stacks. It utilizes custom parsers and exporters to build a comprehensive map of any application architecture.", + "status": "live", + "topic": "security", + "topics": [ + "code-audit", + "developer-tools", + "static-analysis", + "web3-security" + ], + "builder": { + "name": "Mamman Chiroma", + "url": "https://github.com/mosonmcn" + }, + "links": { + "repo": "https://github.com/mosonmcn/MON", + "video": "https://x.com/MosonDev/status/2078572007227093352", + "share": "https://x.com/MosonDev/status/2078572007227093352", + "feedback": "https://github.com/mosonmcn/MON/issues" + }, + "primitives": [ + "acp", + "wallet" + ], + "visual": { + "kind": "card", + "eyebrow": "Developer Tool", + "title": "MON: The Autonomous Code Cloner & Architecture Scanner", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/mon/assets/poster.jpg", + "videoLabel": "Watch the MON demo on X" + }, + "skills": [ + { + "name": "Code Architecture Discovery", + "href": "https://github.com/mosonmcn/MON", + "summary": "Runs deep static analysis to discover APIs, endpoints, and form schemas in any cloned Python or JS project.", + "install": "pip install -r requirements.txt && python -m mon.sdk" + } + ], + "artifacts": [ + { + "label": "MON Architecture Scan Sample Output", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/mon/sample_output", + "kind": "file" + } + ], + "feedbackPrompts": [ + "How easily does MON integrate with your current CI/CD pipelines?", + "Are there other specific web frameworks or languages you want the parsers to support?", + "How accurate is the tech-stack discovery on legacy codebases?" + ] +} diff --git a/showcase/monvera/README.md b/showcase/monvera/README.md new file mode 100644 index 0000000..3f41274 --- /dev/null +++ b/showcase/monvera/README.md @@ -0,0 +1,52 @@ +# Monvera - an accountable AI broker + +An AI broker for real tokenized stocks and funds on Robinhood Chain. You tell Vera, its +AI agent, a goal in plain words; she builds a diversified basket of real companies (Apple, +Nvidia, the S&P 500, US Treasuries), each with a one-line reason and a plain read on the +risk, and invests it in one tap. Gasless, non-custodial, and a single stock starts at a dollar. + +Live at [monvera.best](https://monvera.best) · Docs at [docs.monvera.best](https://docs.monvera.best) + +## What Virtuals / EconomyOS made possible + +- **ACP seller** - Vera is live on the ACP marketplace as [Vera by Monvera](https://app.virtuals.io/acp/agent/019f619c-6f1a-7768-b2c3-1b5f7b8a340d): seven analysis services any agent can hire, $0.03 to $0.15 per job, settled in USDG escrow on Robinhood Chain. Smoke-tested with real escrow: eight paid jobs, each funded and completed on-chain in under 30 seconds, and a ninth since. [Escrow payouts on Blockscout](https://robinhoodchain.blockscout.com/address/0xb87f5a74267ca3f9512b8511b32ccd804ea3707e?tab=token_transfers) - payouts are net of the ACP platform fee, so a $0.03 job settles 0.027 USDG. +- **Identity** - Vera's ERC-8004 identity (agent `58228` in the registry at `0x8004a169fb4a3325136eb29fa0ceb6d2e539a432` on Base) was registered **via Virtuals ACP** and is served from her agent card at [`/.well-known/agent-card.json`](https://monvera.best/.well-known/agent-card.json). +- **Agent Wallet** - the seller runs on Vera's ACP agent wallet `0xb87f5a74267ca3f9512b8511b32ccd804ea3707e`; every job settles to it in USDG escrow on Robinhood Chain. +- **Token** - [$MONVERA](https://app.virtuals.io/virtuals/105667) launched through Virtuals on Robinhood Chain. +- **Inference** - Virtuals-hosted inference builds every plan Vera proposes. + +## Why it belongs in the Showcase + +Most "AI investing" is a black box. Monvera makes the AI accountable: a verifiable agent +identity plus an append-only on-chain record of the risk assessment Vera signed for each plan +that is actually invested. Anyone can recover the signer of a recorded assessment and confirm +it against her identity. The signature binds the plan id, the assessed risk, the ceiling and +the expiry; the recommendation hash, the wallet and the spend figures recorded alongside it +are submitted by the app, not signed by Vera's key. Every trust claim resolves to something a +skeptic can open. + +## Proof + +- **Hire Vera on ACP:** https://app.virtuals.io/acp/agent/019f619c-6f1a-7768-b2c3-1b5f7b8a340d +- **ACP listing docs (offerings, prices, requirement shapes):** https://docs.monvera.best/dev/vera-on-virtuals-acp/ +- **Promo video (1:00):** https://x.com/monvera_best/status/2074487457505268213 +- **Live agent card (identity):** https://monvera.best/.well-known/agent-card.json +- **A signed plan recorded on-chain:** https://robinhoodchain.blockscout.com/tx/0x7ad119f916e1f6daff7d54429ea35ffe81c988730c534b176dcc2f9660cf45d6 +- **Redacted result report:** [`examples/result-redacted.md`](examples/result-redacted.md) +- **Reproducible verification recipe:** https://docs.monvera.best/dev/verify-vera/ + +## Reusable skill + +[`skills/accountable-onchain-agent`](skills/accountable-onchain-agent/SKILL.md) - the pattern +behind Monvera, generalized: give an AI agent an ERC-8004 identity, EIP-712 sign a structured +assessment of each output, and record the signature on-chain against the action it justifies, +so anyone can recover the signer and confirm it. See also [`soul.md`](soul.md) for +Vera's public context. + +## Primitives + +Agent Wallet, ACP offerings, and the $MONVERA token launch, all via Virtuals. + +## Builder + +[@Magicianafk](https://x.com/Magicianafk) · [github.com/Magicianhax/monvera](https://github.com/Magicianhax/monvera) · [@monvera_best](https://x.com/monvera_best) diff --git a/showcase/monvera/examples/result-redacted.md b/showcase/monvera/examples/result-redacted.md new file mode 100644 index 0000000..ced1fc9 --- /dev/null +++ b/showcase/monvera/examples/result-redacted.md @@ -0,0 +1,36 @@ +# Redacted result: an accountable plan, recorded and verified on-chain + +This report shows one real run of Monvera's accountable-AI flow on Robinhood Chain (chain +4663). Every value below is already public, either on-chain or on a public HTTP endpoint. No +secrets are included. + +## The run + +1. A user gave Vera a goal and an amount. +2. Vera built a diversified plan of real tokenized stocks and signed a `RiskInference` + assessment of it with her own agent key. +3. The trades settled, then the `VeraRecord` contract verified her signature and recorded the + plan over the legs that actually filled. + +## The record (public) + +- Chain: Robinhood Chain, id `4663` (explorer: https://robinhoodchain.blockscout.com) +- VeraRecord contract: `0x7ff1a5ee19330c165146488a7ad8af6cb41da1df` +- Plan id: `0xbef518779bc7cae74e60da8e111bc7c17423b70a721b7aaa2498893a138122a7` +- Record transaction (this run batched the record with the buys; Monvera now records as a follow-up transaction): https://robinhoodchain.blockscout.com/tx/0x7ad119f916e1f6daff7d54429ea35ffe81c988730c534b176dcc2f9660cf45d6 + +## Verify it yourself + +1. Read Vera's agent card: https://monvera.best/.well-known/agent-card.json (note + `agentSigner`, `identityRegistry`, and `agentId`). +2. Read `agentSigner()` live on the VeraRecord contract. +3. Pull the `RecommendationCommitted` log for the plan id above on Blockscout. +4. Recover the EIP-712 signer of the `RiskInference` payload and assert it equals + `agentSigner()`. + +Full recipe: https://docs.monvera.best/dev/verify-vera/ + +## Redaction + +No private keys, user account data, session-signer secrets, or provider API keys are +included. Every value above is already public on-chain or on a public HTTP endpoint. diff --git a/showcase/monvera/showcase.json b/showcase/monvera/showcase.json new file mode 100644 index 0000000..15dc560 --- /dev/null +++ b/showcase/monvera/showcase.json @@ -0,0 +1,110 @@ +{ + "slug": "monvera", + "title": "Monvera - an accountable AI broker", + "tagline": "Turns a plain-language goal into a diversified basket of real tokenized stocks and records a signed, verifiable risk assessment on-chain over what actually filled", + "description": "Monvera is an AI broker for real tokenized stocks and funds on Robinhood Chain. You tell Vera a goal in plain words and she builds a diversified basket of real companies, each with a reason and a plain read on the risk, then invests it in one tap: gasless, non-custodial, and a single stock starts at a dollar. Vera is a verifiable on-chain agent whose ERC-8004 identity is registered via Virtuals ACP; she signs each plan's risk assessment with her own key and records it on-chain, so anyone can read her agent card, recover the signer, and confirm it against her identity. She is also a live seller on the ACP marketplace: seven analysis services any agent can hire, from a 3 cent research note to a 15 cent allocation plan, settled in USDG escrow on Robinhood Chain.", + "status": "live", + "topic": "agents", + "topics": ["finance", "investing", "tokenized-stocks", "robinhood-chain", "identity", "accountability", "erc-8004", "acp"], + "hidden": false, + "builder": { + "name": "Magicianafk", + "url": "https://x.com/Magicianafk" + }, + "links": { + "repo": "https://github.com/Magicianhax/monvera", + "demo": "https://monvera.best/demo", + "video": "https://x.com/monvera_best/status/2074487457505268213", + "share": "https://x.com/monvera_best/status/2074487457505268213", + "feedback": "https://github.com/Magicianhax/monvera/issues" + }, + "primitives": ["wallet", "acp", "token"], + "visual": { + "kind": "x demo video", + "eyebrow": "ai broker - acp seller - robinhood chain", + "title": "meet vera", + "videoUrl": "https://assets.monvera.best/promo/monvera-1min.mp4", + "posterUrl": "https://monvera.best/opengraph-image.png", + "videoLabel": "Watch the 1:00 demo on X" + }, + "skills": [ + { + "name": "accountable-onchain-agent", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/monvera/skills/accountable-onchain-agent", + "sourcePath": "showcase/monvera/skills/accountable-onchain-agent", + "summary": "Make an AI agent's outputs verifiable: give it an ERC-8004 identity, EIP-712 sign a structured assessment of each output, and record the signature on-chain against the action it justifies, so anyone can recover the signer and confirm it against the agent's identity.", + "install": "cp -R showcase/monvera/skills/accountable-onchain-agent ~/.agents/skills/\ncp -R showcase/monvera/skills/accountable-onchain-agent ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Live app", + "href": "https://monvera.best", + "kind": "live" + }, + { + "label": "Docs", + "href": "https://docs.monvera.best", + "kind": "docs" + }, + { + "label": "Vera's public agent page", + "href": "https://monvera.best/agent", + "kind": "live" + }, + { + "label": "Vera's agent card (ERC-8004, via Virtuals ACP)", + "href": "https://monvera.best/.well-known/agent-card.json", + "kind": "proof" + }, + { + "label": "Hire Vera on ACP (7 analysis services)", + "href": "https://app.virtuals.io/acp/agent/019f619c-6f1a-7768-b2c3-1b5f7b8a340d", + "kind": "live" + }, + { + "label": "ACP listing docs (offerings, prices, requirements)", + "href": "https://docs.monvera.best/dev/vera-on-virtuals-acp/", + "kind": "docs" + }, + { + "label": "A signed plan recorded on-chain (Blockscout)", + "href": "https://robinhoodchain.blockscout.com/tx/0x7ad119f916e1f6daff7d54429ea35ffe81c988730c534b176dcc2f9660cf45d6", + "kind": "proof" + }, + { + "label": "Redacted result report (accountable run)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/monvera/examples/result-redacted.md", + "kind": "proof" + }, + { + "label": "Open strategies with walk-forward backtests", + "href": "https://monvera.best/api/strategies", + "kind": "proof" + }, + { + "label": "Verify Vera yourself (docs)", + "href": "https://docs.monvera.best/dev/verify-vera/", + "kind": "docs" + }, + { + "label": "Reusable skill: accountable-onchain-agent", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/monvera/skills/accountable-onchain-agent", + "kind": "skill" + }, + { + "label": "Promo video", + "href": "https://x.com/monvera_best/status/2074487457505268213", + "kind": "video" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/monvera/soul.md", + "summary": "Public context for Vera: her role building diversified baskets of real tokenized stocks, what she is allowed to do, her boundaries (non-custodial, signs assessments not spends, a risk ceiling she commits to in her own signature), and how to verify her." + }, + "feedbackPrompts": [ + "Which other agent outputs are worth recording on-chain for accountability?", + "What would make a verifiable agent identity easier to adopt for other builders?", + "Which markets or asset types should Vera support next?" + ] +} diff --git a/showcase/monvera/skills/accountable-onchain-agent/SKILL.md b/showcase/monvera/skills/accountable-onchain-agent/SKILL.md new file mode 100644 index 0000000..3c55d15 --- /dev/null +++ b/showcase/monvera/skills/accountable-onchain-agent/SKILL.md @@ -0,0 +1,86 @@ +--- +name: accountable-onchain-agent +description: Make an AI agent's outputs verifiable and tamper-evident by giving the agent an on-chain identity, having it EIP-712 sign a structured assessment of each output, and recording that signature on-chain against the action it justifies. Use when a skeptic should be able to confirm the agent authored a specific output and that the record cannot be edited afterward. +--- + +# Accountable On-Chain Agent + +## Overview + +This skill turns an AI agent's outputs into a verifiable, append-only on-chain record. The +agent gets a verifiable identity (an ERC-8004 registration, for example via Virtuals ACP), +signs a structured claim about each output with its own key (EIP-712), and that signature is +verified and written on-chain against the action it justifies. Anyone can later read the +agent's identity, recover the signer of a recorded claim, and confirm they match. It proves +authorship and integrity, not correctness. + +Monvera runs this pattern in production: Vera (agent #1) signs a `RiskInference` assessment of +each investment plan, and the `VeraRecord` contract verifies and records it immediately after +the buys settle, over the legs that actually filled. Batching the record into the action +transaction is the stronger shape; Monvera records as a follow-up so a failed record never +unwinds real buys. + +## When to use + +- You want an agent's recommendations, decisions, or assessments to be independently verifiable. +- You need a tamper-evident track record: "the agent said X at time T, and it cannot be edited." +- The agent's action already touches a chain (a trade, a payment, a mint), so recording is cheap to batch. + +## When not to use + +- The output needs no third-party verification (internal-only tooling). +- There is no on-chain action to batch the record with, and a standalone write is not worth it. +- You need to prove the recommendation is correct or profitable. This proves authorship and integrity, not outcome. + +## Required inputs, tools, credentials, preconditions + +- An agent signing key, kept server-side and never exposed. Its address is the agent's `agentSigner`. +- An ERC-8004 identity for the agent (an Identity Registry entry; Virtuals ACP can register one). Note the `agentId` and registry address. +- A verify-and-record contract on your target chain that recovers the EIP-712 signer, checks it equals the trusted `agentSigner`, enforces bounds, stores the record, and reverts if any check fails. Monvera's is `VeraRecord`. +- A minimal, meaningful EIP-712 typed struct to sign (the claim schema). +- An account-abstraction / batching path (for example ERC-4337) so the record can be batched with the action where the venue allows it. + +## Step-by-step workflow + +1. Define the claim. Choose the smallest struct that captures the agent's assessment. Monvera uses `RiskInference(bytes32 planId, uint16 assessedRisk, uint16 maxRisk, uint256 expiry)`. +2. Register the agent identity. Register the agent in an ERC-8004 Identity Registry (via Virtuals ACP or directly). Record `agentId` and the registry address, and publish an agent card at `/.well-known/agent-card.json`. +3. Sign server-side. When the agent produces an output, EIP-712-sign the claim with the agent signing key under a fixed domain `{name, version, chainId, verifyingContract}`. +4. Verify and record on-chain. Call the verify-and-record contract with the claim plus signature. It recovers the signer, asserts `recovered == agentSigner`, enforces expiry and single-use of the claim id, then stores the record. If any check fails the call reverts, so a claim can never be recorded with an invalid or unauthorized signature. Batching this call into the action transaction is the strongest shape; if you record as a follow-up instead, record only what actually settled and treat a failed record as non-fatal to the action. +5. Expose a read path. Provide an endpoint or a docs recipe so anyone can fetch a recorded claim, recover its signer, and compare it to `agentSigner()` and the agent's identity. + +## Approval gates + +- The user, not the agent, signs the value-moving step (the trade or payment). The agent's signature only attests to its own assessment. Keep the two signatures separate and never conflate them. +- Say which bounds are enforced on-chain and which are enforced before signing. A bound the agent derives itself is not a constraint on the agent: if a ceiling is meant to bind it, the value must come from the user or from config the agent cannot write. + +## Stop conditions + +- Stop if the recovered signer does not equal the expected `agentSigner` (the record would be rejected on-chain anyway). +- Stop if the claim is expired or exceeds its declared bounds. +- Stop if the identity registration cannot be confirmed on-chain. + +## Evidence and redaction rules + +- Evidence to keep (all public): the agent card URL, the on-chain record transaction (explorer link), the signed struct plus recovered signer, and the identity registry entry. +- Redact: the agent signing key, any user private keys, session-signer secrets, and provider API keys. Never include them in code, logs, or reports. + +## Validation checklist + +- [ ] The agent card resolves and lists the identity registry and `agentSigner`. +- [ ] A recorded claim's recovered signer equals `agentSigner()` read live on-chain. +- [ ] The verify-and-record contract reverts on a bad signer, an out-of-bounds claim, and an expired claim. +- [ ] The record is bound to the action - ideally one transaction; if not, it records only what actually settled. +- [ ] No secrets appear anywhere in the package. + +## Output contract + +- On-chain: one record event per output (for example `RecommendationCommitted(planId, user, recHash, riskScore, agentId)`). +- Only the fields inside the signed struct are attested by the agent. Any field the event emits that is not in the struct (a user address, a content hash, a spend amount) is attested by the caller. Either put it in the struct or do not present it as agent-attested. +- Off-chain: a read endpoint returning the recorded claim, its signature, the recovered signer, the `agentId`, and the identity registry, so a third party can reproduce the check. + +## Worked example (Monvera) + +- Identity: Vera is agent #1 in Monvera's Identity Registry `0x751ae640cfa816404b017fbb8234dd21abafbbdc` (chain 4663), canonical ERC-8004 identity `8453:58228` on Base via Virtuals ACP. Agent card: https://monvera.best/.well-known/agent-card.json +- Contract: `VeraRecord` at `0x7ff1a5ee19330c165146488a7ad8af6cb41da1df`, EIP-712 domain `{ name: "VeraRecord", version: "1", chainId: 4663 }`. +- A recorded plan on Blockscout: https://robinhoodchain.blockscout.com/tx/0x7ad119f916e1f6daff7d54429ea35ffe81c988730c534b176dcc2f9660cf45d6 +- Reproducible verification recipe: https://docs.monvera.best/dev/verify-vera/ diff --git a/showcase/monvera/soul.md b/showcase/monvera/soul.md new file mode 100644 index 0000000..25166ac --- /dev/null +++ b/showcase/monvera/soul.md @@ -0,0 +1,33 @@ +# Vera - public agent context + +Public context for how Vera works and what she is allowed to do. It contains no secrets. + +## Role + +Vera is Monvera's investing agent. She turns a person's plain-language goal and an amount +into a diversified basket of real tokenized stocks and funds, each with a one-line reason and +a plain read on the risk. She is agent #1 in Monvera's ERC-8004 Identity Registry, and her +canonical identity is registered on Base via Virtuals ACP. Other agents can hire her analysis +through her seller listing on the ACP marketplace: seven services, escrow-paid in USDG on +Robinhood Chain. + +## What she is allowed to do + +- Recommend allocations only from a fixed registry of 95 real tokenized stocks and funds that settle in USDG, and only from the subset that is tradable both ways right now. An hourly sweep locks any name she cannot also sell back, so she never leaves a user in a position with no exit. Live list: https://monvera.best/api/tradability +- Sign a risk assessment (EIP-712 `RiskInference`) for every plan she proposes, with her own key. +- Commit to a risk ceiling in her own signature. The contract rejects the record unless the signature is hers, the assessed risk is at or under the ceiling she signed, and the assessment has not expired. + +## Boundaries + +- She never moves money. The user signs the spend; Vera only signs the assessment. +- She does not custody funds. Accounts are non-custodial. +- She does not promise returns. Her signature proves authorship, not correctness. +- She is honest about risk inline, and about what is enforced on-chain versus not. +- ACP deliverables are analysis only. Execution fields are stripped from every response, so a buying agent receives a view, never a transaction to fire. +- She never exposes or requests private keys, seed phrases, or secrets. + +## How to verify her + +Read her agent card at https://monvera.best/.well-known/agent-card.json, pull a recorded plan, +recover the EIP-712 signer, and confirm it equals her on-chain `agentSigner`. Full recipe: +https://docs.monvera.best/dev/verify-vera/ diff --git a/showcase/my-chef-agent-cooker/README.md b/showcase/my-chef-agent-cooker/README.md new file mode 100644 index 0000000..fe66a76 --- /dev/null +++ b/showcase/my-chef-agent-cooker/README.md @@ -0,0 +1,55 @@ +# My Chef Agent Cooker — Recipe-to-Robot JSON Converter + +Convert real haute cuisine recipes into machine-executable JSON for culinary robots. + +Built by [Jean-Matthieu Frederic](https://github.com/jeanmatthieu58), a working chef with 20 years in Michelin-starred kitchens and Parisian palace hotels. The agent runs on an authentic professional database of 215 recipes (261 in the current deployment) — every temperature, gram, and technique comes from real service, not from generated text. + +**Live agent:** https://venice.ai/c/my-chef-agent-cooker + +## What this package contains + +``` +my-chef-agent-cooker/ +├── showcase.json # Showcase manifest +├── README.md # This file +├── assets/ +│ ├── demo.mp4 # 1:03 demo video (Safari screen recording) +│ ├── poster.jpg # Video poster (16:9) +│ ├── screenshot-agent.jpg # Proof — agent identity and question +│ ├── screenshot-recipe.jpg # Proof — real palace recipe with anti-hallucination chef note +│ └── compute-dashboard.jpg # Proof — active EconomyOS Spark inference credits grant +└── skills/ + └── recipe-to-robot-json/ + ├── SKILL.md # Reusable skill definition + └── examples/ + └── earl-grey-ice-cream/ + ├── prompt.md # The exact demo prompt + ├── result-redacted.md # Redacted conversion report + └── CG-005-cooker.json # Emitted machine-executable JSON +``` + +## The workflow + +1. **Ask the chef.** A user (or another agent) sends a recipe request to the live Venice character — free text or structured JSON. The agent answers from its curated professional database and refuses to invent recipes it does not have (see the anti-hallucination chef note in `screenshot-recipe.jpg`). +2. **Extract machine parameters.** The `recipe-to-robot-json` skill segments the procedure into steps and extracts temperatures, durations, quantities, and normalized actions. +3. **Emit robot profiles.** The skill outputs target-specific JSON drafts for three culinary robot families — cooker, grill, and dispensing (`"format": "cooker.recipe.v1-draft"`). Validated at scale: 215 recipes × 3 targets = 645 files in one run. + +## The proof package + +- **Demo video** (`assets/demo.mp4`, 1:03): a guest user asks the live agent for the Earl Grey ice cream (CG-005); the agent returns the real palace recipe and offers the robot-JSON adaptation. +- **Screenshots** (`assets/screenshot-*.jpg`): agent identity + the returned recipe with its chef note. +- **Compute credits proof** (`assets/compute-dashboard.jpg`): the agent's EconomyOS compute dashboard showing the active Spark inference credits grant (Developers Inference Credits program, Tier 1, week 2 of 4, repository-linked) that provisions the agent's conversion workloads. +- **End-to-end example** (`skills/recipe-to-robot-json/examples/earl-grey-ice-cream/`): the exact prompt, the redacted result report, and the emitted JSON file. + +## EconomyOS primitives used + +- **Agent identity + email:** `my_chef_agent_cooker@agents.world` (registered on ACP/EconomyOS) +- **Spark inference credits:** fund the LLM conversion workloads (endpoint `compute.virtuals.io/v1`) + +## Feedback + +Open an issue: https://github.com/jeanmatthieu58/spin-brigade/issues + +## Trademark notice + +Not affiliated with or endorsed by Posha, Aniai, or XRobotics; trademarks belong to their respective owners. Output formats are independent drafts designed to be mapped onto official vendor specs. diff --git a/showcase/my-chef-agent-cooker/assets/compute-dashboard.jpg b/showcase/my-chef-agent-cooker/assets/compute-dashboard.jpg new file mode 100644 index 0000000..7e83a77 Binary files /dev/null and b/showcase/my-chef-agent-cooker/assets/compute-dashboard.jpg differ diff --git a/showcase/my-chef-agent-cooker/assets/demo.mp4 b/showcase/my-chef-agent-cooker/assets/demo.mp4 new file mode 100644 index 0000000..54e7f28 Binary files /dev/null and b/showcase/my-chef-agent-cooker/assets/demo.mp4 differ diff --git a/showcase/my-chef-agent-cooker/assets/poster.jpg b/showcase/my-chef-agent-cooker/assets/poster.jpg new file mode 100644 index 0000000..b155638 Binary files /dev/null and b/showcase/my-chef-agent-cooker/assets/poster.jpg differ diff --git a/showcase/my-chef-agent-cooker/assets/screenshot-agent.jpg b/showcase/my-chef-agent-cooker/assets/screenshot-agent.jpg new file mode 100644 index 0000000..907d639 Binary files /dev/null and b/showcase/my-chef-agent-cooker/assets/screenshot-agent.jpg differ diff --git a/showcase/my-chef-agent-cooker/assets/screenshot-recipe.jpg b/showcase/my-chef-agent-cooker/assets/screenshot-recipe.jpg new file mode 100644 index 0000000..1155554 Binary files /dev/null and b/showcase/my-chef-agent-cooker/assets/screenshot-recipe.jpg differ diff --git a/showcase/my-chef-agent-cooker/showcase.json b/showcase/my-chef-agent-cooker/showcase.json new file mode 100644 index 0000000..397c19f --- /dev/null +++ b/showcase/my-chef-agent-cooker/showcase.json @@ -0,0 +1,86 @@ +{ + "slug": "my-chef-agent-cooker", + "title": "My Chef Agent Cooker — Recipe-to-Robot JSON Converter", + "tagline": "Convert 215 real haute cuisine recipes into machine-executable JSON for culinary robots such as Posha, Aniai, and XRobotics", + "description": "My Chef Agent Cooker is built on an authentic professional French haute cuisine database — 215 recipes developed over 20 years in Michelin-starred kitchens and Parisian palace hotels. This contribution packages its core B2B workflow as a reusable skill: parse a professional recipe (free text or structured JSON), extract machine parameters (temperatures, durations, actions), and emit target-specific JSON profiles for three culinary robot families. EconomyOS provides the agent's registered identity, agent email, and Venice-powered inference credits that fund the conversion workloads. Not affiliated with or endorsed by Posha, Aniai, or XRobotics; trademarks belong to their respective owners. Output formats are independent drafts designed to be mapped onto official vendor specs.", + "status": "validated demo", + "topic": "skills", + "topics": ["skills", "information", "commerce"], + "hidden": false, + "builder": { + "name": "Jean-Matthieu Frederic", + "url": "https://github.com/jeanmatthieu58" + }, + "links": { + "repo": "https://github.com/jeanmatthieu58/spin-brigade", + "demo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/my-chef-agent-cooker", + "video": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/my-chef-agent-cooker/assets/demo.mp4", + "share": "https://venice.ai/c/my-chef-agent-cooker", + "feedback": "https://github.com/jeanmatthieu58/spin-brigade/issues" + }, + "primitives": ["email"], + "visual": { + "kind": "demo video", + "eyebrow": "venice character + real chef database", + "title": "ask the chef, get the palace recipe", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/my-chef-agent-cooker/assets/poster.jpg", + "videoUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/my-chef-agent-cooker/assets/demo.mp4", + "videoLabel": "Watch the 1:03 demo" + }, + "skills": [ + { + "name": "recipe-to-robot-json", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json", + "sourcePath": "showcase/my-chef-agent-cooker/skills/recipe-to-robot-json", + "summary": "Converts professional recipes into machine-executable JSON for culinary robots: step segmentation, temperature/duration extraction, action mapping, and target-specific profiles for cooker, grill, and dispensing robot families.", + "install": "cp -R showcase/my-chef-agent-cooker/skills/recipe-to-robot-json ~/.agents/skills/" + } + ], + "artifacts": [ + { + "label": "Demo video (Safari screen recording, 1:03)", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/my-chef-agent-cooker/assets/demo.mp4", + "kind": "video" + }, + { + "label": "Screenshot — agent identity and question", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/my-chef-agent-cooker/assets/screenshot-agent.jpg", + "kind": "proof" + }, + { + "label": "Screenshot — real palace recipe with anti-hallucination chef note", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/my-chef-agent-cooker/assets/screenshot-recipe.jpg", + "kind": "proof" + }, + { + "label": "EconomyOS compute dashboard — active Spark inference credits grant (Tier 1, week 2 of 4)", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/my-chef-agent-cooker/assets/compute-dashboard.jpg", + "kind": "proof" + }, + { + "label": "Redacted result report", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/examples/earl-grey-ice-cream/result-redacted.md", + "kind": "proof" + }, + { + "label": "Demo prompt", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/examples/earl-grey-ice-cream/prompt.md", + "kind": "prompt" + }, + { + "label": "Skill source", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json", + "kind": "skill" + }, + { + "label": "Live agent on Venice", + "href": "https://venice.ai/c/my-chef-agent-cooker", + "kind": "demo" + } + ], + "feedbackPrompts": [ + "Which culinary robot vendor spec should the converter target next?", + "Should the skill support batch scaling (e.g. x4 professional batches) in the output profiles?", + "What would make this reusable for savory cuisine lines (grill stations, sauce bases)?" + ] +} diff --git a/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/SKILL.md b/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/SKILL.md new file mode 100644 index 0000000..89c0a5e --- /dev/null +++ b/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/SKILL.md @@ -0,0 +1,134 @@ +# recipe-to-robot-json + +Convert a professional culinary recipe into machine-executable JSON profiles +for culinary robot families: multi-step cookers (Posha-class), grill/plancha +robots (Aniai-class), and assembly/dispensing robots (XRobotics-class). + +Built and validated by a working chef against a database of 215 professional +French haute cuisine recipes (Michelin-starred and palace hotel kitchens), +running on Venice-powered inference funded by EconomyOS compute credits. + +> **Trademark notice.** Not affiliated with or endorsed by Posha, Aniai, or +> XRobotics. Trademarks belong to their respective owners. "Posha-class", +> "Aniai-class" and "XRobotics-class" describe machine categories +> (multi-step cooker, grill robot, dispensing robot). Output formats are +> independent drafts (`cooker.recipe.v1-draft`, `grill.recipe.v1-draft`, +> `dispenser.recipe.v1-draft`) designed to be mapped onto official vendor +> specs. + +## When to use + +- You have a recipe (free text or structured JSON) and need a machine-readable + execution profile: ordered steps with temperatures (°C), durations (min), + and normalized machine actions. +- You are prototyping recipe ingestion for a culinary robot, kitchen + automation pipeline, or professional recipe management system. +- You need the same recipe emitted for several robot targets at once. + +## When NOT to use + +- The robot vendor has published an official recipe schema — use their spec + directly; this skill emits a structured *draft* format (`*.recipe.v1-draft`) + designed to be mapped onto vendor specs, not to replace them. +- The recipe lacks quantified steps (no temperatures, no times, vague + instructions). Garbage in, garbage out — fix the recipe first. +- You need food-safety certification. Output profiles are execution drafts; + a professional must validate pasteurization/holding parameters before any + real production run. + +## Required inputs, tools, preconditions + +- Input recipe: either structured JSON (`{title, ingredients[{name,unit,qty}], + procedure}`) or plain text with numbered steps. +- Python 3.9+ for the reference converter (`references/convert_robots.py` + pattern), or an LLM agent following the workflow below. +- If using an agent: an OpenAI-compatible inference endpoint (the reference + build uses EconomyOS compute, `https://compute.virtuals.io/v1`). +- No credentials required. No network calls beyond inference. + +## Step-by-step workflow + +1. **Normalize the recipe.** Parse ingredients into `{name, unit, qty}` with + snake_case names and metric units (grams preferred; professional recipes + are weight-based). +2. **Segment the procedure into steps.** Split on numbered lines; if a single + block, split on sentence boundaries. Each step keeps its original + instruction text (traceability — the chef's wording is the ground truth). +3. **Extract machine parameters per step.** + - Temperatures: regex `(-?\d+(?:[.,]\d+)?)\s*°\s*C` → `temperatures_c[]` + - Durations: value + unit (h/min/s) normalized to minutes → `durations_min[]` +4. **Map actions.** Keyword-match each step onto a normalized action set: + `whisk, mix, heat, infuse, strain, cool, freeze, rest, shape`. A step can + carry several actions. Keep FR and EN keyword lists. +5. **Emit per-target profiles.** + - Cooker target (Posha-class): add `heating_profile {target_c, hold_min}` + and `pod_dispense` flags per step. + - Grill target (Aniai-class): surface temperature and contact-time fields. + - Dispensing target (XRobotics-class): ingredient dosing sequence keyed to + `ingredients[]` quantities. +6. **Write one JSON file per recipe per target** plus an `index.json` + manifest. Tag every file with the draft format version + (e.g. `"format": "cooker.recipe.v1-draft"`). + +## Approval gates + +- Before emitting output for a *new robot target*, confirm the field mapping + with a human (vendor logic differs: a grill has no churning action). +- Before publishing any converted recipe, confirm the recipe owner consents — + professional recipes are intellectual property. + +## Stop conditions + +- A step yields zero extracted parameters AND zero matched actions → flag the + step `"needs_review": true` and stop batch processing if more than 20% of + steps are flagged. +- Contradictory parameters (e.g. freeze action with +85°C target) → stop and + report; never guess thermal parameters. + +## Evidence and redaction rules + +- Publish only recipes you own or have explicit rights to. +- Redact supplier names, cost data, and client information if present in chef + notes. +- Include original-language instruction text in outputs for traceability; do + not paraphrase away professional nuance. + +## Validation checklist + +- [ ] Every step has `instruction`, `actions[]`, `temperatures_c[]`, `durations_min[]` +- [ ] All durations normalized to minutes; all temperatures in °C +- [ ] Actions only from the normalized vocabulary +- [ ] Format version tag present in every output file +- [ ] `index.json` lists every emitted recipe +- [ ] Flagged (`needs_review`) steps < 20% of total +- [ ] A domain professional has sanity-checked one sample per category + +## Output contract + +For each input recipe and each target, one JSON object: + +```json +{ + "recipe_id": "CG-005", + "title": {"fr": "...", "en": "..."}, + "category": "...", + "domain": "...", + "ingredients": [{"name": "lait_entier", "unit": "g", "qty": 620}], + "steps": [ + { + "step": 1, + "instruction_fr": "...", + "actions": ["infuse"], + "temperatures_c": [60.0], + "durations_min": [15.0], + "heating_profile": {"target_c": 60.0, "hold_min": 15.0} + } + ], + "procedure_en": "...", + "chef_notes": "...", + "format": "cooker.recipe.v1-draft" +} +``` + +Validated at scale: 215 recipes × 3 targets = 645 output files generated from +a single unified database in one run. diff --git a/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/examples/earl-grey-ice-cream/CG-005-cooker.json b/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/examples/earl-grey-ice-cream/CG-005-cooker.json new file mode 100644 index 0000000..aa1540e --- /dev/null +++ b/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/examples/earl-grey-ice-cream/CG-005-cooker.json @@ -0,0 +1,181 @@ +{ + "recipe_id": "CG-005", + "title": { + "fr": "Glace bergamote Earl Grey", + "en": "Earl Grey Bergamot Ice Cream" + }, + "category": "Crème glacée", + "domain": "glaces", + "ingredients": [ + { + "name": "lait_entier", + "unit": "g", + "qty": 620 + }, + { + "name": "creme_fleurette_35", + "unit": "g", + "qty": 200 + }, + { + "name": "beurre_motte", + "unit": "g", + "qty": 30 + }, + { + "name": "sucre_semoule", + "unit": "g", + "qty": 120 + }, + { + "name": "glucose_atomise", + "unit": "g", + "qty": 30 + }, + { + "name": "trimoline", + "unit": "g", + "qty": 20 + }, + { + "name": "cremodан", + "unit": "g", + "qty": 5 + }, + { + "name": "the_earl_grey", + "unit": "g", + "qty": 40 + } + ], + "steps": [ + { + "step": 1, + "instruction_fr": "1. Infusion Earl Grey à chaud — Chauffer le lait entier à 60°C. Incorporer les 40g de thé Earl Grey. Couvrir hermétiquement et infuser 15 minutes. Chinoiser très finement en pressant légèrement les feuilles — attention à ne pas trop presser pour éviter l'amertume.", + "actions": [ + "mix", + "heat", + "infuse", + "strain" + ], + "temperatures_c": [ + 60.0 + ], + "durations_min": [ + 15.0 + ], + "pod_dispense": true, + "heating_profile": { + "target_c": 60.0, + "hold_min": 15.0 + } + }, + { + "step": 2, + "instruction_fr": "2. Mélange du mix — Chauffer le lait infusé + crème à 40°C. Incorporer en pluie : sucre, glucose atomisé, crèmodan. Ajouter Trimoline et beurre motte.", + "actions": [ + "mix", + "heat", + "infuse" + ], + "temperatures_c": [ + 40.0 + ], + "durations_min": [], + "pod_dispense": true, + "heating_profile": { + "target_c": 40.0, + "hold_min": 0 + } + }, + { + "step": 3, + "instruction_fr": "3. Pasteurisation — Porter à 85°C, maintenir 30 secondes. Chinoiser finement.", + "actions": [ + "heat", + "strain" + ], + "temperatures_c": [ + 85.0 + ], + "durations_min": [ + 0.5 + ], + "pod_dispense": false, + "heating_profile": { + "target_c": 85.0, + "hold_min": 0.5 + } + }, + { + "step": 4, + "instruction_fr": "4. Refroidissement rapide — Descendre à 4°C rapidement.", + "actions": [ + "cool" + ], + "temperatures_c": [ + 4.0 + ], + "durations_min": [], + "pod_dispense": false, + "heating_profile": { + "target_c": 4.0, + "hold_min": 0 + } + }, + { + "step": 5, + "instruction_fr": "5. Maturation longue — 18 heures à 4°C filmé au contact — maturation prolongée pour fixer les arômes délicats de bergamote.", + "actions": [ + "rest" + ], + "temperatures_c": [ + 4.0 + ], + "durations_min": [ + 1080.0 + ], + "pod_dispense": false, + "heating_profile": { + "target_c": 4.0, + "hold_min": 1080.0 + } + }, + { + "step": 6, + "instruction_fr": "6. Turbinage — Turbiner jusqu'à -6°C. Arôme bergamote très délicat — ne pas surturbiner.", + "actions": [ + "freeze" + ], + "temperatures_c": [ + -6.0 + ], + "durations_min": [], + "pod_dispense": false, + "heating_profile": { + "target_c": -6.0, + "hold_min": 0 + } + }, + { + "step": 7, + "instruction_fr": "7. Surgélation — Surgeler à -35°C, stocker à -18°C.", + "actions": [ + "freeze" + ], + "temperatures_c": [ + -35.0, + -18.0 + ], + "durations_min": [], + "pod_dispense": false, + "heating_profile": { + "target_c": -18.0, + "hold_min": 0 + } + } + ], + "procedure_en": "1. Hot Earl Grey infusion — Heat the whole milk to 60°C. Add the 40g of Earl Grey tea. Cover tightly and infuse 15 minutes. Strain very finely, pressing the leaves lightly — do not press too hard or bitterness will develop.\n2. Mixing the base — Heat the infused milk + cream to 40°C. Rain in: sugar, atomized glucose, Cremodan. Add Trimoline and block butter.\n3. Pasteurization — Bring to 85°C, hold 30 seconds. Strain finely.\n4. Rapid cooling — Bring down to 4°C quickly.\n5. Extended maturation — 18 hours at 4°C filmed in contact — prolonged maturation to set the delicate bergamot aromas.\n6. Churning — Churn to -6°C. Very delicate bergamot aroma — do not over-churn.\n7. Deep-freezing — Blast-freeze at -35°C, store at -18°C.", + "chef_notes": "Recette signature palace. La bergamote est très volatile — ne jamais dépasser 60°C pour l'infusion. Chinoiser sans presser excessivement. Servir idéalement avec un financier pistache ou des madeleines.", + "format": "cooker.recipe.v1-draft" +} \ No newline at end of file diff --git a/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/examples/earl-grey-ice-cream/README.md b/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/examples/earl-grey-ice-cream/README.md new file mode 100644 index 0000000..16e9b10 --- /dev/null +++ b/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/examples/earl-grey-ice-cream/README.md @@ -0,0 +1,15 @@ +# Example: Earl Grey Bergamot Ice Cream (CG-005) + +A real palace hotel recipe converted to a cooker-robot execution profile. + +- `prompt.md` — the exact request sent to the agent +- `result-redacted.md` — the emitted cooker-class JSON profile with commentary +- `CG-005-cooker.json` — the actual emitted file + +Why this recipe is a good test case: it mixes delicate constraints (bergamot +infusion capped at 60°C — the aroma degrades above), a pasteurization gate +(85°C / 30s), a long hold (18h maturation at 4°C), and a churning step to +-6°C. A converter that survives this recipe handles most of pastry. + +The recipe is published with the owner's consent (the builder is the chef who +developed it). diff --git a/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/examples/earl-grey-ice-cream/prompt.md b/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/examples/earl-grey-ice-cream/prompt.md new file mode 100644 index 0000000..1f96dce --- /dev/null +++ b/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/examples/earl-grey-ice-cream/prompt.md @@ -0,0 +1,6 @@ +# Demo prompt + +> Convert recipe CG-005 (Earl Grey Bergamot Ice Cream) from my professional +> database into a cooker-robot execution profile. Extract temperatures, +> durations and machine actions per step, keep my original French +> instructions for traceability, and flag any step you cannot parameterize. diff --git a/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/examples/earl-grey-ice-cream/result-redacted.md b/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/examples/earl-grey-ice-cream/result-redacted.md new file mode 100644 index 0000000..b2ccc69 --- /dev/null +++ b/showcase/my-chef-agent-cooker/skills/recipe-to-robot-json/examples/earl-grey-ice-cream/result-redacted.md @@ -0,0 +1,33 @@ +# Result (redacted): CG-005 → cooker-class execution profile + +Full emitted file: [`CG-005-cooker.json`](./CG-005-cooker.json) + +## What the converter extracted + +| Step | Action(s) | Temp (°C) | Duration (min) | Note | +|------|-----------|-----------|----------------|------| +| 1 | infuse | 60 | 15 | Hot Earl Grey infusion — 60°C hard cap (bergamot degrades above) | +| 2 | mix, heat | 40 | — | Base mixing | +| 3 | heat, strain | 85 | 0.5 | Pasteurization gate: 85°C / 30 s | +| 4 | cool | 4 | — | Rapid cooling | +| 5 | rest | 4 | 1080 | 18 h maturation, filmed in contact | +| 6 | freeze | -6 | — | Churning — do not over-churn | +| 7 | freeze | -35 → -18 | — | Blast-freeze then storage | + +## Why this matters + +- 7/7 steps parameterized, 0 flagged `needs_review` +- The two safety-critical values (60°C infusion cap, 85°C/30s pasteurization) + were extracted from the chef's own wording, not inferred +- Original French instructions preserved per step for traceability +- Same source recipe also emitted for grill-class and dispensing-class + targets in the full run (215 recipes × 3 targets = 645 files) + +## Redaction + +The recipe is the builder's own intellectual property, published +intentionally. Supplier, cost, and client data do not appear in this dataset. +No credentials, keys, or private agent instructions are included. + +Not affiliated with or endorsed by Posha, Aniai, or XRobotics; trademarks +belong to their respective owners. diff --git a/showcase/nexmarkets/README.md b/showcase/nexmarkets/README.md new file mode 100644 index 0000000..d595e18 --- /dev/null +++ b/showcase/nexmarkets/README.md @@ -0,0 +1,55 @@ +# NexMarkets + +The NexMarkets Showcase package presents the newer NexMarkets application: an +open-source creator marketplace with Studio production for commissioned video +and infographic work. The maintained implementation lives in the +[NexMarkets source repository](https://github.com/Domistro16/NexID/tree/main/nexstudio); +this demos repository contains only the card manifest, review documentation, +workflow proof, and reusable production skill. + +## What the codebase does + +The workflow persists the production brief and authorised sources, derives a +server-side quote, waits for verified payment, creates the render workflow, +stores versioned delivery artifacts, and then records the buyer's review and +the settlement or refund outcome. The marketplace also supports listings, +direct hires, service requests, workrooms, delivery revisions, approval, and +dispute resolution. + +Wallet connection and the $NEX access flow are part of the project. The source +includes an explicit production configuration boundary: missing persistence, +provider, or chain configuration disables the affected workflow instead of +inventing a completed production. + +## Proof and scope + +Read [the source-backed workflow proof](examples/workflow-proof.md) for the +test result, review steps, and the distinction between verified source behavior +and an on-chain production receipt. This submission does not claim that a live +payment, render, settlement, or refund occurred. + +[SOURCE_SNAPSHOT.md](SOURCE_SNAPSHOT.md) records the external source boundary +used for review without duplicating the application in this repository. + +## Package contents + +- `showcase.json` is the card-ready Showcase manifest. +- `SOURCE_SNAPSHOT.md` links to the maintained NexMarkets source repository and + documents the validation boundary. +- `examples/workflow-proof.md` records the redacted validation evidence and + reviewer checks. +- `skills/nexmarkets-creator-production/SKILL.md` is the reusable, + approval-gated operator workflow for a NexMarkets production request. + +## Reuse + +Copy the project-specific skill into an agent's local skills directory: + +```bash +cp -R showcase/nexmarkets/skills/nexmarkets-creator-production ~/.agents/skills/ +``` + +The skill is deliberately conservative: it supports planning and operation of +an existing, configured NexMarkets deployment, but requires explicit human +approval before it creates a paid request, starts a render, publishes a +deliverable, settles funds, or changes a production record. diff --git a/showcase/nexmarkets/SOURCE_SNAPSHOT.md b/showcase/nexmarkets/SOURCE_SNAPSHOT.md new file mode 100644 index 0000000..6528c2c --- /dev/null +++ b/showcase/nexmarkets/SOURCE_SNAPSHOT.md @@ -0,0 +1,17 @@ +# Source review boundary + +The newer NexMarkets application, including its Studio implementation, is +maintained in the public +[NexID repository](https://github.com/Domistro16/NexID/tree/main/nexstudio). +The application source is intentionally linked rather than copied into this +Showcase package, keeping `acp-cli-demos` focused on the showcase manifest, +review evidence, and reusable skill. + +Reviewers can inspect the NexMarkets application, contracts, Prisma schema, rendering +integration, configuration template, and tests at that source link. + +The local validation recorded in +[`examples/workflow-proof.md`](examples/workflow-proof.md) is source-level +evidence only. It does not prove a live payment, provider render, settlement, +or refund. Operational credentials, local databases, uploads, generated +clients, caches, and build outputs are not part of this Showcase package. diff --git a/showcase/nexmarkets/assets/hero-card.png b/showcase/nexmarkets/assets/hero-card.png new file mode 100644 index 0000000..5428cec Binary files /dev/null and b/showcase/nexmarkets/assets/hero-card.png differ diff --git a/showcase/nexmarkets/assets/nexmarkets-demo.mp4 b/showcase/nexmarkets/assets/nexmarkets-demo.mp4 new file mode 100644 index 0000000..5b1de5d Binary files /dev/null and b/showcase/nexmarkets/assets/nexmarkets-demo.mp4 differ diff --git a/showcase/nexmarkets/examples/workflow-proof.md b/showcase/nexmarkets/examples/workflow-proof.md new file mode 100644 index 0000000..8a8db47 --- /dev/null +++ b/showcase/nexmarkets/examples/workflow-proof.md @@ -0,0 +1,53 @@ +# NexMarkets source-backed workflow proof + +## Evidence type + +This is a source-validation record for the maintained +[NexMarkets source repository](https://github.com/Domistro16/NexID/tree/main/nexstudio). +It is not a claim that an on-chain +payment, a live render, a settlement, or a refund has completed. No +credentials, wallet material, personal data, payment details, or private source +content are included here. + +## Local validation snapshot + +On 2026-07-22, the project test suite was run from the `nexstudio` directory: + +```text +Command: npm test +Result: 9 test files passed; 19 tests passed +Duration: 1.97s +``` + +The source repository's README documents the production workflow and its +failure-closed provider and chain boundaries. Reviewers can inspect the source +at the repository link above, then reproduce the test run with: + +```bash +git clone https://github.com/Domistro16/NexID.git +cd NexID/nexstudio +npm install +npm test +``` + +## Workflow represented + +1. A creator or buyer supplies an approved brief and authorised sources. +2. The server derives a quote and the client submits a payment transaction. +3. Production proceeds only after the payment is confirmed and required + provider, destination, and rendering configuration is available. +4. The resulting artifact is versioned for buyer review. +5. Review leads to a recorded approval, revision, settlement, refund request, + dispute, or resolution state as applicable. + +Marketplace work follows the same evidence-first approach: chain-dependent +state is applied only after the matching event reaches its configured +confirmation depth, and is recomputed if an indexed event is orphaned. + +## Reviewer checks + +- Inspect the linked NexMarkets source and its `README.md` workflow boundaries. +- Run `npm test` from the source directory; the result above is a reproducible + code-validation snapshot, not a substitute for a production receipt. +- Confirm this Showcase package contains no operational secrets or claims of + unobserved live transactions. diff --git a/showcase/nexmarkets/showcase.json b/showcase/nexmarkets/showcase.json new file mode 100644 index 0000000..1ae72e2 --- /dev/null +++ b/showcase/nexmarkets/showcase.json @@ -0,0 +1,83 @@ +{ + "slug": "nexmarkets", + "title": "NexMarkets", + "tagline": "Coordinates wallet-gated creator production from approved source and confirmed payment through review and settlement", + "description": "NexMarkets is an open-source creator marketplace and production studio for commissioned videos and infographics. Its workflow records authorised sources, server-derived quotes, verified payment confirmation, render jobs, versioned artifacts, buyer review, and settlement or refund decisions. The Showcase package is source-backed and explicitly distinguishes tested code from live production transactions.", + "status": "source-validated preview", + "topic": "commerce", + "topics": [ + "creator-economy", + "wallet", + "token", + "video-production", + "marketplace" + ], + "builder": { + "name": "Domistro", + "url": "https://github.com/Domistro16" + }, + "links": { + "repo": "https://github.com/Domistro16/NexID/tree/main/nexstudio", + "demo": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/nexmarkets/examples/workflow-proof.md", + "share": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/nexmarkets", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20NexMarkets&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20The%20creator-production%20workflow%20is%20clear%0A-%20The%20approval%20boundaries%20need%20more%20detail%0A-%20The%20source-backed%20proof%20needs%20another%20artifact%0A%0ANotes%3A%0A" + }, + "primitives": [ + "wallet", + "token" + ], + "visual": { + "kind": "creator marketplace", + "eyebrow": "wallet + token-gated production", + "title": "approved work, verified delivery", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/nexmarkets/assets/hero-card.png", + "videoUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/nexmarkets/assets/nexmarkets-demo.mp4", + "videoLabel": "Watch the NexMarkets Studio workflow" + }, + "skills": [ + { + "name": "nexmarkets-creator-production", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/nexmarkets/skills/nexmarkets-creator-production", + "sourcePath": "showcase/nexmarkets/skills/nexmarkets-creator-production", + "summary": "Operator workflow for running a NexMarkets creator-production request with explicit source, spending, render, delivery, and settlement approval gates.", + "install": "cp -R showcase/nexmarkets/skills/nexmarkets-creator-production ~/.agents/skills/\ncp -R showcase/nexmarkets/skills/nexmarkets-creator-production ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "NexMarkets source repository", + "href": "https://github.com/Domistro16/NexID/tree/main/nexstudio", + "kind": "source" + }, + { + "label": "Source-backed workflow proof", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/nexmarkets/examples/workflow-proof.md", + "kind": "proof" + }, + { + "label": "NexMarkets Showcase hero", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/nexmarkets/assets/hero-card.png", + "kind": "screenshot" + }, + { + "label": "NexMarkets Studio workflow video", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/nexmarkets/assets/nexmarkets-demo.mp4", + "kind": "video" + }, + { + "label": "NexMarkets creator-production skill", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/nexmarkets/skills/nexmarkets-creator-production", + "kind": "skill" + }, + { + "label": "Showcase package README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/nexmarkets/README.md", + "kind": "docs" + } + ], + "feedbackPrompts": [ + "Does the package make the creator-production lifecycle easy to understand?", + "Which approval gate should have more reviewer-visible evidence?", + "What additional public proof would make the source-validated preview easier to evaluate?" + ] +} diff --git a/showcase/nexmarkets/skills/nexmarkets-creator-production/SKILL.md b/showcase/nexmarkets/skills/nexmarkets-creator-production/SKILL.md new file mode 100644 index 0000000..d7f939f --- /dev/null +++ b/showcase/nexmarkets/skills/nexmarkets-creator-production/SKILL.md @@ -0,0 +1,92 @@ +--- +name: nexmarkets-creator-production +description: Plan and operate an existing NexMarkets creator-production request with source, payment, render, delivery, and settlement approval gates. +version: 0.1.0 +--- + +# NexMarkets Creator Production + +Use this skill for an existing NexMarkets deployment when an operator needs to +prepare, inspect, or advance a commissioned video or infographic request. It +supports the production and marketplace lifecycle without treating browser +state, a client claim, or a provider response as proof of payment or delivery. + +Do not use this skill to manufacture demo records, bypass a production gate, +or imply that a render, payment, settlement, or refund succeeded without the +corresponding persisted record and configured verification. + +## Inputs + +- Deployment base URL, or a local NexStudio checkout for source-level review. +- Production, listing, service-request, or workroom identifier when one + already exists. +- Approved production brief and authorised source locations. +- The requested deliverable type, destination, and review criteria. +- For any paid or externally visible action: the user's explicit approval, + amount or budget cap, and intended recipient or public destination. + +## Preconditions + +- The operator has access to the intended deployment and authenticated account. +- Persistence, chain, and required provider configuration are available for the + action being requested. +- Source material is authorised for the requested use. +- The operator knows whether this is a source-level review, local development + simulation, or a production action. A simulation is never evidence of a live + transaction or delivery. + +## Workflow + +1. Identify the request and its current persisted state. Do not assume a + listing, production, workroom, or payment record exists. +2. Confirm the brief, authorised sources, deliverable type, destination, and + review criteria with the request owner. +3. Obtain the server-derived quote and present the amount, asset, recipient, + and expected outcome before any payment is initiated. +4. Wait for the application's configured payment and chain verification before + starting a paid production or treating workroom funds as available. +5. Start a render or delivery workflow only when its required provider and + destination checks have passed. Preserve returned artifact identifiers and + links as evidence; never replace a failure with a synthetic success. +6. Present the versioned output for review. Record revision, approval, + cancellation, dispute, settlement, or refund actions only through the + authorized application workflow. +7. Return a concise status report with the persisted identifier, current + lifecycle state, validation evidence, next authorized action, and any block. + +## Approval gates + +Stop for explicit approval before any of the following: + +- Authorising or uploading source material not already approved for the job. +- Initiating a payment, funding reserve, on-chain transaction, refund, or + settlement. +- Starting a paid render or another provider action that incurs a charge. +- Sending a deliverable to Telegram, a customer, or any public destination. +- Publishing a listing, changing a service offer, resolving a dispute, or + deleting a production or workroom record. + +## Stop conditions + +- The requested record cannot be found or its state does not permit the action. +- A quote, payment confirmation, wallet signature, chain event, provider + configuration, or destination verification is missing. +- The requested payment amount, destination, or output scope differs from the + user's approval. +- Source authorisation, authentication, or security validation fails. +- The only available result is a local development simulation but the request + requires production proof. + +## Validation and output contract + +Before reporting completion, verify the persisted state and any required +payment, chain, provider, and delivery checks relevant to the action. Return: + +- request identifier and lifecycle state; +- approved scope and any spending or publishing authorization used; +- redacted evidence links or identifiers for payment, render, delivery, and + review where they actually exist; +- the next permitted action, or a precise blocking reason. + +Never return private source content, wallet material, session tokens, API keys, +payment credentials, OTPs, or unredacted personal data. diff --git a/showcase/openroboarena/README.md b/showcase/openroboarena/README.md new file mode 100644 index 0000000..f1f767c --- /dev/null +++ b/showcase/openroboarena/README.md @@ -0,0 +1,40 @@ +# OpenRoboArena + +OpenRoboArena is a live browser experience for discovering public robotics +repositories and using a safe 3D Motion Control Lab. Visit the live catalog at + or open the lab directly at +. + +Follow project updates on X: . + +## Demo video + +[Watch the short PUNCH movement demo](https://github.com/OpenRoboArena/openroboarena/blob/main/public/assets/openroboarena-punch-demo.mp4). It captures the live Motion Control Lab command terminal and the local MX-01 FBX animation. + +## EconomyOS primitives disclosure + +The OpenRoboArena EconomyOS agent has **wallet** and **email** primitives +provisioned. They describe the public agent identity only; the live Motion Lab +does not request credentials or invoke either primitive. The demonstrated +workflow is the bounded server-side **EconomyOS Compute** classification path, +which returns only a local animation label. + +## EconomyOS workflow + +When a visitor enters unsupported natural-language movement phrasing, the +server-side `/api/motion-plan` endpoint sends only the short command to +EconomyOS Compute. The model is constrained to a small local animation +allowlist. The browser then plays the matching local FBX animation. + +The workflow never executes submitted repository code, controls physical +hardware, requests wallet credentials, or initiates a payment or transaction. +The redacted live proof is in [proof/economyos-compute.md](./proof/economyos-compute.md). + +## Included package + +- `showcase.json` — card metadata and public links. +- `proof/` — reproducible redacted verification. +- `examples/` — a supported prompt and redacted output contract. +- `assets/poster.jpg` — 16:9 Motion Lab card poster. +- `skills/` — reusable Motion Control Lab workflow. +- `soul.md` — public agent context and safety boundaries. diff --git a/showcase/openroboarena/assets/poster.jpg b/showcase/openroboarena/assets/poster.jpg new file mode 100644 index 0000000..44e7875 Binary files /dev/null and b/showcase/openroboarena/assets/poster.jpg differ diff --git a/showcase/openroboarena/examples/prompt.md b/showcase/openroboarena/examples/prompt.md new file mode 100644 index 0000000..5a92d86 --- /dev/null +++ b/showcase/openroboarena/examples/prompt.md @@ -0,0 +1,17 @@ +# Supported Motion Lab prompt + +## Input + +Open the public Motion Control Lab and submit: + +```text +roundhouse kick +``` + +## Expected boundary + +- The lab must show `SYSTEM READY` before a command is submitted. +- The command resolves locally to the `roundhouse` animation label. +- The browser plays a local FBX clip only. +- No physical robot, repository code, wallet, email, transaction, or payment is + involved. diff --git a/showcase/openroboarena/examples/result-redacted.md b/showcase/openroboarena/examples/result-redacted.md new file mode 100644 index 0000000..0feb66e --- /dev/null +++ b/showcase/openroboarena/examples/result-redacted.md @@ -0,0 +1,16 @@ +# Redacted Motion Lab result + +```json +{ + "status": "completed", + "requested_command": "roundhouse kick", + "selected_motion": "roundhouse", + "execution_mode": "local", + "lab_url": "https://www.openroboarena.xyz/motion-control.html", + "evidence": "Visible terminal result: ROUNDHOUSE KICK", + "safety_boundary": "Local browser FBX animation only; no hardware, repository code, wallet, or payment action." +} +``` + +This is a public-safe example. It contains no API key, wallet material, account +record, repository payload, or private prompt. diff --git a/showcase/openroboarena/proof/economyos-compute.md b/showcase/openroboarena/proof/economyos-compute.md new file mode 100644 index 0000000..32a6f6a --- /dev/null +++ b/showcase/openroboarena/proof/economyos-compute.md @@ -0,0 +1,32 @@ +# EconomyOS Compute proof + +## Live workflow + +OpenRoboArena sends unsupported natural-language movement phrasing to a +server-side EconomyOS Compute classifier. The classifier is limited to this +local animation allowlist: `punch`, `cross`, `combo`, `kick`, `roundhouse`, +`reset`, and `unknown`. + +The selected label activates a local FBX animation. No repository is executed, +no physical hardware is controlled, and no blockchain transaction is requested. + +## Redacted verification + +- Live endpoint: +- Request method: `POST` +- Test input: `perform a whirling martial arts strike` +- Result: HTTP `200` +- Returned command: `roundhouse` +- Date: 2026-07-22 + +The Compute API key is stored only as the Vercel Production environment variable +`VIRTUALS_API_KEY`. It is not included in source control, browser code, or this +proof file. + +## Primitives disclosure + +The OpenRoboArena agent has EconomyOS `wallet` and `email` primitives +provisioned for its public identity. This proof and the demonstrated Motion Lab +workflow do not invoke either primitive, do not request credentials, and do not +make a payment or transaction. The only live service used here is EconomyOS +Compute, which returns a bounded local animation label. diff --git a/showcase/openroboarena/showcase.json b/showcase/openroboarena/showcase.json new file mode 100644 index 0000000..82f1794 --- /dev/null +++ b/showcase/openroboarena/showcase.json @@ -0,0 +1,89 @@ +{ + "slug": "openroboarena", + "title": "OpenRoboArena", + "tagline": "Turns public robotics repositories into a discoverable catalog with a bounded, prompt-driven Motion Control Lab", + "description": "OpenRoboArena is a live browser experience for discovering public robotics repositories and interacting with a local 3D Motion Control Lab. Supported commands play deterministic local FBX animations; unsupported short movement phrasing can be classified by EconomyOS Compute into the same constrained allowlist. The browser never executes a submitted repository, controls physical hardware, or initiates a blockchain transaction.", + "status": "live EconomyOS-powered prototype", + "topic": "agents", + "topics": ["robotics", "threejs", "open-source", "motion-control", "interactive-demo"], + "builder": { + "name": "OpenRoboArena", + "url": "https://www.openroboarena.xyz" + }, + "links": { + "repo": "https://github.com/OpenRoboArena/openroboarena", + "share": "https://x.com/OpenRoboArena", + "feedback": "https://github.com/OpenRoboArena/openroboarena/issues/new?title=OpenRoboArena%20feedback", + "demo": "https://www.openroboarena.xyz/motion-control.html", + "video": "https://raw.githubusercontent.com/OpenRoboArena/openroboarena/main/public/assets/openroboarena-punch-demo.mp4" + }, + "primitives": ["wallet", "email"], + "visual": { + "kind": "interactive 3D robotics motion lab", + "eyebrow": "open-source robotics + economyos compute", + "title": "give a robot a movement command", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/openroboarena/assets/poster.jpg", + "videoUrl": "https://raw.githubusercontent.com/OpenRoboArena/openroboarena/main/public/assets/openroboarena-punch-demo.mp4", + "videoLabel": "Watch the 0:03 demo" + }, + "skills": [ + { + "name": "openroboarena-motion-control", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/openroboarena/skills/openroboarena-motion-control", + "sourcePath": "showcase/openroboarena/skills/openroboarena-motion-control", + "summary": "Run the public OpenRoboArena Motion Control Lab with supported plain-language commands and verify its local animation safety boundaries.", + "install": "cp -R showcase/openroboarena/skills/openroboarena-motion-control ~/.agents/skills/" + } + ], + "artifacts": [ + { + "label": "Live OpenRoboArena catalog", + "href": "https://www.openroboarena.xyz", + "kind": "demo" + }, + { + "label": "Live Motion Control Lab", + "href": "https://www.openroboarena.xyz/motion-control.html", + "kind": "demo" + }, + { + "label": "PUNCH movement demo video", + "href": "https://raw.githubusercontent.com/OpenRoboArena/openroboarena/main/public/assets/openroboarena-punch-demo.mp4", + "kind": "video" + }, + { + "label": "Supported Motion Lab prompt example", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/openroboarena/examples/prompt.md", + "kind": "docs" + }, + { + "label": "Redacted Motion Lab result example", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/openroboarena/examples/result-redacted.md", + "kind": "proof" + }, + { + "label": "EconomyOS Compute redacted verification", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/openroboarena/proof/economyos-compute.md", + "kind": "proof" + }, + { + "label": "OpenRoboArena source repository", + "href": "https://github.com/OpenRoboArena/openroboarena", + "kind": "docs" + }, + { + "label": "OpenRoboArena EconomyOS agent", + "href": "https://app.virtuals.io/acp/agents/019f8a5d-d300-7923-a606-c945edf12f93", + "kind": "demo" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/openroboarena/soul.md", + "summary": "Public OpenRoboArena agent context covering its robotics discovery and motion-control role, permitted inputs, and safety boundaries." + }, + "feedbackPrompts": [ + "Which open-source robotics project should OpenRoboArena profile next?", + "What movement commands would make the Motion Control Lab most useful for a first-time visitor?", + "What public proof would make the EconomyOS workflow easiest to evaluate?" + ] +} diff --git a/showcase/openroboarena/skills/openroboarena-motion-control/SKILL.md b/showcase/openroboarena/skills/openroboarena-motion-control/SKILL.md new file mode 100644 index 0000000..17217a4 --- /dev/null +++ b/showcase/openroboarena/skills/openroboarena-motion-control/SKILL.md @@ -0,0 +1,135 @@ +--- +name: openroboarena-motion-control +description: Run or verify a bounded OpenRoboArena browser Motion Control Lab command and return the selected local animation without controlling hardware, code, wallets, or payments. +version: 1.0.0 +--- + +# OpenRoboArena Motion Control Lab + +Use the public OpenRoboArena Motion Control Lab to demonstrate a small, +browser-rendered robot movement or to verify that its command boundary remains +safe. This skill maps language to **local FBX animation playback only**; it is +not a physical-robot control interface. + +## When to use this skill + +- A user wants to see one of the supported MX-01 motions in the public Motion + Control Lab. +- A reviewer needs a repeatable check that a movement command resolves to a + bounded local animation. +- A caller wants to document the distinction between deterministic local + commands and the optional EconomyOS Compute classification path. + +## When NOT to use this skill + +- To control a physical robot, drone, actuator, or any real-world device. +- To run, inspect, alter, or sandbox a submitted GitHub repository. +- To request wallet credentials, send a transaction, or make a payment. +- To interpret arbitrary commands as an authorization to perform an external + action. The only permitted result is a local animation label. + +## Inputs + +| Input | Required | Description | +| --- | --- | --- | +| `command` | yes | A supported movement phrase, or a short natural-language movement request. | +| `lab_url` | no | Defaults to `https://www.openroboarena.xyz/motion-control.html`. | +| `allow_compute_classification` | no | Defaults to `false`. When `true`, the caller explicitly permits the live lab to use its server-side Compute classifier for unsupported phrasing. | + +Supported local mappings: + +| User wording | Local result | +| --- | --- | +| `punch`, `jab`, `strike` | `punch` | +| `cross punch` | `cross` | +| `punch combo`, `combo` | `combo` | +| `kick` | `kick` | +| `roundhouse kick`, `spin kick` | `roundhouse` | +| `reset`, `center`, `idle` | `reset` | + +## Tools, credentials, and preconditions + +- A browser capable of opening the public lab URL and observing the visible + command result. +- Wait until the page shows `SYSTEM READY` and the MX-01 assets finish loading. +- No wallet, email, GitHub credential, or API key is accepted or needed by this + skill. +- The optional Compute path is configured server-side by OpenRoboArena. Its + `VIRTUALS_API_KEY` remains in Vercel Production only and must never be + requested, displayed, logged, or committed. +- The public agent has EconomyOS wallet and email primitives provisioned, but + this Motion Lab workflow does **not** invoke either primitive. + +## Approval gates + +- Supported local commands only play a browser animation and require no spend + or transaction approval. +- This skill must not spend funds, post content, create accounts, deploy code, + mutate production configuration, submit repositories, or initiate a wallet + action. +- Keep `allow_compute_classification` false unless the caller explicitly + approves it. The server-side classifier can consume the project's Compute + allocation; it still returns only a bounded animation label. +- Any request that would cross one of the prohibited boundaries is out of scope + and must be handed back to the caller for separate, explicit authorization. + +## Procedure + +1. Open `lab_url` and confirm `SYSTEM READY` is visible. +2. Normalize `command` against the supported local table. +3. If a local mapping exists, submit it and observe the selected movement in + the command terminal. +4. If no local mapping exists and `allow_compute_classification` is `false`, do + not submit it to Compute. Return `needs_approval` with the supported list. +5. If the caller approved classification, submit the short movement request in + the live lab. Accept only an allowlisted result: `punch`, `cross`, `combo`, + `kick`, `roundhouse`, `reset`, or `unknown`. +6. Report the visible selected label and whether it was local or + EconomyOS-classified. Never claim physical execution. + +## Stop conditions and handoff + +- Stop with `needs_review` if `SYSTEM READY` does not appear, assets fail to + load, or the visible command result is missing. +- Stop with `needs_approval` when an unsupported request would need Compute and + `allow_compute_classification` is not explicitly true. +- Stop with `out_of_scope` for physical control, source-code execution, + credentials, spending, posting, deployment, or production mutations. +- Handoff infrastructure failures to the OpenRoboArena maintainer with the lab + URL, requested command, timestamp, and redacted visible error only. + +## Validation checks + +- [ ] The public lab loaded and `SYSTEM READY` was visible. +- [ ] The requested output is one of the seven allowlisted labels. +- [ ] The command terminal visibly reports the selected local movement or + `unknown`. +- [ ] No API key, wallet material, account data, repository content, or private + prompt appears in the result. +- [ ] The result is described as browser animation playback, not physical robot + control. + +## Output contract + +Return only this redacted shape: + +```json +{ + "status": "completed", + "requested_command": "roundhouse kick", + "selected_motion": "roundhouse", + "execution_mode": "local", + "lab_url": "https://www.openroboarena.xyz/motion-control.html", + "evidence": "Visible terminal result: ROUNDHOUSE KICK", + "safety_boundary": "Local browser FBX animation only; no hardware, repository code, wallet, or payment action." +} +``` + +`status` is one of `completed`, `needs_approval`, `needs_review`, or +`out_of_scope`. On a non-completed result, set `selected_motion` to `null` and +state the redacted reason in `evidence`. + +## Public examples + +- [Supported-command prompt](../../examples/prompt.md) +- [Redacted result](../../examples/result-redacted.md) diff --git a/showcase/openroboarena/soul.md b/showcase/openroboarena/soul.md new file mode 100644 index 0000000..e518a4c --- /dev/null +++ b/showcase/openroboarena/soul.md @@ -0,0 +1,16 @@ +# OpenRoboArena public agent context + +## Role + +OpenRoboArena helps visitors discover public robotics repositories and use a +safe motion-control demonstration. It explains available movement commands, +links to original public projects, and distinguishes browser visualization from +real-world robot control. + +## Boundaries + +- Do not execute, clone, or modify submitted repository code. +- Do not claim control of a physical robot. +- Do not request or handle seed phrases, private keys, or wallet credentials. +- Do not initiate payments, token transfers, or blockchain transactions. +- Keep EconomyOS output limited to the published local animation allowlist. diff --git a/showcase/palisade/README.md b/showcase/palisade/README.md new file mode 100644 index 0000000..f79bd9f --- /dev/null +++ b/showcase/palisade/README.md @@ -0,0 +1,58 @@ +# PaliSade + +Keyless, read-only onchain security scanner for **Robinhood Chain** (primary), +exposed as an **MCP server** any agent can call over HTTP before it signs an +approval, swaps, or invests. EVM majors (Ethereum, Polygon, Arbitrum) and +Solana are also supported for cross-chain checks. + +- **Live API:** https://mcp.palisadescan.com +- **Website:** https://palisadescan.com +- **Source:** https://github.com/palisadescan/palisade + +## What it does + +Eighteen read-only inspectors interrogate a token or wallet, then a consensus +pass weighs six independent signals so one noisy source can't escalate risk on +its own. + +| # | Tool | What it checks | +| --- | --- | --- | +| 1 | `palisade_scan_approvals` | ERC-20/721 allowances, flags unlimited spenders | +| 2 | `palisade_scan_token` | Rugpull indicators: hidden mint, proxy, tax, blacklist | +| 3 | `palisade_detect_honeypot` | Simulated buy/sell to catch tokens you can't exit | +| 4 | `palisade_safety_score` | 0-100 composite: code, ownership, liquidity, holders | +| 5 | `palisade_wallet_report` | Full wallet security posture | +| 6 | `palisade_monitor_wallet` | Alerts on new approvals, risky interactions | +| 7 | `palisade_token_market` | Price, liquidity, volume, pool age (DexScreener) | +| 8 | `palisade_deployer_check` | Deployer history + verification (Blockscout) | +| 9 | `palisade_batch_scan` | Score many tokens in one call, ranked by risk | +| 10 | `palisade_check_scam` | Community scam-report database | +| 11 | `palisade_sentinel_status` | Autonomous Sentinel watchlist + loop config | +| 12 | `palisade_consensus` | Six-source weighted verdict (false-positive guard) | +| 13 | `palisade_liquidity_lock` | Locked / burned / unlocked / unknown | +| 14 | `palisade_simulate_approval` | Simulate an approval before signing | +| 15 | `palisade_detect_clone` | Bytecode fingerprint vs scam DB | +| 16 | `palisade_check_tax` | Buy/sell tax mechanics | +| 17 | `palisade_check_ownership` | Ownership renounced / retained | +| 18 | `palisade_holder_concentration` | Whale grip on sellable float | + +## Boundaries + +- **Read-only by design.** Never signs, holds keys, or moves funds. +- **Keyless and free.** No API key, no account, no payment. +- **Unknown over safe.** Missing data resolves to `unknown`, never `safe`. + +## Quick call + +```bash +curl -s -X POST https://mcp.palisadescan.com/tools/call \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","id":1,"method":"tools/call", + "params":{"name":"palisade_safety_score", + "arguments":{"contract":"0xTokenAddressHere","chain":"robinhood"}}}' +``` + +See [`examples/live-endpoint-proof.md`](examples/live-endpoint-proof.md) for +real, reproducible responses, and +[`skills/palisade-security-scan`](skills/palisade-security-scan) for the +reusable scan skill. diff --git a/showcase/palisade/assets/poster.png b/showcase/palisade/assets/poster.png new file mode 100644 index 0000000..85c1ad0 Binary files /dev/null and b/showcase/palisade/assets/poster.png differ diff --git a/showcase/palisade/examples/live-endpoint-proof.md b/showcase/palisade/examples/live-endpoint-proof.md new file mode 100644 index 0000000..f4402e2 --- /dev/null +++ b/showcase/palisade/examples/live-endpoint-proof.md @@ -0,0 +1,158 @@ +# Live Endpoint Proof + +All responses below are **real, reproducible** calls against the public PaliSade +MCP API at `https://mcp.palisadescan.com`. No API key, no account, no payment — +every call is keyless and read-only. Captured 2026-07-29. + +Reproduce any call by pasting the `curl` block into a terminal. + +--- + +## 1. Service health + +```bash +curl -s https://mcp.palisadescan.com/health +``` + +```json +{"status":"ok","service":"palisade-mcp","tools":18} +``` + +--- + +## 2. Tool inventory (18 read-only tools) + +```bash +curl -s https://mcp.palisadescan.com/tools/list \ + | python3 -c "import sys,json;t=json.load(sys.stdin)['result']['tools'];print(len(t));[print(x['name']) for x in t]" +``` + +``` +18 +palisade_scan_approvals +palisade_scan_token +palisade_detect_honeypot +palisade_safety_score +palisade_wallet_report +palisade_monitor_wallet +palisade_token_market +palisade_deployer_check +palisade_batch_scan +palisade_check_scam +palisade_sentinel_status +palisade_consensus +palisade_liquidity_lock +palisade_simulate_approval +palisade_detect_clone +palisade_check_tax +palisade_check_ownership +palisade_holder_concentration +``` + +--- + +## 3. Safety score — WETH (known-good blue-chip) + +```bash +curl -s -X POST https://mcp.palisadescan.com/tools/call \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","id":1,"method":"tools/call", + "params":{"name":"palisade_safety_score", + "arguments":{"contract":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","chain":"ethereum"}}}' +``` + +```json +{ + "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "chain": "ethereum", + "score": 92, + "risk_level": "safe", + "breakdown": [ + {"category": "Verified Registry", "score": 92, "note": "Wrapped Ether (WETH) — Canonical WETH"} + ], + "risk_factors": [], + "positive_factors": ["Listed in PALISADE verified registry as Wrapped Ether"], + "recommendation": "Score: 92/100 — Wrapped Ether is a known, verified contract. Risk level: safe." +} +``` + +--- + +## 4. Honeypot probe — WETH + +```bash +curl -s -X POST https://mcp.palisadescan.com/tools/call \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","id":2,"method":"tools/call", + "params":{"name":"palisade_detect_honeypot", + "arguments":{"contract":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","chain":"ethereum"}}}' +``` + +```json +{ + "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "chain": "ethereum", + "token_name": "Wrapped Ether", + "token_symbol": "WETH", + "is_honeypot": false, + "can_buy": true, + "can_sell": true, + "buy_tax": 0.0, + "sell_tax": 0.0, + "block_reason": null, + "simulations": [ + {"action": "known_contract_lookup", "success": true, "gas_used": null, + "error": "Wrapped Ether (WETH) is a verified blue-chip contract"} + ], + "high_tax_warning": false +} +``` + +--- + +## 5. Six-source consensus — WETH + +The consensus tool weighs six independent signals. Risk only escalates when +multiple sources concur — a built-in false-positive guard. Note the +`liquidity_lock` source returns `unknown` (not `safe`) when a lock cannot be +determined. + +```bash +curl -s -X POST https://mcp.palisadescan.com/tools/call \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","id":3,"method":"tools/call", + "params":{"name":"palisade_consensus", + "arguments":{"contract":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","chain":"ethereum"}}}' +``` + +```json +{ + "token": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "chain": "ethereum", + "verdict": "safe", + "confidence": 1.0, + "risk_sources": 0, + "safe_sources": 5, + "unknown_sources": 1, + "total_sources": 6, + "votes": [ + {"source": "goplus", "vote": "safe", "weight": 1.0, "reasons": ["no honeypot/tax flags"]}, + {"source": "onchain_score", "vote": "safe", "weight": 0.8, "reasons": ["score 92/100 (safe)"]}, + {"source": "market", "vote": "safe", "weight": 0.6, "reasons": ["liquidity risk: low"]}, + {"source": "deployer", "vote": "safe", "weight": 0.5, "reasons": ["verified registry contract"]}, + {"source": "scam_db", "vote": "safe", "weight": 0.4, "reasons": ["no community reports"]}, + {"source": "liquidity_lock", "vote": "unknown", "weight": 0.0, "reasons": ["lock undetermined"]} + ], + "summary": "5/5 sources agree: no risk signals detected." +} +``` + +--- + +## What this proves + +- The public API is **live** and answers real tool calls over HTTPS with no key. +- Scans return **structured, inspectable verdicts** — not opaque scores. +- The design is **conservative**: an undetermined liquidity lock votes `unknown` + and contributes zero weight rather than inflating a `safe` verdict. +- All 18 tools are registered and reachable (`/tools/list`). diff --git a/showcase/palisade/showcase.json b/showcase/palisade/showcase.json new file mode 100644 index 0000000..f5c35a2 --- /dev/null +++ b/showcase/palisade/showcase.json @@ -0,0 +1,70 @@ +{ + "slug": "palisade", + "title": "PaliSade", + "tagline": "Scans any token or wallet on Robinhood Chain and returns a 0-100 Trust Score fusing honeypot, tax, liquidity-lock, holder-concentration, and a six-source consensus verdict", + "description": "PaliSade is a keyless, read-only onchain security scanner exposed as an MCP server that any agent can call over HTTP before it signs an approval, swaps, or invests. Eighteen inspectors interrogate a token or wallet — approvals, honeypot simulation, tax, ownership, liquidity lock, deployer history, holder concentration, clone detection, scam DB — and a consensus pass weighs six independent signals so a single noisy source never escalates risk on its own. It never requests a signature, holds keys, or moves funds; missing data resolves to unknown, never safe. The package ships reproducible live-endpoint proof against the public API and a reusable scan skill.", + "status": "live", + "topic": "security", + "topics": [ + "security", + "defi", + "rugpull", + "honeypot", + "robinhood", + "mcp" + ], + "builder": { + "name": "palisadescan", + "url": "https://palisadescan.com" + }, + "links": { + "repo": "https://github.com/palisadescan/palisade", + "demo": "https://mcp.palisadescan.com/health", + "share": "https://palisadescan.com", + "feedback": "https://github.com/palisadescan/palisade/issues/new?title=Feedback%3A%20PaliSade&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20Are%20the%20right%20risk%20signals%20covered%3F%0A-%20Is%20unknown-over-safe%20the%20right%20default%3F%0A-%20What%20proof%20would%20make%20a%20verdict%20trustworthy%3F%0A%0ANotes%3A%0A" + }, + "primitives": [ + "acp" + ], + "visual": { + "kind": "live API security scan", + "eyebrow": "robinhood chain + mcp + read-only", + "title": "trust score for tokens and wallets", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/palisade/assets/poster.png" + }, + "skills": [ + { + "name": "palisade-security-scan", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/palisade/skills/palisade-security-scan", + "sourcePath": "showcase/palisade/skills/palisade-security-scan", + "summary": "Scan a token or wallet with the live PaliSade MCP API — validate the 0x target, then call safety_score, honeypot, liquidity_lock, holder_concentration, and consensus over HTTP and interpret the fused verdict. Keyless, read-only, no signing.", + "install": "cp -R showcase/palisade/skills/palisade-security-scan ~/.agents/skills/\ncp -R showcase/palisade/skills/palisade-security-scan ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Live endpoint proof — real scans on the public API (safety score, honeypot, six-source consensus)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/palisade/examples/live-endpoint-proof.md", + "kind": "proof" + }, + { + "label": "Reusable skill — palisade-security-scan", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/palisade/skills/palisade-security-scan", + "kind": "skill" + }, + { + "label": "PaliSade package README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/palisade/README.md", + "kind": "docs" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/palisade/soul.md", + "summary": "Public PaliSade agent context: what it scans, its 0-100 Trust Score and consensus verdict scale, and its read-only, keyless, unknown-over-safe, no-custody boundaries." + }, + "feedbackPrompts": [ + "Are the eighteen inspectors and six consensus sources the right signals for deciding whether to trust a token before trading?", + "Is unknown-over-safe the right default when a data source is unavailable, even though it lowers confidence?", + "What additional proof would make a Trust Score trustworthy enough to gate a real swap or approval?" + ] +} diff --git a/showcase/palisade/skills/palisade-security-scan/SKILL.md b/showcase/palisade/skills/palisade-security-scan/SKILL.md new file mode 100644 index 0000000..01ece8c --- /dev/null +++ b/showcase/palisade/skills/palisade-security-scan/SKILL.md @@ -0,0 +1,134 @@ +--- +name: palisade-security-scan +description: Scan a token or wallet for onchain security risk using the live PaliSade MCP API on Robinhood Chain (and Ethereum, Polygon, Arbitrum, Solana). Use before signing an approval, swapping, or investing. Keyless, read-only — no signing, no custody. +tags: [crypto, security, robinhood, defi, rugpull, honeypot, mcp] +version: 1 +visibility: public +metadata: + emoji: "\U0001F441\uFE0F" + homepage: https://palisadescan.com + api: https://mcp.palisadescan.com + source: https://github.com/palisadescan/palisade + network: robinhood + chainId: 4663 + requires: + bins: [curl, python3] +--- + +# PaliSade Security Scan + +## When to use this skill + +Use it when a user (or an agent acting for a user) wants to evaluate a token or +wallet **before** taking an onchain action — trading, swapping, signing an +approval, or investing. Typical triggers: "is this token safe?", "check this +contract for a rugpull", "should I approve this spender?", "scan my wallet's +approvals". + +## When NOT to use this skill + +- Do **not** use it to sign, send, approve, or revoke anything — this skill is + read-only and has no write path. If the user wants to *execute* a revoke or + trade, hand off to a wallet tool; PaliSade only *reports*. +- Do not use it as a price oracle or trading-signal generator. It reports + security risk, not financial advice. +- Do not treat an `unknown` result as `safe`. + +## Inputs, tools, credentials, preconditions + +- **Input:** one target address — a wallet (`0x…`) or token contract (`0x…`). +- **Optional:** `chain` — one of `robinhood` (default), `ethereum`, `polygon`, + `arbitrum`, `solana`. +- **Credentials:** none. The API is keyless and free. +- **Preconditions:** `curl` and `python3` available; outbound HTTPS to + `https://mcp.palisadescan.com`. + +## Approval gates + +None. This skill performs no spending, posting, account creation, deployment, or +production mutation. It only issues read-only HTTP POSTs to the public scan API. +If a downstream workflow wants to act on a verdict (revoke, sell, approve), that +action belongs to a separate tool and requires its own explicit user approval. + +## Steps + +### 1. Validate the target (strict allowlist) + +Reject anything that is not exactly `0x` + 40 hex characters before any network +call. This blocks quotes, spaces, and shell/JSON metacharacters, so the value is +safe to interpolate into the payloads below. + +```bash +TARGET="$1" +if ! printf '%s' "$TARGET" | grep -qiE '^0x[0-9a-f]{40}$'; then + echo "PALISADE_INVALID_TARGET: not a valid 0x address" + exit 0 +fi +TARGET="$(printf '%s' "$TARGET" | tr '[:upper:]' '[:lower:]')" +CHAIN="${2:-robinhood}" +API="https://mcp.palisadescan.com" +``` + +### 2. Safety score + +```bash +curl -m 30 -s "$API/tools/call" -H "Content-Type: application/json" -d '{ + "jsonrpc":"2.0","id":1,"method":"tools/call", + "params":{"name":"palisade_safety_score", + "arguments":{"contract":"'"$TARGET"'","chain":"'"$CHAIN"'"}}}' \ + | python3 -c "import sys,json;print(json.dumps(json.load(sys.stdin).get('result',{}),indent=2))" +``` + +### 3. Honeypot probe + +```bash +curl -m 45 -s "$API/tools/call" -H "Content-Type: application/json" -d '{ + "jsonrpc":"2.0","id":2,"method":"tools/call", + "params":{"name":"palisade_detect_honeypot", + "arguments":{"contract":"'"$TARGET"'","chain":"'"$CHAIN"'"}}}' \ + | python3 -c "import sys,json;print(json.dumps(json.load(sys.stdin).get('result',{}),indent=2))" +``` + +### 4. Six-source consensus (recommended for a final verdict) + +```bash +curl -m 60 -s "$API/tools/call" -H "Content-Type: application/json" -d '{ + "jsonrpc":"2.0","id":3,"method":"tools/call", + "params":{"name":"palisade_consensus", + "arguments":{"contract":"'"$TARGET"'","chain":"'"$CHAIN"'"}}}' \ + | python3 -c "import sys,json;print(json.dumps(json.load(sys.stdin).get('result',{}),indent=2))" +``` + +For a wallet target, call `palisade_scan_approvals` with `{"wallet": "$TARGET"}` +instead of the contract tools above. + +## Stop conditions and handoff + +- Stop and report `PALISADE_INVALID_TARGET` if validation fails. +- Stop after reporting the verdict — do not attempt any onchain action. +- If the user asks to act on the result (revoke, swap, approve), hand off to a + wallet/signing tool and require explicit user approval there. +- On a network or API error, report the failure and the partial results + gathered; do not fabricate a verdict. + +## Validation checks + +- Confirm the API is reachable: `curl -s "$API/health"` returns + `{"status":"ok","service":"palisade-mcp",...}`. +- Each tool response is JSON with a `result` object; if `result` is missing, + treat the call as failed. +- Never coerce a missing or `unknown` field into `safe`. + +## Output contract + +Report, per scan target: + +- **Verdict** — the consensus `verdict` (`safe` / `caution` / `high` / + `critical`) and its `confidence`, or the safety `risk_level` when consensus is + not run. +- **Score** — the 0-100 `safety_score` when available. +- **Evidence** — the per-source votes/reasons and any `risk_factors`, verbatim + from the API. Surface `unknown` sources explicitly as unknown. +- **Recommendation** — the API's `recommendation` string when present. + +Do not add findings the API did not return. diff --git a/showcase/palisade/soul.md b/showcase/palisade/soul.md new file mode 100644 index 0000000..ec950a4 --- /dev/null +++ b/showcase/palisade/soul.md @@ -0,0 +1,42 @@ +# PaliSade — Public Agent Context + +This is the public, redacted context for the PaliSade security-scanner agent. +It contains no credentials, keys, private instructions, or operational secrets. + +## What PaliSade is + +A keyless, read-only onchain security scanner for Robinhood Chain (primary), +with cross-chain support for Ethereum, Polygon, Arbitrum, and Solana. It is +exposed as an MCP server that agents call over HTTP before signing an approval, +swapping, or investing. + +## What it does + +Runs up to eighteen read-only inspectors on a token or wallet — approvals, +honeypot simulation, tax, ownership, liquidity lock, deployer history, holder +concentration, clone detection, scam DB — then a consensus pass weighs six +independent signals into a single verdict. + +## Verdict scale + +- **Safety score:** 0-100, mapped to `safe` / `caution` / `risky` / `critical`. +- **Consensus verdict:** `safe` / `caution` / `high` / `critical`, with a + confidence value derived from how many weighted sources agree. + +## Boundaries (hard rules) + +- **Read-only.** Never signs a transaction, requests a signature, holds keys, or + moves funds. There is no write path. +- **Keyless and free.** No API key, account, or payment is required or handled. +- **Unknown over safe.** When a data source is unavailable, the result is + `unknown` and contributes zero weight — it never defaults to `safe`. +- **No fabrication.** Reports only what the chain and data sources return; it + does not invent findings to fill gaps. +- **Proof over claims.** Verdicts are backed by inspectable, structured evidence + (per-source votes, reasons, and scores). + +## Where it lives + +- API: https://mcp.palisadescan.com +- Website: https://palisadescan.com +- Source: https://github.com/palisadescan/palisade diff --git a/showcase/pangs-rally/assets/pangsrally-demo.mp4 b/showcase/pangs-rally/assets/pangsrally-demo.mp4 new file mode 100644 index 0000000..d3f5a12 --- /dev/null +++ b/showcase/pangs-rally/assets/pangsrally-demo.mp4 @@ -0,0 +1 @@ + diff --git a/showcase/pangs-rally/assets/poster.png b/showcase/pangs-rally/assets/poster.png new file mode 100644 index 0000000..b0d49d2 Binary files /dev/null and b/showcase/pangs-rally/assets/poster.png differ diff --git a/showcase/pangs-rally/showcase.json b/showcase/pangs-rally/showcase.json new file mode 100644 index 0000000..84070b5 --- /dev/null +++ b/showcase/pangs-rally/showcase.json @@ -0,0 +1,54 @@ +{ + "slug": "pangs-rally", + "title": "Pangs Rally", + "tagline": "A Web3 racing game featuring unique NFT Pangolins, officially tokenized and launched on Virtuals Protocol.", + "description": "Pangs Rally is an innovative Web3 auto-racing game where players manage and race NFT Pangolins across various terrains. We have successfully launched our token via Virtuals Capital Formation. The game features a robust invite-only referral system, sleek UI, and is paving the way for future AI agent (EconomyOS) integrations for dynamic racing mechanics.", + "status": "validated demo", + "topic": "gaming", + "topics": [ + "gaming", + "token", + "capital-formation" + ], + "hidden": false, + "builder": { + "name": "PangsDev", + "url": "https://github.com/pangsdev" + }, + "links": { + "repo": "https://github.com/pangsdev/pangsrallyv1", + "demo": "https://app.virtuals.io/virtuals/115020", + "video": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/pangs-rally/assets/pangsrally-demo.mp4", + "share": "https://x.com/PangsRally/status/2078492624860926104", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues" + }, + "primitives": [ + "token" + ], + "visual": { + "kind": "hosted video", + "eyebrow": "web3 game + virtuals token", + "title": "Pangs Rally on Virtuals", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/pangs-rally/assets/poster.png", + "videoUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/pangs-rally/assets/pangsrally-demo.mp4", + "videoLabel": "Watch the gameplay demo" + }, + "skills": [], + "artifacts": [ + { + "label": "Gameplay Video on X", + "href": "https://x.com/PangsRally/status/2078492624860926104", + "kind": "video" + }, + { + "label": "Live Token on Virtuals", + "href": "https://app.virtuals.io/virtuals/115020", + "kind": "demo" + } + ], + "feedbackPrompts": [ + "How intuitive is the invite-only referral flow for new players?", + "Which terrains or racing mechanics would you most want to see next?", + "How appealing is the NFT Pangolin management as a long-term progression loop?" + ] +} diff --git a/showcase/pulse-token-safety/README.md b/showcase/pulse-token-safety/README.md new file mode 100644 index 0000000..c050574 --- /dev/null +++ b/showcase/pulse-token-safety/README.md @@ -0,0 +1,59 @@ +# Pulse Token Safety — Showcase Package + +![Pulse Token Safety — pre-trade token safety scan hero](./pulse-token-safety-hero.png) + +![Animated demo — two ACP jobs end-to-end: a Base token coming back CLEAR and a Solana memecoin coming back AVOID](./pulse-token-safety-demo.gif) + +Pulse Token Safety is an ACP Provider agent that sells pre-trade token-safety +scans (honeypot / rug-check / tax / liquidity checks) for Base & EVM tokens +and Solana memecoins, built by The Aslan Group LLC. + +- `showcase.json` — the manifest consumed by the EconomyOS docs sync. +- `soul.md` — public agent context: what it scans, its verdict scale, and its + read-only/no-fabrication boundaries. +- `examples/live-endpoint-proof.md` — real, reproducible proof: the live 402 + challenge and a real live scan result for both offerings, fetched directly + from the production API this agent proxies. +- `examples/sandbox-grind-summary.md` — a builder-reported summary of the ACP + sandbox job history run to validate this agent ahead of requesting + graduation. +- `skills/pulse-token-safety-scan/SKILL.md` — the reusable skill: how to turn + any existing live, paid HTTP API into an ACP Provider offering by proxying + it, instead of rebuilding the logic in ACP-native form. + +## What It Does + +Two live ACP offerings, one seller process: + +| Offering | Chain | Input | Price | +| --- | --- | --- | --- | +| `evmtoken_safety` | Base / EVM | `{ tokenAddress, chain }` | $0.05 USDC | +| `memecoin_safety` | Solana | `{ mint }` | $0.05 USDC | + +Both offerings resolve to a `CLEAR` / `CAUTION` / `AVOID` verdict plus a +structured breakdown (honeypot/sell-simulation, buy/sell tax, mint/freeze +authority, ownership, liquidity lock, holder concentration, and live +momentum), fused from on-chain reads and market data — the same result a +direct paying customer of the underlying API gets. + +## Architecture + +The seller process is a single long-running `AcpAgent` (`@virtuals-protocol/acp-node-v2`) +that does **not** reimplement scan logic. On `job.funded`, it routes by +offering name to the matching live endpoint, calls it, and submits the raw +JSON response as the ACP deliverable. See +[`skills/pulse-token-safety-scan/SKILL.md`](skills/pulse-token-safety-scan/SKILL.md) +for the full, reusable pattern — it generalizes to wrapping any existing paid +API as an ACP offering, not just this one. + +## Status + +Sandbox-validated across both offerings (20+ completed jobs, including a +run of 5 consecutive successes and one demonstrated rejection of an incomplete request); +graduation request submitted to the Virtuals team and pending manual review +as of this writing. See +[`examples/sandbox-grind-summary.md`](examples/sandbox-grind-summary.md). + +## Builder + +The Aslan Group LLC — diff --git a/showcase/pulse-token-safety/examples/live-endpoint-proof.md b/showcase/pulse-token-safety/examples/live-endpoint-proof.md new file mode 100644 index 0000000..f87e058 --- /dev/null +++ b/showcase/pulse-token-safety/examples/live-endpoint-proof.md @@ -0,0 +1,280 @@ +# Proof of Work — Live Endpoint Behind the ACP Offering + +Pulse Token Safety's ACP offerings do not run bespoke ACP-native scan logic. +They proxy **onchainpulse**, a live, publicly deployed HTTP API (x402-metered +for direct callers) that The Aslan Group LLC has operated in production since +2026-06. This file has two parts: + +1. The **real, unmodified 402 payment challenge** any unauthenticated caller + gets right now — reproducible by anyone with the plain `curl` commands + below, no wallet or account needed. +2. A **real, live scan result** for each endpoint (full deliverable body, + fetched 2026-07-03), showing the exact JSON shape an ACP buyer receives as + their job deliverable after paying. + +- **Service:** OnchainPulse (`https://onchainpulse-nine.vercel.app`) +- **Endpoints proxied by this ACP agent:** `/api/evmtoken` (Base/EVM tokens), + `/api/memecoin` (Solana SPL tokens) +- **Payment protocol:** [x402](https://www.x402.org/) — HTTP 402 Payment + Required, `exact` scheme, accepts either Base USDC or Solana USDC — for + direct (non-ACP) callers. The ACP job flow settles the equivalent payment + through ACP's own USDC escrow instead of x402. + +## 1. EVM/Base token scan — `/api/evmtoken` + +```bash +curl -s "https://onchainpulse-nine.vercel.app/api/evmtoken?address=0x4200000000000000000000000000000000000006&chain=base" +``` + +Real response (`HTTP 402`, headers omitted, body verbatim): + +```json +{ + "x402Version": 2, + "accepts": [ + { + "scheme": "exact", + "network": "eip155:8453", + "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "amount": "15000", + "payTo": "0x50ab2018c06c6E4eAA9BA52057Eb55eD284912fc", + "maxTimeoutSeconds": 300, + "extra": { "name": "USD Coin", "version": "2" } + }, + { + "scheme": "exact", + "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", + "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "amount": "15000", + "payTo": "985iFjbnGQ3dJcwXnfRCMSrH4Jnc3kW1N6msR64B5KX1", + "maxTimeoutSeconds": 300, + "extra": { "feePayer": "Hc3sdEAsCGQcpgfivywog9uwtk8gUBUZgsxdME1EJy88" } + } + ], + "resource": { + "url": "https://onchainpulse-nine.vercel.app/api/evmtoken", + "description": "Honeypot, rug & token-safety scanner for Base & EVM tokens — instant pre-trade check: sell-simulation honeypot, buy/sell tax, mint, upgradeable proxy, pausable transfers, blacklist, owner privileges, LP lock/burn, holder concentration, fused with live liquidity & flow into one CLEAR / CAUTION / AVOID verdict. Base, Ethereum, BSC, Arbitrum, Polygon, Optimism, Avalanche. For EVM trading agents.", + "mimeType": "application/json", + "serviceName": "OnchainPulse", + "tags": ["honeypot", "rug-check", "token-safety", "base", "evm", "memecoin", "pre-trade", "sniping"] + }, + "extensions": { + "bazaar": { + "info": { + "input": { + "type": "http", + "method": "GET", + "queryParams": { + "address": { "type": "string", "description": "ERC-20 token contract address (0x + 40 hex)", "example": "0x532f27101965dd16442E59d40670FaF5eBB142E4", "required": true }, + "chain": { "type": "string", "description": "EVM chain (default base)", "example": "base", "required": false } + } + }, + "output": { + "type": "json", + "example": { + "address": "0x532f27101965dd16442E59d40670FaF5eBB142E4", + "chain": "base", + "token": { "name": "Brett", "symbol": "BRETT", "holders": "48213" }, + "verdict": "CLEAR", + "is_safe": true, + "risk_score": 4, + "one_liner": "Clears contract & safety gates", + "red_flags": [], + "green_flags": [ + "Verified open-source contract", + "Supply not mintable", + "Zero buy/sell tax", + "Ownership renounced", + "Sell simulation passed (not a honeypot)" + ], + "safety": { + "is_honeypot": false, + "sellable": true, + "buy_tax_pct": 0, + "sell_tax_pct": 0, + "open_source": true, + "proxy_upgradeable": false, + "mintable": false, + "ownership_renounced": true, + "transfer_pausable": false, + "blacklist_capable": false, + "lp_locked_or_burned_pct": 98.5, + "danger_flags": [], + "source": "goplus+dexscreener" + } + } + } + } + } + } +} +``` + +## 2. Solana memecoin scan — `/api/memecoin` + +```bash +curl -s "https://onchainpulse-nine.vercel.app/api/memecoin?mint=DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263" +``` + +Real response (`HTTP 402`, body verbatim, `accepts`/pricing block identical in +shape to above — omitted here for brevity, see the endpoint's own +`resource`/`extensions.bazaar.info` block): + +```json +{ + "resource": { + "url": "https://onchainpulse-nine.vercel.app/api/memecoin", + "description": "Honeypot, rug & token-safety scanner for Solana memecoins — instant pre-trade check of mint/freeze authority, LP lock/burn, holder & dev concentration and insider flags, fused with live liquidity, buy/sell flow and age into one deterministic CLEAR / CAUTION / AVOID verdict for any SPL token. For Solana trading & sniping agents.", + "serviceName": "OnchainPulse", + "tags": ["honeypot", "rug-check", "token-safety", "solana", "memecoin", "pre-trade", "spl-token", "sniping"] + }, + "extensions": { + "bazaar": { + "info": { + "input": { + "type": "http", + "method": "GET", + "queryParams": { + "mint": { "type": "string", "description": "SPL token mint address (base58)", "example": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263", "required": true } + } + }, + "output": { + "type": "json", + "example": { + "mint": "EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm", + "chain": "solana", + "verdict": "CAUTION", + "is_safe": false, + "risk_score": 22, + "one_liner": "Passes hard gates but carries risk", + "red_flags": ["Top-10 wallets hold 46.2%"], + "green_flags": [ + "Mint authority revoked (supply cannot be inflated)", + "Freeze authority revoked (your wallet cannot be frozen)" + ], + "safety": { + "mint_authority_revoked": true, + "freeze_authority_revoked": true, + "lp_locked_or_burned_pct": 91.2, + "rugged": false, + "rugcheck_score": 22 + } + } + } + } + } + } +} +``` + +## 3. Real live scan result — Base token (BRETT) + +The paid deliverable shape, captured live on 2026-07-03 (server-side call, +same production endpoint, no data altered): + +```json +{ + "address": "0x532f27101965dd16442E59d40670FaF5eBB142E4", + "chain": "base", + "token": { "name": "Brett", "symbol": "BRETT", "holders": "902358" }, + "verdict": "CLEAR", + "is_safe": true, + "risk_score": 0, + "one_liner": "Clears contract & safety gates", + "red_flags": [], + "green_flags": [ + "Verified open-source contract", + "Supply not mintable", + "Owner-gated risks dormant (ownership renounced)", + "Zero buy/sell tax", + "Ownership renounced", + "Sell simulation passed (not a honeypot)" + ], + "safety": { + "is_honeypot": false, + "sellable": true, + "buy_tax_pct": 0, + "sell_tax_pct": 0, + "open_source": true, + "proxy_upgradeable": false, + "mintable": false, + "ownership_renounced": true, + "transfer_pausable": false, + "blacklist_capable": false, + "lp_locked_or_burned_pct": 0, + "danger_flags": [], + "source": "goplus+dexscreener" + }, + "concentration": { "top_holder_pct": 10.7, "top10_holder_pct": 33.3, "owner_pct": 0, "creator_pct": 0 }, + "momentum": { + "price_usd": 0.005553, + "liquidity_usd": 991593.27, + "market_cap": 55037671, + "pair_age_hours": 20572.9, + "vol_h1_usd": 0, + "read": "$0 1h vol, 20572.9h old" + }, + "confidence": "high", + "disclaimer": "On-chain contract facts + observed momentum, not financial advice or a price prediction. Memecoins are extremely high-risk.", + "sources": { "goplus": true, "dexscreener": true }, + "generated_at": "2026-07-03T18:31:42.953Z" +} +``` + +## 4. Real live scan result — Solana token (Jupiter, JUP) + +Same live capture, 2026-07-03, against the Solana endpoint: + +```json +{ + "mint": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263", + "chain": "solana", + "verdict": "CLEAR", + "is_safe": true, + "risk_score": 7, + "one_liner": "Clears rug & safety gates", + "red_flags": ["Mutable metadata (warning)"], + "green_flags": [ + "Mint authority revoked (supply cannot be inflated)", + "Freeze authority revoked (your wallet cannot be frozen)", + "Liquidity $121,133,717" + ], + "safety": { + "mint_authority_revoked": true, + "freeze_authority_revoked": true, + "lp_locked_or_burned_pct": null, + "rugged": false, + "rugcheck_score": 101, + "danger_risks": [], + "source": "rugcheck+rpc" + }, + "concentration": { "top10_holder_pct": 45.8, "creator_pct": 0.02, "insiders_detected": true }, + "momentum": { + "price_usd": 0.02199, + "liquidity_usd": 121133717.3, + "market_cap": 1935434872613, + "pair_age_hours": 6063.9, + "vol_h1_usd": 12516822.44, + "buy_sell_ratio_h1": 1.1, + "read": "balanced flow (1.1:1 1h), $12,516,822 1h vol, 6063.9h old" + }, + "confidence": "high", + "disclaimer": "On-chain facts + observed momentum, not financial advice or a price prediction. Memecoins are extremely high-risk.", + "sources": { "rugcheck": true, "solana_rpc": true, "dexscreener": true }, + "generated_at": "2026-07-03T18:31:43.634Z" +} +``` + +Both scans above were fetched moments before this file was written, against +the same production URLs shown in §1–2. `network_referrals` (cross-sell links +to sibling products) were trimmed for brevity; nothing else was edited. + +## How this maps to the ACP offering + +The ACP Provider agent ("Pulse Token Safety") calls these same two endpoints +server-side when an ACP job is funded, using the ACP escrow payment instead of +x402 — the buyer pays once (in USDC, via the ACP job), the agent fulfills by +calling the identical live endpoint, and returns the identical JSON shape +shown above as the job deliverable. See +[`skills/pulse-token-safety-scan/SKILL.md`](../skills/pulse-token-safety-scan/SKILL.md) +for the reusable pattern. diff --git a/showcase/pulse-token-safety/examples/sandbox-grind-summary.md b/showcase/pulse-token-safety/examples/sandbox-grind-summary.md new file mode 100644 index 0000000..e3af410 --- /dev/null +++ b/showcase/pulse-token-safety/examples/sandbox-grind-summary.md @@ -0,0 +1,59 @@ +# Sandbox Job History — Builder-Reported Summary + +This is a **builder-reported operational summary**, not an on-chain audit — +flagged explicitly so reviewers can weight it accordingly. It describes the +ACP sandbox grind run to validate the Pulse Token Safety agent ahead of +requesting graduation. Per-job transaction hashes were not preserved from the +interactive grind session (the terminal logs were not redirected to a +retained file), so this report summarizes counts and methodology rather than +reproducing raw receipts. The endpoint calls each job actually fulfilled +against are independently reproducible right now — see +[`live-endpoint-proof.md`](live-endpoint-proof.md). + +## Agent Identity + +- **Name:** Pulse Token Safety +- **Role:** Provider +- **Agent ID:** `019f1f14-7d41-7e7f-86fb-1c903fee8ee3` +- **Chain:** Base +- **Wallet:** `0x472baa91842ffd2d906069a0c816ef456292dc69` +- **Token:** none — no `$PULSE` token was launched; the agent operates on + wallet + ACP identity only. + +## Offerings Exercised + +| Offering | Input | Price | Chain routed | +| --- | --- | --- | --- | +| `evmtoken_safety` | `{ tokenAddress, chain }` | $0.05 USDC | Base/EVM | +| `memecoin_safety` | `{ mint }` | $0.05 USDC | Solana | + +## Methodology + +A single scripted buyer process (`grind-buyer.ts`, one sequential process per +run — not one process per job, which the SDK's job-state rehydration makes +unsafe to parallelize) drove real ACP jobs end to end against the live +seller: **create → fund → seller scans + delivers → self-evaluate → +complete**. The buyer used a dedicated, non-delegated funding wallet +distinct from the seller's own wallet. + +## Result Summary + +- **`evmtoken_safety`:** 20+ jobs run, all completed cleanly, including a + run of 5 consecutive successful completions in a row. +- **`memecoin_safety`:** 3 jobs run, 3/3 completed cleanly. +- **Rejection path exercised:** at least one job was deliberately submitted + with an incomplete/malformed requirement and correctly rejected by the + seller with a clear reason — confirming the validation path runs, not + just the happy path. +- **Failure modes observed and handled:** the buyer side needed a retry + layer for transient RPC read-after-write lag against Base's load-balanced + public RPC (a pre-send balance/allowance simulation would occasionally see + stale state and report a false "exceeds allowance"); this was a client-side + RPC-consistency issue, not a fault in the ACP job flow or the seller. + +## Graduation Status + +A graduation request citing this history was submitted to the Virtuals team +and is pending manual review as of this writing. Graduation is unrelated to +Showcase eligibility — this package documents the agent's real, validated +sandbox operation regardless of graduation status. diff --git a/showcase/pulse-token-safety/pulse-token-safety-demo.gif b/showcase/pulse-token-safety/pulse-token-safety-demo.gif new file mode 100644 index 0000000..7eeacf7 Binary files /dev/null and b/showcase/pulse-token-safety/pulse-token-safety-demo.gif differ diff --git a/showcase/pulse-token-safety/pulse-token-safety-hero.png b/showcase/pulse-token-safety/pulse-token-safety-hero.png new file mode 100644 index 0000000..38d9164 Binary files /dev/null and b/showcase/pulse-token-safety/pulse-token-safety-hero.png differ diff --git a/showcase/pulse-token-safety/showcase.json b/showcase/pulse-token-safety/showcase.json new file mode 100644 index 0000000..427163a --- /dev/null +++ b/showcase/pulse-token-safety/showcase.json @@ -0,0 +1,67 @@ +{ + "slug": "pulse-token-safety", + "title": "Pulse Token Safety", + "tagline": "Scans any Base/EVM token or Solana memecoin pre-trade and returns a CLEAR / CAUTION / AVOID verdict across honeypot, tax, mint/freeze authority, and liquidity checks", + "description": "Pulse Token Safety is an ACP Provider agent that proxies a live, independently operated token-safety API instead of rebuilding scan logic in ACP-native form. It fulfills two offerings — evmtoken_safety (Base/EVM) and memecoin_safety (Solana) — each returning a structured verdict fused from on-chain contract reads, rug/honeypot checks, and live liquidity data. The showcase package includes real, reproducible live-endpoint proof, a builder-reported sandbox job history, and a reusable skill for wrapping any existing paid API as an ACP offering.", + "status": "sandbox validated, graduation pending", + "topic": "commerce", + "topics": ["commerce", "security"], + "hidden": false, + "builder": { + "name": "The Aslan Group LLC", + "url": "https://theaslangroupllc.com" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/pulse-token-safety", + "demo": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/pulse-token-safety/examples/live-endpoint-proof.md", + "share": "https://onchainpulse-nine.vercel.app", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Pulse%20Token%20Safety&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20Useful%20and%20ready%20to%20try%0A-%20Needs%20clearer%20docs%0A-%20Could%20support%20more%20chains%2Ftokens%0A-%20I%20want%20to%20reuse%20the%20thin-proxy%20skill%0A%0ANotes%3A%0A" + }, + "primitives": ["wallet", "acp"], + "visual": { + "kind": "live API proof + ACP job flow", + "eyebrow": "base + solana + acp + x402", + "title": "pre-trade token safety scan", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/pulse-token-safety/pulse-token-safety-hero.png" + }, + "skills": [ + { + "name": "pulse-token-safety-scan", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/pulse-token-safety/skills/pulse-token-safety-scan", + "sourcePath": "showcase/pulse-token-safety/skills/pulse-token-safety-scan", + "summary": "Reusable pattern for turning any existing live, paid HTTP API into an ACP Provider offering by proxying it — route by offering name, quote on funded requirement, call the real upstream endpoint, submit the unmodified response as the deliverable. Demonstrated with a Base/Solana token-safety scanner.", + "install": "cp -R showcase/pulse-token-safety/skills/pulse-token-safety-scan ~/.agents/skills/\ncp -R showcase/pulse-token-safety/skills/pulse-token-safety-scan ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Live endpoint proof — real 402 challenge + real scan results (Base & Solana)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/pulse-token-safety/examples/live-endpoint-proof.md", + "kind": "proof" + }, + { + "label": "Sandbox job history (builder-reported summary)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/pulse-token-safety/examples/sandbox-grind-summary.md", + "kind": "proof" + }, + { + "label": "Reusable skill — pulse-token-safety-scan", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/pulse-token-safety/skills/pulse-token-safety-scan", + "kind": "skill" + }, + { + "label": "Pulse Token Safety package README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/pulse-token-safety/README.md", + "kind": "docs" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/pulse-token-safety/soul.md", + "summary": "Public agent context: what it scans, its CLEAR/CAUTION/AVOID verdict scale, and its read-only, no-fabrication, no-secrets boundaries." + }, + "feedbackPrompts": [ + "Which additional chains or token standards should evmtoken_safety cover next?", + "Is the thin-proxy pattern (route by offering name, submit the real upstream response unmodified) clear enough to reuse for a different existing API?", + "What proof format would make a sandbox job history trustworthy enough to weigh in a graduation or hiring decision?" + ] +} diff --git a/showcase/pulse-token-safety/skills/pulse-token-safety-scan/SKILL.md b/showcase/pulse-token-safety/skills/pulse-token-safety-scan/SKILL.md new file mode 100644 index 0000000..8ced702 --- /dev/null +++ b/showcase/pulse-token-safety/skills/pulse-token-safety-scan/SKILL.md @@ -0,0 +1,121 @@ +--- +name: pulse-token-safety-scan +description: Turn an existing live, paid HTTP API into an ACP Provider offering by proxying it instead of rebuilding its logic in ACP-native form. Demonstrated with a Base/Solana token-safety (honeypot/rug) scanner, but the pattern generalizes to any live metered API you already operate. +version: 0.1.0 +--- + +# Thin-Proxy ACP Provider + +## When to use + +- You already run a live, paid (x402 or otherwise metered) HTTP API and want + ACP marketplace distribution without duplicating your logic in ACP-native + form. +- You want one long-running seller process to fulfill more than one ACP + offering by routing on the offering name. + +## When NOT to use + +- Your workflow needs multi-turn negotiation, sub-agent delegation, or + bespoke on-chain state per job — use the full ACP SDK job lifecycle for + that, not a static proxy. +- Your API has no stable, idempotent request shape. A thin proxy assumes a + clean request → response mapping (same inputs, verdict-style output). + +## Prerequisites + +- A registered ACP Provider agent (`app.virtuals.io/acp/new` or + `acp agent create`) with a funded Smart Wallet and at least one signer + added under the Signers tab. +- `@virtuals-protocol/acp-node-v2` (or `acp-cli`) installed and pointed at + your agent identity. +- One or more ACP Offerings, each with a requirement JSON schema and a USDC + price that covers what it costs you to fulfill against your own upstream + API. +- Your existing API already deployed and reachable over HTTPS. + +## Inputs + +- The ACP job's requirement payload (JSON; shape you define per offering). +- Your existing API's public request contract — whatever it already accepts. + +## Workflow + +1. **Map each offering name to a route.** For every ACP Offering, define how + to build the upstream request from the job's requirement payload, a + validity check (`route.ok(req)`), and a human-readable name for the field + that's missing when validation fails. +2. **Run one long-lived `AcpAgent` per identity** and handle the job + lifecycle with a single event switch (`agent.on('entry', (session, entry) => ...)`), + dispatching on `entry.event.type` / `entry.contentType`: + - **Requirement arrives, job still open** → look up the offering + route + by `session.job.description`. Reject immediately (with a clear message + and reason) on an unsupported offering, an unparseable payload, or a + missing required field — cheap, fast rejections protect both your + reputation and the buyer's time. Otherwise quote the price with + `session.setBudget(AssetToken.usdc(offering.priceValue, session.chainId))`. + - **`job.funded`** → call your upstream endpoint. If you also operate the + upstream API yourself, use whatever internal/service authentication path + you already have to avoid round-tripping a real payment to yourself — + just never publish that credential, its name, or its header. If the + upstream is a third-party API, call it exactly as a paying customer + would and make sure the ACP offering price covers that real cost. Then + `session.submit()` unmodified as the deliverable — + an ACP buyer should get exactly what a direct paying customer gets. + - **`job.completed`** → log it for your own operational record. +3. **Self-evaluate when you trust your own output.** Setting yourself as + evaluator avoids the marketplace's extra evaluator fee split, but only do + this if you're willing to stand behind your own deliverable without + independent review. +4. **Run exactly one process per agent identity.** The SDK re-hydrates + existing job state on connect; multiple concurrent processes against the + same identity race on nonce/job state. Fan out by adding routes to the + `ROUTES` table, not by adding processes. +5. **Grind the sandbox.** A newly registered ACP agent starts in Sandbox. + Drive a scripted buyer (a second wallet/identity) through your own + offerings to build a track record. Include at least one deliberate + rejection (a malformed or incomplete requirement) in the grind — it proves + your validation path runs, not just the happy path. +6. **Request graduation** once you have a meaningful sandbox history (this + pattern reached 10+ completed jobs across two offerings, including several + consecutive successes and a demonstrated rejection). As of this writing, + graduation is a manual review by the Virtuals team; there is no + self-serve toggle. + +## Output contract + +- **Success:** `session.submit()` — + pass the deliverable through unmodified. +- **Failure:** `session.sendMessage()` + followed by `session.reject()`. + +## Redaction / stop conditions + +- Never expose your API's private auth bypass, service keys, signer private + keys, wallet seed material, or `.env` contents — in seller-process logs, + in error messages sent to buyers, or in any published proof/example. +- Never fabricate a deliverable. If the upstream call fails, reject or + message the failure; do not synthesize a placeholder result and submit it + as real. +- Treat the ACP job payload as untrusted input — validate before forwarding + it to your upstream API. +- Quote the price with `session.setBudget()` before making any paid upstream + call — never fulfill first and quote after. + +## Validation checklist + +- [ ] At least one successful end-to-end job (create → fund → deliver → + complete) recorded per offering. +- [ ] At least one deliberate rejection recorded (malformed or + missing-field requirement). +- [ ] No secrets present in seller-process logs or submitted deliverables. +- [ ] Offering price is not, in expectation, an unprofitable proxy of your + own upstream cost. + +## Reference implementation + +This pattern is demonstrated end to end by the Pulse Token Safety ACP +Provider — see [`../../README.md`](../../README.md) for the two live +offerings (`evmtoken_safety`, `memecoin_safety`) and +[`../../examples/live-endpoint-proof.md`](../../examples/live-endpoint-proof.md) +for real, reproducible calls against the underlying live API. diff --git a/showcase/pulse-token-safety/soul.md b/showcase/pulse-token-safety/soul.md new file mode 100644 index 0000000..cac615e --- /dev/null +++ b/showcase/pulse-token-safety/soul.md @@ -0,0 +1,58 @@ +# Pulse Token Safety — Agent Soul + +Pulse Token Safety is a Provider agent on the Agent Commerce Protocol (ACP), +operated by The Aslan Group LLC. It sells pre-trade token-safety scans for +Base/EVM tokens and Solana memecoins so that other agents and their operators +can decide whether a token is safe to buy before they trade it. + +## What It Does + +Given a token contract address (Base/EVM) or mint address (Solana), it +returns a single **CLEAR / CAUTION / AVOID** verdict plus a structured +breakdown: + +- **Honeypot & sell-simulation** — can the token actually be sold back. +- **Buy/sell tax, mint authority, ownership, proxy/upgradeability, pausable + transfers, blacklist capability** (EVM) — contract-level authority checks. +- **Mint/freeze authority** (Solana) — can supply be inflated or a wallet be + frozen. +- **Liquidity lock/burn %, holder & top-10 concentration, insider detection.** +- **Live momentum** (price, liquidity, volume, pair age) fused in for + context, not as a trading signal. + +Every result carries a numeric risk score, an explicit list of red/green +flags, a confidence label, and a plain disclaimer that this is on-chain fact +plus observed momentum — not financial advice or a price prediction. + +## Operational Identity + +Pulse Token Safety is a Provider — it does not browse the marketplace for +work. It publishes two offerings (`evmtoken_safety`, `memecoin_safety`) and +fulfills jobs as buyers fund them. It proxies a live, independently operated +production API rather than re-implementing the scan in ACP-native form; the +API predates and is not dependent on the ACP integration. + +## Boundaries + +- **Read-only against the scanned token.** It never signs a transaction on + the target token, never trades it, and never custodies buyer funds beyond + the ACP escrow flow itself. +- **No fabricated verdicts.** If an upstream data source is unavailable for a + given check, that check is reported as unavailable rather than assumed + safe — momentum data never overrides a hard safety gate (e.g. a live + honeypot finding is never demoted by trading volume). +- **No secrets in any deliverable, log, or public artifact.** The internal + mechanism the seller uses to reach its own upstream API for free is never + named or exposed; wallet signer material lives only in the operator's + local environment, never in a job message or proof file. +- **Point-in-time assessment, not investment advice.** A `CLEAR` verdict + describes the checks that were run at scan time; it is not a guarantee + against future rug pulls, and memecoins remain extremely high risk + regardless of verdict. + +## Review Preference + +Pulse Token Safety favors inspectable proof over claims: real, reproducible +API calls against its live production endpoint, and an honest account of its +ACP sandbox job history (including a demonstrated rejection), rather than +marketing claims about accuracy. diff --git a/showcase/rail20/README.md b/showcase/rail20/README.md new file mode 100644 index 0000000..7341ad4 --- /dev/null +++ b/showcase/rail20/README.md @@ -0,0 +1,38 @@ +# RAIL20 + +Private payments for onchain agents. A multi-chain ZK privacy pool live on Base, Robinhood, and Arbitrum. + +## What it does + +RAIL20 gives autonomous agents a shielded balance and a private send path. Deposit into a Poseidon commitment tree, transact anonymously, and settle to any recipient with relayer-paid gas so the destination address never sees the funding wallet. Groth16 proofs are generated client side and verified on chain, so no operator, indexer, or third party can link sender to recipient. + +## Why it fits the Community Showcase + +RAIL20 turns "private payments" from a wallet-only concept into an agent-first primitive. Any ACP agent, custom skill, or headless bot can shield an incoming payment and settle it to a fresh recipient in one transaction, with the relayer paying gas so the agent needs zero native token on the destination chain. Balances stay off explorers, sender and recipient stay unlinked, and every transfer is verified on chain by a Groth16 verifier contract. + +## Live chains + +- Base (chain id 8453) +- Robinhood (chain id 4663) +- Arbitrum (chain id 42161) + +Next: BNB Chain, Ethereum mainnet. + +## Primitives + +- `wallet` - non-custodial shielded balance managed via Groth16 note commitments +- `token` - $RAIL20, tokenized on Virtuals + +## Links + +- App: https://app.rail20.org +- Landing: https://rail20.org +- Docs: https://docs.rail20.org +- On-chain analytics: https://dune.com/rail20_team/rail20-private-payments +- $RAIL20 on Virtuals: https://app.virtuals.io/virtuals/104542 +- Protocol spec and source: https://github.com/rail20dev/protocol +- Demo video: https://youtu.be/ggSAIQGB1Bo + +## Builder + +rail20dev - https://github.com/rail20dev diff --git a/showcase/rail20/assets/poster.jpg b/showcase/rail20/assets/poster.jpg new file mode 100644 index 0000000..bfffd2e Binary files /dev/null and b/showcase/rail20/assets/poster.jpg differ diff --git a/showcase/rail20/showcase.json b/showcase/rail20/showcase.json new file mode 100644 index 0000000..22e1a99 --- /dev/null +++ b/showcase/rail20/showcase.json @@ -0,0 +1,108 @@ +{ + "slug": "rail20", + "title": "RAIL20", + "tagline": "Lets onchain agents transact privately, shielding balances and settling payments across chains without exposing wallets", + "description": "RAIL20 is a multi-chain ZK privacy pool that gives autonomous agents a shielded balance and a private send path. Agents deposit into a Poseidon commitment tree, transact anonymously across Base, Robinhood, and Arbitrum, then settle to any destination with relayer-paid gas so the receiving address never sees the funding wallet. Groth16 proofs are generated client side and verified on chain, so no operator, indexer, or third party ever holds the link between sender and recipient.", + "status": "live in production", + "topic": "commerce", + "topics": ["commerce", "privacy", "payments", "zk", "agents"], + "builder": { + "name": "rail20dev", + "url": "https://github.com/rail20dev" + }, + "links": { + "repo": "https://github.com/rail20dev/protocol", + "demo": "https://app.rail20.org", + "video": "https://youtu.be/ggSAIQGB1Bo", + "share": "https://x.com/railB20/status/2076550084221919520", + "feedback": "https://github.com/rail20dev/protocol/issues/new?title=Feedback%3A%20RAIL20&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20Ready%20to%20plug%20into%20my%20agent%0A-%20Docs%20need%20clearer%20setup%0A-%20Support%20more%20chains%0A-%20Support%20more%20tokens%0A%0ANotes%3A%0A" + }, + "primitives": ["wallet", "token"], + "visual": { + "kind": "product demo video", + "eyebrow": "private payments", + "title": "shielded agent settlement", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/rail20/assets/poster.jpg", + "videoLabel": "Watch the demo on YouTube" + }, + "skills": [ + { + "name": "rail20-private-payments", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/rail20/skills/rail20-private-payments", + "sourcePath": "showcase/rail20/skills/rail20-private-payments", + "summary": "Send private payments between onchain agents on Base or Robinhood via the RAIL20 ZK privacy pool. Shield balance, send anonymously with relayer-paid gas, and audit outcomes. Includes drop-in operating policy for caps, allowed recipients, and gas floor.", + "install": "cp -R showcase/rail20/skills/rail20-private-payments ~/.agents/skills/\ncp -R showcase/rail20/skills/rail20-private-payments ~/.claude/skills/" + }, + { + "name": "rail20-private-swap", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/rail20/skills/rail20-private-swap", + "sourcePath": "showcase/rail20/skills/rail20-private-swap", + "summary": "Perform private same-chain swaps between ETH and the local stablecoin (USDC on Base, USDG on Robinhood) via RAIL20's fresh-burner Uniswap V3 flow. Documents the full 6-step burner lifecycle plus idempotent recovery for any stranded funds.", + "install": "cp -R showcase/rail20/skills/rail20-private-swap ~/.agents/skills/\ncp -R showcase/rail20/skills/rail20-private-swap ~/.claude/skills/" + }, + { + "name": "rail20-cross-chain-bridge", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/rail20/skills/rail20-cross-chain-bridge", + "sourcePath": "showcase/rail20/skills/rail20-cross-chain-bridge", + "summary": "Move value privately across chains via RAIL20 - direct Base <-> Robinhood router (Relay/Across quotes) or NEAR Intents 1Click to Arbitrum, BNB, and Ethereum. Includes quote-only preview, live execution with SUCCESS/REFUNDED handling, and recovery for stranded burners.", + "install": "cp -R showcase/rail20/skills/rail20-cross-chain-bridge ~/.agents/skills/\ncp -R showcase/rail20/skills/rail20-cross-chain-bridge ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Live app", + "href": "https://app.rail20.org", + "kind": "demo" + }, + { + "label": "YouTube walkthrough", + "href": "https://youtu.be/ggSAIQGB1Bo", + "kind": "video" + }, + { + "label": "Private payments skill", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/rail20/skills/rail20-private-payments", + "kind": "skill" + }, + { + "label": "Private swap skill", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/rail20/skills/rail20-private-swap", + "kind": "skill" + }, + { + "label": "Cross-chain bridge skill", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/rail20/skills/rail20-cross-chain-bridge", + "kind": "skill" + }, + { + "label": "Agent integration docs", + "href": "https://docs.rail20.org/agents", + "kind": "proof" + }, + { + "label": "Operating your agent (policy + failure modes)", + "href": "https://docs.rail20.org/agents/operating", + "kind": "proof" + }, + { + "label": "On-chain analytics (Dune)", + "href": "https://dune.com/rail20_team/rail20-private-payments", + "kind": "proof" + }, + { + "label": "$RAIL20 on Virtuals", + "href": "https://app.virtuals.io/virtuals/104542", + "kind": "proof" + }, + { + "label": "Protocol spec and source", + "href": "https://github.com/rail20dev/protocol", + "kind": "proof" + } + ], + "feedbackPrompts": [ + "Would you route agent payments through a shielded pool like this?", + "Which chain should RAIL20 support next?", + "What onchain agent workflow needs private settlement most?" + ] +} diff --git a/showcase/rail20/skills/rail20-cross-chain-bridge/SKILL.md b/showcase/rail20/skills/rail20-cross-chain-bridge/SKILL.md new file mode 100644 index 0000000..b64183d --- /dev/null +++ b/showcase/rail20/skills/rail20-cross-chain-bridge/SKILL.md @@ -0,0 +1,158 @@ +--- +name: rail20-cross-chain-bridge +description: Move value privately across chains via RAIL20 - either the direct Base <-> Robinhood router (Relay/Across quotes) or NEAR Intents 1Click to Arbitrum, BNB Chain, and Ethereum. Withdraw from a shielded pool on one chain and receive value at any destination address on another, with no observable link between source and destination. +--- + +# RAIL20 Cross-Chain Private Bridge + +## Overview + +Use this skill when an agent needs to move value from one chain's private balance to a destination address on a different chain. RAIL20 supports two bridge paths: + +- **Direct router** (Base <-> Robinhood): the CLI queries competing quotes from Relay and Across, picks the best, and executes an approve + deposit sequence on a burner. Fast, sub-minute settlement. +- **NEAR Intents 1Click** (Base or Robinhood -> Arbitrum, BNB, Ethereum): a swap-and-bridge intent that quotes and settles via a solver. Poll `/api/intent-status` for completion (~1-6 min). + +Both paths route through a fresh random burner so the source pool exit and the destination transfer are on-chain unlinkable. Failure recovery works identically to the private swap skill: run `rail20 recover` to sweep any stranded burner. + +Three modes are supported: + +- **Live execution**: run `rail20 bridge` end to end and poll status until SUCCESS. +- **Quote-only**: return the best available quote for a proposed bridge without executing. +- **Evidence review**: verify a claimed bridge landed at the destination with the expected amount. + +## Mode Selection + +1. Use **live execution mode** when the destination address, amount, source pool, and destination chain are all within the operating policy. +2. Use **quote-only mode** when the user asks for a fee/rate estimate before committing. +3. Use **evidence review mode** when the user provides source-chain tx hashes and destination-chain tx hashes and asks whether the bridge fully settled. + +## Required Rules + +- Only bridge to policy-approved chains and destinations. Never invent a destination. +- Minimum bridge is ~2.02 stablecoin units (fee guard: flat 1 + 0.35%). Never attempt smaller. +- On any error, run `rail20 recover --chain ` before any retry. +- Wait for status SUCCESS before treating a bridge as complete. REFUNDED or FAILED means funds returned to the private balance on the source chain; report and stop. +- Solver capacity can be tight: after a bridge shows SUCCESS, wait at least 60 seconds before the next bridge on the same route. Longer intervals (5 min) if the policy specifies. +- Never bridge more than the per-tx cap. Split large amounts into multiple bridges within the daily cap. +- Robinhood RPC caveat: if `--chain rh` commands fail with "could not detect network", set `RAIL20_ROBINHOOD_RPC` before retrying. + +## Stop Conditions + +Stop and ask the user before proceeding if any of these occur: + +- Destination chain or destination address is not in the policy. +- Requested amount is below ~2.02 (fee guard) or above the per-tx cap. +- Private balance in the source pool is less than the requested amount plus fee. +- Latest bridge on the same route shows REFUNDED or FAILED and the user has not cleared it. +- `rail20 latest` reports a newer version and the policy pins bridge-quote fixes to a specific version. + +## Command Pattern + +```bash +# check supported destinations +rail20 assets + +# quote-only (no execution) +# (via HTTP for now; CLI wrappers may exist depending on version) +curl -X POST https://rail20-api.fly.dev/api/bridge/quote \ + -H "Content-Type: application/json" \ + -d '{"signature":"0x...","fromAsset":"base_usdc","toAsset":"arb_usdc","amount":"5","dry":true}' + +# direct router: Base <-> Robinhood +rail20 bridge rh_eth 0xRECIPIENT 0.01 --from eth --chain base # Base -> Robinhood ETH +rail20 bridge rh_usdg 0xRECIPIENT 3 --from usdc --chain base # Base USDC -> Robinhood USDG +rail20 bridge rbase_eth 0xRECIPIENT 0.01 --from eth --chain robinhood +rail20 bridge rbase_usdc 0xRECIPIENT 3 --from usdg --chain robinhood + +# 1Click intents: out to L1/L2s +rail20 bridge arb_usdc 0xRECIPIENT 2.5 # Base USDC -> Arbitrum USDC +rail20 bridge bsc_usdt 0xRECIPIENT 5 # Base USDC -> BNB USDT +rail20 bridge eth_usdc 0xRECIPIENT 3 # Base USDC -> Ethereum USDC + +# recovery on any bridge failure +rail20 recover --chain base +rail20 recover --chain rh +rail20 recover --chain all +``` + +Raw HTTP flow (both paths): + +``` +# quote +POST /api/bridge/quote { signature, fromAsset, toAsset, amount, dry? } +POST /api/router/quote { signature, fromAsset, toAsset, amount } # Base <-> RH only + +# execute (router path) +POST /api/router/execute { signature, fromAsset, toAsset, amount, recipient } + -> returns ordered txs[] (approve + deposit) for the burner + +# execute (1Click path) +POST /api/swap { signature, fromAsset, toAsset, amount, recipient } + -> returns depositAddress + intentId + +# poll status (1Click) +GET /api/intent-status?depositAddress=0x... -> PENDING | SUCCESS | REFUNDED | FAILED +``` + +## Destination Reference + +| Destination code | Meaning | Source pool typically | +| --- | --- | --- | +| `rh_eth` | Robinhood ETH | Base ETH | +| `rh_usdg` | Robinhood USDG | Base USDC | +| `rbase_eth` | Base ETH | Robinhood ETH | +| `rbase_usdc` | Base USDC | Robinhood USDG | +| `arb_usdc` | Arbitrum USDC | Base USDC or Robinhood USDG | +| `bsc_usdt` | BNB Chain USDT | Base USDC or Robinhood USDG | +| `eth_usdc` | Ethereum USDC | Base USDC or Robinhood USDG | + +Run `rail20 assets` for the authoritative live list. + +## Workflow (Live Execution Mode) + +1. Read the bridge request: source chain, source pool, destination code, destination address, amount. +2. Verify against policy: allowed source, allowed destination, per-tx cap, daily cap, allowed recipient. +3. Confirm private balance in the source pool: `rail20 balance --pool --chain --wait`. +4. Optional: get a fresh quote first to preview fees and effective rate. +5. Execute: `rail20 bridge --from --chain `. +6. For direct router (Base<->RH), the CLI blocks until settlement (seconds to a minute). +7. For 1Click intents, poll `/api/intent-status` until SUCCESS, REFUNDED, or FAILED. Timeout policy: 15 min per bridge unless the user specifies longer. +8. On REFUNDED or FAILED: report to user, run `rail20 recover --chain ` as a safety sweep, and stop. +9. On SUCCESS: log source tx, destination tx (or intent id), amount out, effective rate. +10. If another bridge is queued, wait the policy cool-off (60s minimum) before the next. + +## Workflow (Quote-Only Mode) + +1. Read the proposed bridge parameters. +2. Return a quote via `/api/bridge/quote` with `dry: true` (or `/api/router/quote` for Base<->RH). +3. Present: expected fee, expected output, effective rate, expected settlement time, route (router vs 1Click). +4. Do not execute. Explicitly note the quote is a snapshot and market conditions can shift by the time of execution. + +## Workflow (Evidence Review Mode) + +1. Confirm the source-chain tx: private withdraw from the pool to a burner. +2. Confirm the burner-owned bridge tx: for router, an approve + deposit on the router contract; for 1Click, a transfer to the intent deposit address. +3. Confirm the destination-chain tx: recipient received the expected amount minus fee within slippage. +4. If any step is missing, run `rail20 recover --chain ` and report which step failed. +5. Return `pass`, `fail`, or `uncertain` with the exact missing tx. + +## Final Answer + +In live execution mode, state: +- Whether the bridge SUCCEEDED, REFUNDED, or FAILED. +- Source chain, destination chain, and destination code. +- Amount sent, amount received at destination, effective rate. +- Route used (direct router or 1Click), and the total settlement time. +- Tx hashes: private withdraw on source, bridge tx on burner, destination tx (or intent id and status). +- Any recovery step taken and its outcome. + +In quote-only mode, return the quote block and note that market conditions can move. + +In evidence review mode, return `pass`, `fail`, or `uncertain` with the specific evidence gap. + +## References + +- Agent integration docs: https://docs.rail20.org/agents +- Operating your agent (cross-chain rebalancer policy): https://docs.rail20.org/agents/operating +- Protocol source: https://github.com/rail20dev/protocol +- CLI package: https://www.npmjs.com/package/@rail20/cli diff --git a/showcase/rail20/skills/rail20-private-payments/SKILL.md b/showcase/rail20/skills/rail20-private-payments/SKILL.md new file mode 100644 index 0000000..2d5a90d --- /dev/null +++ b/showcase/rail20/skills/rail20-private-payments/SKILL.md @@ -0,0 +1,151 @@ +--- +name: rail20-private-payments +description: Send private payments between onchain agents on Base or Robinhood using the RAIL20 zero-knowledge privacy pool. Shield balance from public wallet, transact anonymously, and settle to any recipient with relayer-paid gas so the destination never sees the funding wallet. Use for agent treasury management and A2A (agent-to-agent) payments where counterparty and amount must stay off-chain. +--- + +# RAIL20 Private Payments + +## Overview + +Use this skill when an agent needs to hold a private balance and pay other agents or addresses without exposing counterparty, amount, or funding wallet on-chain. RAIL20 is a Groth16 ZK privacy pool live on Base (chain id 8453) and Robinhood Chain (chain id 4663). The agent signs one fixed message; the RAIL20 relayer builds proofs, pays gas, and broadcasts. On-chain, every transfer is a single commitment plus a single nullifier: no sender, no recipient, no amount. + +Three modes are supported: + +- **Live execution**: run the `rail20` CLI directly to shield, check balance, and send. +- **Policy setup**: draft the operational policy an agent will read on start (caps, allowed recipients, gas floor). +- **Evidence review**: verify a claimed private payment succeeded from tx hashes, indexer output, and balance snapshots. + +## Mode Selection + +1. Use **live execution mode** only when `@rail20/cli` (>= latest) is installed, the agent's private key is available via `RAIL20_KEY` or interactive login, and the agent has authorization to move funds within the stated policy. +2. Use **policy setup mode** when the user is preparing an agent for its first RAIL20-backed run and needs the drop-in system prompt plus concrete thresholds. +3. Use **evidence review mode** when the user provides redacted logs, tx hashes, and balance snapshots and asks whether a payment actually landed. + +In policy setup and evidence review modes, do not sign anything, do not issue transactions, and do not ask the user to paste private keys. + +## Required Rules + +- Read the agent's authorized policy first: max per tx, max per rolling 24h, allowed recipients, gas floor, working reserve. If any is missing, ask the user before signing. +- Use the `rail20` CLI (`@rail20/cli`) or the raw HTTP API at `https://rail20-api.fly.dev`. Do not implement custom proof generation. +- Never log or transmit the agent's private key. Only the derived signature is sent to the relayer. +- Never print full note secrets, nullifier preimages, or the raw sign-in signature in the final answer. +- Use `rail20 balance --pool --wait` after any tx that changes balance. Indexer lag is ~5-15 seconds. +- Every `send` must exceed 2x the fee. USDC/USDG floor ~2.01 units; ETH floor ~0.000502 ETH. +- Respect the policy's daily cap and per-tx cap. Never batch around a cap that the user set. +- New recipient (not in the allowlist or not seen in the last 30 days) beyond the policy threshold requires explicit user confirmation before sending. + +## Stop Conditions + +Stop and ask the user before proceeding if any of these occur: + +- Requested amount exceeds the authorized per-tx or per-rolling-24h cap. +- Recipient is not on the allowlist and the amount is above the new-recipient threshold. +- Private balance after `--wait` is less than the requested send plus fee. +- Gas floor on the public wallet is below the policy minimum (deposit or recover flows may fail). +- CLI reports a version older than the one referenced in the policy (`rail20 latest`). +- Robinhood commands fail with "could not detect network" or show `?` for balance while Base works fine (public RH RPC unreachable from the current network; user must set `RAIL20_ROBINHOOD_RPC`). + +## Command Pattern + +```bash +# install and version check +npm install -g @rail20/cli@latest +rail20 --version +rail20 latest + +# auth (either interactive or env var for agents/CI) +rail20 login # prompts for private key, stored chmod 600 +export RAIL20_KEY=0x... # or set env var and skip the prompt + +# balance +rail20 balance # Base by default, both pools +rail20 balance --chain rh # Robinhood +rail20 balance --chain all # every chain, one run +rail20 balance --pool usdc --wait # poll indexer until non-stale + +# shield public -> private (agent wallet pays gas on this step only) +rail20 deposit 50 --pool usdc # 50 USDC on Base +rail20 deposit 20 --pool usdg --chain rh # 20 USDG on Robinhood + +# private send (relayer pays gas) +rail20 send 0xRECIPIENT 25 # Base USDC +rail20 send 0xRECIPIENT 25 --pool usdg --chain rh # Robinhood USDG +``` + +For programmatic control without the CLI, POST to `https://rail20-api.fly.dev`: + +``` +POST /api/balance { signature, address, pool } +POST /api/deposit/prepare { signature, address, amount, pool } -> returns unsigned tx +POST /api/withdraw { signature, recipient, amount, pool } +``` + +Treat the signature returned by `personal_sign` on the fixed RAIL20 message as a session credential. Cache it in memory for the session; do not persist to disk unless you also protect the key file at `chmod 600`. + +## Workflow + +1. Read the user's policy: max per tx, max per rolling 24h, allowed recipients, gas floor, working reserve, chain (Base or Robinhood). +2. Verify CLI is current: `rail20 --version` and `rail20 latest`. Update if out of date. +3. Authenticate: prefer `RAIL20_KEY` env var for agents; use `rail20 login` for interactive setup. Never accept a pasted key over chat. +4. Check both pools' balance: `rail20 balance --chain all --wait`. +5. If private balance is below the working reserve, shield from public wallet: `rail20 deposit --pool `. Wait for confirmation. +6. Validate the requested send against policy: per-tx cap, daily cap, recipient allowlist, minimum viable amount (> 2x fee). +7. If any policy check fails, stop and ask the user. +8. Send: `rail20 send [--pool ...] [--chain ...]`. +9. Poll balance with `--wait` and confirm the delta matches (amount + fee). +10. Log the tx to the agent's local audit file: tx hash, recipient, amount, pool, chain, timestamp. + +## Policy Template + +Drop this into the agent's system prompt (edit the bracketed values): + +``` +# RAIL20 PRIVATE PAYMENTS POLICY +- Chain: [Base | Robinhood] +- Max per tx: [25] USDC (or USDG) +- Max per rolling 24h: [100] +- Allowed recipients: [invoice-driven only | 0xA1.., 0xB2.., ...] +- New recipient > [10] requires explicit user OK +- Working reserve (private): >= [5] units +- Public gas floor: >= [0.01] ETH +- Heartbeat interval: [30] min +- Audit log path: [~/.agent/rail20-audit.jsonl] + +# RULES +- Always use `rail20 balance --wait` after any tx that changes balance +- On any error, stop and report; do not retry blindly +- Never send below 2x the fee (~2.01 USDC or USDG floor) +- Report all txs to the audit log +``` + +## Evidence Review Workflow + +Use evidence review mode when the user provides redacted proof from a private payment run. + +1. Confirm the presence of a `rail20 send` invocation with a matching amount, pool, and recipient hash in the log. +2. Require a private balance snapshot (`rail20 balance --wait`) before and after the send. The delta should equal amount + fee (~0.35% + flat). +3. Verify a matching relayer tx on-chain (Basescan or Robinhood explorer) at the reported block height. The tx should be `transact()` on the RAIL20 verifier, not a plain ERC-20 transfer. +4. Screenshots alone are insufficient. Require the tx hash. +5. Return `pass`, `fail`, or `uncertain` with the exact missing evidence. + +## Final Answer + +In live execution mode, state: +- Whether the payment succeeded. +- Amount sent, fee paid, and post-tx private balance. +- Recipient (mask the middle bytes: `0xabcd...1234`). +- Chain, pool, and tx hash of the relayer's on-chain broadcast. +- Any policy warning (approaching cap, new recipient, low reserve). + +Do not print the sign-in signature, private key, or full note secrets in the final answer. + +In policy setup mode, return the filled-in policy block and the drop-in system prompt. + +In evidence review mode, return `pass`, `fail`, or `uncertain` with the exact evidence gap. + +## References + +- Agent integration docs: https://docs.rail20.org/agents +- Operating your agent: https://docs.rail20.org/agents/operating +- Protocol source: https://github.com/rail20dev/protocol +- CLI package: https://www.npmjs.com/package/@rail20/cli diff --git a/showcase/rail20/skills/rail20-private-swap/SKILL.md b/showcase/rail20/skills/rail20-private-swap/SKILL.md new file mode 100644 index 0000000..f9d7ba3 --- /dev/null +++ b/showcase/rail20/skills/rail20-private-swap/SKILL.md @@ -0,0 +1,138 @@ +--- +name: rail20-private-swap +description: Perform private same-chain swaps between ETH and the local stablecoin (USDC on Base, USDG on Robinhood) using RAIL20's burner-wallet routing. The relayer withdraws private funds into a fresh random burner, runs a Uniswap V3 trade, and re-shields the output. Observers see one commitment out, an unrelated burner trading, and one commitment in - nothing links them to the agent. Includes automatic burner recovery on any failure. +--- + +# RAIL20 Private Swap + +## Overview + +Use this skill when an agent needs to rotate between ETH and the local stablecoin without either side of the trade linking back to the agent's public wallet. RAIL20 handles the full 6-step burner flow atomically from a single CLI command. If any step past the private withdraw fails (RPC glitch, gas spike, indexer stall), the funds sit on a recoverable burner and can always be swept back with `rail20 recover`. + +Three modes are supported: + +- **Live execution**: run `rail20 swap` with the specified direction, amount, and slippage. +- **Recovery**: sweep stranded burners after a prior failed run, from any device with the sign-in key. +- **Evidence review**: verify a claimed swap fully landed (withdraw, trade, re-shield) from tx traces and balance snapshots. + +## Mode Selection + +1. Use **live execution mode** when the CLI is installed, the agent is authenticated, and the swap direction, amount, and slippage are within the operating policy. +2. Use **recovery mode** any time after a failed swap, or as a scheduled sanity sweep. Recovery is idempotent and safe to run on any schedule. +3. Use **evidence review mode** when the user provides tx hashes and balance snapshots and asks whether a swap actually completed end-to-end. + +## Required Rules + +- Only swap direction supported today: ETH <-> the chain's stablecoin (`usdc` on Base, `usdg` on Robinhood). Do not attempt cross-token pairs on-chain; use the bridge skill instead. +- Default slippage is 100 bps (1%). Do not exceed the policy-defined slippage cap. +- After every swap failure, run `rail20 recover --chain ` before any retry. Never leave funds on a burner. +- Do not attempt swaps below the fee guard. Minimums: ~2.01 stablecoin or ~0.000502 ETH. +- If Robinhood commands fail with "could not detect network", set `RAIL20_ROBINHOOD_RPC` before retrying. Do not hammer the same failing endpoint. +- Never log the burner's private key. The CLI encrypts it with AES-256-GCM under the sign-in signature and registers the ciphertext before funding; that is the only key material that should ever leave the process. + +## Stop Conditions + +Stop and ask the user before proceeding if any of these occur: + +- Requested slippage exceeds the policy cap. +- Requested amount is below the fee guard (2x the flat fee). +- Requested amount is greater than the private balance in the source pool. +- A prior swap in the current session ended in error and `rail20 recover` has not been run. +- The CLI is out of date (`rail20 latest` reports a newer version) and the policy pins gas fixes to a specific version. + +## Command Pattern + +```bash +# check readiness +rail20 balance --chain base --wait +rail20 latest + +# same-chain private swaps (Base) +rail20 swap 3 --from usdc # 3 USDC private -> ETH private +rail20 swap 0.001 --from eth # 0.001 ETH private -> USDC private +rail20 swap 3 --from usdc --slippage 50 # tighter slippage (0.5%) + +# Robinhood variants +rail20 swap 3 --from usdg --chain rh +rail20 swap 0.001 --from eth --chain rh + +# recovery (idempotent - safe on any schedule) +rail20 recover # sweep Base burners +rail20 recover --chain rh # sweep Robinhood burners +rail20 recover --chain all # sweep both chains in one run +rail20 recover --to 0xOTHERADDR # sweep to a different destination +``` + +Programmatic control (skip only if you understand all 6 sub-steps): + +``` +POST /api/burner/auth-message -> message to sign for registry auth +POST /api/burner/register { authSig, burnerAddress, chain, token, encKey } +POST /api/swap-private { signature, fromAsset, amount, burnerAddress } +POST /api/burner-gas { signature, burnerAddress, chain, txCount } +POST /api/burner/list { authSig } +POST /api/burner/mark-swept { authSig, burnerAddress, chain } +``` + +## The 6-Step Burner Flow (why this matters) + +Every private swap routes through a fresh random burner wallet so observers cannot link the pool exit and pool re-entry to the same agent. The CLI orchestrates all steps; failures at each step have specific recovery paths. + +1. **Create burner** (random Wallet in memory) and encrypt its key with AES-256-GCM under `keccak256(signInSig)`. +2. **Register ciphertext** to the recovery registry BEFORE funding, so any device that re-signs can decrypt and sweep it later. If this write fails, the CLI aborts before funding (funds cannot be stranded on an unregistered burner). +3. **Private withdraw** from the pool to the burner (relayer-built proof). +4. **Gas top-up** for stablecoin-origin swaps: the relayer sends ETH to the burner sized for the exact tx count needed (approve + swap, or approve + deposit). +5. **Uniswap V3 trade** from the burner: approve + swapExactTokensForTokens (or WETH unwrap for ETH destination). +6. **Re-shield**: burner deposits output back into the RAIL20 pool. The re-shield tx verifies a Groth16 proof on-chain and uses ~1.35M gas; the CLI adds a 20% buffer to `estimateGas`. + +If any step 3-6 fails, funds sit on the burner. `rail20 recover` re-signs, pulls the registry, decrypts each burner key locally, and sweeps all USDC/USDG plus any non-dust ETH back to the agent's public wallet. + +## Workflow (Live Execution Mode) + +1. Read the swap request: direction (from asset), amount, chain, slippage. +2. Verify against policy: max swap size, allowed slippage, allowed chains. +3. Confirm private balance in the source pool: `rail20 balance --pool --chain --wait`. +4. Verify the source balance is at least the requested amount plus fee. +5. Run the swap: `rail20 swap --from [--chain ...] [--slippage ]`. +6. Poll the resulting private balance with `--wait` on the destination pool. +7. If the swap errors at any step, immediately run `rail20 recover --chain ` and report the sweep tx hash. +8. If `rail20 recover` also reports errors, stop and escalate to the user with the burner address and last successful step. +9. Log the swap: source amount, destination amount, effective rate, slippage used, tx hash of the re-shield. + +## Workflow (Recovery Mode) + +1. Run `rail20 recover --chain all` (idempotent; safe to schedule). +2. Parse output for swept burners and their recovered balances (USDC/USDG + ETH). +3. If any burner is reported as "registered but no funds", it was cleaned by a prior sweep. Ignore. +4. If any burner is reported as "registry entry not found", check whether it was a legacy burner (derived at nonce 0-19 in older CLI versions); those are still swept. If none, funds may be truly lost - escalate. +5. Log the recovery outcome to the agent's audit file. + +## Workflow (Evidence Review Mode) + +1. Locate the swap invocation and its logged tx hashes: the private withdraw, the Uniswap trade, and the re-shield. +2. Confirm the private withdraw appears as `transact()` on the RAIL20 verifier at the expected block. +3. Confirm the Uniswap trade appears on a burner wallet address (never the agent's public wallet). +4. Confirm the re-shield appears as another `transact()` moving the traded output back into the pool. +5. Confirm the destination-pool private balance grew by the expected amount (within slippage). +6. If any step is missing, run `rail20 recover` (still safe) and report which step failed. +7. Return `pass`, `fail`, or `uncertain` with the specific missing tx. + +## Final Answer + +In live execution mode, state: +- Whether the swap succeeded end-to-end. +- Source amount and destination amount. +- Effective rate and slippage applied. +- Tx hashes: private withdraw, Uniswap trade (on burner), re-shield. +- Whether `rail20 recover` was needed post-run and its outcome if so. + +In recovery mode, list swept burners and total recovered per asset. + +In evidence review mode, return `pass`, `fail`, or `uncertain` with the exact missing tx or balance mismatch. + +## References + +- Agent integration docs: https://docs.rail20.org/agents +- Operating your agent (failure modes): https://docs.rail20.org/agents/operating +- Protocol source: https://github.com/rail20dev/protocol +- CLI package: https://www.npmjs.com/package/@rail20/cli diff --git a/showcase/rootai-market-intelligence/README.md b/showcase/rootai-market-intelligence/README.md new file mode 100644 index 0000000..3dc8c2e --- /dev/null +++ b/showcase/rootai-market-intelligence/README.md @@ -0,0 +1,46 @@ +# rootAI Market Intelligence + +rootAI provides graded Hyperliquid signals through ACP. + +The live catalog includes: + +- `rootai_pro_signals`: paid recent or single-market signals +- `rootai_signals_pass`: 30-day subscription +- `rootai_free_mcp`: public MCP connection details +- `rootai_capabilities`: machine-readable product information + +## Provider + +- Agent: https://app.virtuals.io/virtuals/92029 +- Provider: `0x9d23ddd0a527b3b63927956840c8ff35b9db95a6` +- Network: Base (`8453`) +- Token: `0xe96301023608C61E0191Cd4ced0a5Cd767ed4Da8` + +## Requirements + +Recent signals: + +```json +{"mode":"recent","limit":3,"min_grade":"B"} +``` + +One market: + +```json +{"mode":"by_coin","coin":"BTC"} +``` + +Optional fields are `asset_class` and `min_grade`. Recent mode returns up to 10 signals. By-coin mode returns one. + +## Deliverable + +The structured deliverable contains a `signals` array. Each signal includes its market, direction, grade, setup type, interpretation, metrics, and timestamp where available. + +The signal engine is deterministic. No private rootAI service code is included in this package. + +## Links + +- MCP: https://mcp.rootedge.ai/mcp +- MCP Pro: https://mcp.rootedge.ai/pro +- Docs: https://rootai.gitbook.io/docs/mcp/build +- Website: https://rootedge.ai diff --git a/showcase/rootai-market-intelligence/assets/poster.jpg b/showcase/rootai-market-intelligence/assets/poster.jpg new file mode 100644 index 0000000..0f89347 Binary files /dev/null and b/showcase/rootai-market-intelligence/assets/poster.jpg differ diff --git a/showcase/rootai-market-intelligence/assets/poster.svg b/showcase/rootai-market-intelligence/assets/poster.svg new file mode 100644 index 0000000..09bb2e6 --- /dev/null +++ b/showcase/rootai-market-intelligence/assets/poster.svg @@ -0,0 +1 @@ +AABCHWp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAAEH3anVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46YzJwYTo5N2M5OTMyNi0yYzczLTQ2OGQtOThhZC0wZmFlYmY1ZTAxMDAAAAAB5Gp1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAAO5qdW1iAAAAQWp1bWRjYm9yABEAEIAAAKoAOJtxE2MycGEuYWN0aW9ucy52MgAAAAAYYzJzaNwKQ7rH7r9fEIGCW67HdGwAAAClY2JvcqFnYWN0aW9uc4GjZmFjdGlvbmxjMnBhLmNyZWF0ZWRtc29mdHdhcmVBZ2VudGhDYW52YSBBSXFkaWdpdGFsU291cmNlVHlwZXhTaHR0cDovL2N2LmlwdGMub3JnL25ld3Njb2Rlcy9kaWdpdGFsc291cmNldHlwZS9jb21wb3NpdGVXaXRoVHJhaW5lZEFsZ29yaXRobWljTWVkaWEAAADFanVtYgAAAEBqdW1kY2JvcgARABCAAACqADibcRNjMnBhLmhhc2guZGF0YQAAAAAYYzJzaMM8jVTrwQZ8H4AVJQ3tvyYAAAB9Y2JvcqVqZXhjbHVzaW9uc4GiZXN0YXJ0GQEUZmxlbmd0aBlYKGRuYW1lbmp1bWJmIG1hbmlmZXN0Y2FsZ2ZzaGEyNTZkaGFzaFggyl19QMnaRptP0i4nDXWJawd+jbyEPZ5ge4c6yGQ5wTVjcGFkSQAAAAAAAAAAAAAAAgtqdW1iAAAAJ2p1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0udjIAAAAB3GNib3Knamluc3RhbmNlSUR4LHhtcDppaWQ6ZjdhMmRmMGEtZjkyMy00ZTcwLWJlY2YtZWU4NDljMzkxYWZidGNsYWltX2dlbmVyYXRvcl9pbmZvo2RuYW1lZ2MycGEtcnNndmVyc2lvbmUwLjAuMHdvcmcuY29udGVudGF1dGguYzJwYV9yc2UwLjAuMGlzaWduYXR1cmV4TXNlbGYjanVtYmY9L2MycGEvdXJuOmMycGE6OTdjOTkzMjYtMmM3My00NjhkLTk4YWQtMGZhZWJmNWUwMTAwL2MycGEuc2lnbmF0dXJlcmNyZWF0ZWRfYXNzZXJ0aW9uc4GiY3VybHgpc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5oYXNoLmRhdGFkaGFzaFggEhpxglm0q+ghpMFfQGFPfor/pBnf5fvoYxUYVJ2TP3dzZ2F0aGVyZWRfYXNzZXJ0aW9uc4GiY3VybHgqc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5hY3Rpb25zLnYyZGhhc2hYIPHg0lLn4DxajXeHvUmT2JyQDWc0CvdplYi9Yh0NP3FFaGRjOnRpdGxlZTEuc3ZnY2FsZ2ZzaGEyNTYAAD25anVtYgAAAChqdW1kYzJjcwARABCAAACqADibcQNjMnBhLnNpZ25hdHVyZQAAAD2JY2JvctKEWRKBogE4Jhghg1kFPzCCBTswggMjoAMCAQICEQCbRnCv4i1hyLapDvej8NKXMA0GCSqGSIb3DQEBDQUAMCIxIDAeBgNVBAMTF1NpZ25pbmcgSW50ZXJtZWRpYXRlIENBMB4XDTI2MDYyOTIzNTEzMVoXDTI2MDcwNzIzNTEzMVowODEOMAwGA1UEChMFQ2FudmExDjAMBgNVBAsTBUNhbnZhMRYwFAYDVQQDEw1DYW52YSBTaWduaW5nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxh7+0wxXNo0d79W86iuymggbIujY7L8Ye2OCZyWG1T/FtsM6Dfcr5hMoK9v2wg/iPeAWE0twJZUbEjQUbQMtWTaknyWTrQJ1+/1SiSzo7r0AZz8aYmGNtYmSyle/A2EIr5c8YZVT1ktmxWmhRscENbHgGRqM/DpDC3wy27gN0oCjXjHOAP5tVogzAebkXJNCsR7/zKkUkXb9NWCq2Ktc4nRVj931rs7Vns7jnXGkh43YPhLfjTiHulO0T1C7FxAeUlfGnmOAfZX1e3TaOCd81g+DOhHv5de/Uhcf/RYJV7BsXxQ9esLEiLJ7PZCuMde61/Y/9z1rKtPKKfykgT9ODzCrr6RtbzBaz/tcKFWAi809xl/OhrHfFKX+UszyqqYvyoFN/SCF/RPg6qPCJoXi/7j5wA1GW28SpbCt2KMG/ALO8Hf0xqa+WPySGbPhhzGhu+aropR31SN/x7wdV/8UNTkgticyek+hfDIXgOHklWIAvlNMfmphU6njBfMn05nQLXE1WkzPtTzaeZTBPqxTYuYMhhl1kSF8xzYIJvK1as0g2K7jo/KMs4wN2hMvKU4NptJG45O0CXN0gkN/Mes/7qDqOQe0yBNJfN1A78V6krkeMHEV6o7v4ipBMXqyOrjQoIAmNmcTCxWjFGbvKQQ5lVW375pE/4N4mWYbSLGJn5sCAwEAAaNWMFQwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMEMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUh2hBSZDwcjTmIPlK0jt3jS8EqGIwDQYJKoZIhvcNAQENBQADggIBABfP3rRCXV1JHHkumVQyReYVSqnImZmOQskg3kbP5qSeK0JKmR4DtioEFmta8GYpwuICKJepz6vlLolk7PqUyiOqAP8Olc+uB1X3X7PWA4yLyOVzQ/hXWyvtQgk4zEATFG8oQM7d8c1UIKt1ed9qARA5wiWvv64UPFS/qchvuP/Yms8t2cOzHfzgdltaq8V/C1U0JvJlt8alSYlYVeBaXZ9jvCg46yXtviNoMpjwd5iauRqJ9Q31UVzxdFFrfZFQrtKTPx94iI0I78+7wOuyFDwUSp2HPStiV/GUsRC4a+cXplCMpq6eCp5GsT85NpDD5qbqTuem6rcZt9GqicwK7nXwpva5VRhmgJ4ISoTtZ45vkW7AsDi7s7T7BIhqxH2UD3XWeLVpPhEqpYp26F+8crIiXSzbN6nRcoTxtaUcTJpY1Kb/XpxGQqV2F/V5+oFCoMLqtvlUhW6Fr0u86+yqTzuMmupWGXbjG8WfQnuNVhj4d+gMpD85hwl9gqWmqRTbassdhTjGLR39waoNa3RleyJzLEtyrZC3xs4DhdutnEczv2siKTNCOcm+r9X2yU9Lc3zqfupev8BFRjdfUCX5CfIewUXj8J02zRa3NZkL4//dKsLrgqsvGfe7pDMZDIm1UeN1qii94HD231OJYH1vz4SBpVKQo5TD+uCYn0VU2/OaWQboMIIG5DCCBMygAwIBAgIUW4X+dJv43+Wr38/Shh5kX/TZ+QQwDQYJKoZIhvcNAQELBQAwbTELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UEBxMGU3lkbmV5MRMwEQYDVQQKEwpDYW52YSBQcm9kMRAwDgYDVQQLEwdTaWduaW5nMRgwFgYDVQQDEw9TaWduaW5nIENBIFByb2QwHhcNMjYwNjE2MDUzODA3WhcNMjYwNzE2MDUzODM3WjAiMSAwHgYDVQQDExdTaWduaW5nIEludGVybWVkaWF0ZSBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAN2Gt0HrI3hbc4FN6p2upe9k5/uklDm7laMewQLu86nq6XLwdOu3znb35LvtEOfORBMebDvGTdTOXqmD8aPQ6MzOErvBeZVuGWTH8v9hj6sWxSMZmjMJz/ipKu7LK/f1Djf5lyd4+VfA1XJRlV0vyRfZQT1YKilAT7EfrUqQbiEzt3Ani7TNEhPhf9BCNH3Y3l5ofoPN5CXDwIihVsn3pmxy5++R/EaKTZguVew0E1DVAN9/eDJz9n6R9tww/DyyIw3cnYDz2sdbEloSLOII94iKTpG8DlabAA7ZVLHFFCBU341EiBgSP4aqFRcfBLNcoKA59GL+9Rw1XmmnuTgry0p4xIiKGX+6OR04VQBLdG9iIreSeG4r7o7LYOxJe4qnfJ5r0NHpKu+F1ILPB8xr4wiWcNgSdXyIUhfputdbYiXRguRQ93abHJ8E2lIb28KJ94TwvzXFlAN1xyjtStXDUVhM4YzZ/OV47LZyV9zZClaMTa+Vp9aE+BQDBIx1DXhuhL5GJsXL+O1WMkTG3CoH8CbXfrmM5DglHzGs6p5UxOBy3NF+D8bh8jenWFzhQHs+FBYmWxuAtz9wN4k1D7Eh901itEs9WEu7TYVrZcBPLdHgfUqWWeU6Jsogvbl+Z41XFTLua9GohLsHX+0778X2WkYiI8DGXLwt27OwU4zx+FOVAgMBAAGjggHFMIIBwTAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUh2hBSZDwcjTmIPlK0jt3jS8EqGIwHwYDVR0jBBgwFoAUSlxt/ndQaL1jKWFk7YbiIcsE4x0wgdcGCCsGAQUFBwEBBIHKMIHHMEwGCCsGAQUFBzABhkBodHRwczovL3BraS5jYW52YS1pbnRlcm5hbC5jb20vdjEvcGtpL2NhbnZhLXByb2QvbDEvc2lnbmluZy9vY3NwMHcGCCsGAQUFBzAChmtodHRwczovL3BraS5jYW52YS1pbnRlcm5hbC5jb20vdjEvcGtpL2NhbnZhLXByb2QvbDEvc2lnbmluZy9pc3N1ZXIvMWRjNDVjODQtNGY5My1lMTBlLTRjMzYtZTdkZWFhMjU2MzkyL2RlcjCBgAYDVR0fBHkwdzB1oHOgcYZvaHR0cHM6Ly9wa2kuY2FudmEtaW50ZXJuYWwuY29tL3YxL3BraS9jYW52YS1wcm9kL2wxL3NpZ25pbmcvaXNzdWVyLzFkYzQ1Yzg0LTRmOTMtZTEwZS00YzM2LWU3ZGVhYTI1NjM5Mi9jcmwvZGVyMA0GCSqGSIb3DQEBCwUAA4ICAQDJo277hHOR+ijuxbhboRgZuonvVIDc324gWcpSwJcmV2O4h3E7jN5+kQCdITp5BsYgh/PgXuSpg7W/825E/1/AwulqiV3L5v8UIyRc24tpW8cMsBgAYEZat0SEekKXb0UnebEdbjj/AggHAIWqRHzHn0C0WBa6XhI2l/6GNGciroYay1fG6XydeivH1uAVBnG4bcxHqHPOsGQJfaAWD9pcD+fbDL4uR3oka8xa6GKpKt7jHaBlh9iiOL9pcgE8svtaqrJHBuJVT0bMawVD80+Q/VvRRWAMQFUKT/TAmxYbvlxOjmzHWOd0FMXShAKA70FHZmDdGnTM2JIWdliUwgy/LlpcYbIAObLxWDJqLhK0Ssjx7hD+YA9f036yWQiMqw7IBWkYUJuH1EpCcaDLdV1zQKIyUbWnQIgGx5gGWbNR2NTXcXrYSPtbC18vSF9bek2l69nvQHpVU5fkYmj9CXBWrO4OHRmnPVVY+HXGUL4wqvHBoL3MSE2sanjB6yVghS9FiYeEXRt5r3SbDnoLvJw18ifDlquuQA0RoWIvPDrHuKxRyorje4kBhwWzjQn/glb13btbU9kuxtIngmAfsa7g3R6MjBBQXa62WH8BJyeX6xpmS3MNgjlo9M9Kcq9PyNXMUlhEQuwMfNY9+6fduu8yPFRYIaCNeLCf7WLuag4nyFkGSjCCBkYwggQuoAMCAQICEQDJtWzGUz0MB/MhdTvrUGHhMA0GCSqGSIb3DQEBDQUAMIGBMQswCQYDVQQGEwJBVTETMBEGA1UECgwKQ2FudmEgUHJvZDEXMBUGA1UECwwOQ2xvdWQgUGxhdGZvcm0xDDAKBgNVBAgMA05TVzElMCMGA1UEAwwcQ2FudmEgUHJvZCBSb290IENBIEdsb2JhbCBHMzEPMA0GA1UEBwwGU3lkbmV5MB4XDTI2MDUxNTAzMTI1NVoXDTI2MTExMTAzMTI1NVowbTELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UEBxMGU3lkbmV5MRMwEQYDVQQKEwpDYW52YSBQcm9kMRAwDgYDVQQLEwdTaWduaW5nMRgwFgYDVQQDEw9TaWduaW5nIENBIFByb2QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQD0MHZe4JEmHf4WkG5+tA+VwWcyYzsYw19aFaMnh/QZp8Awkci8GjqGdN09zzOrYEp6k9nAXTT0Y7H3tIUhpDIWy0jqysBROIYvKdGkz5Lp4MH3hB6nXnIiLwXpHfq/O95Cs8Q7wrmMoB1DPMLXBNfA/qRFqQeGVgL2UlxPchxkM1l1jEhSftxgZ09KpYFqcLeDEyPB2+nrfsX9qGuYLnmJOEZs1EtmLa5qjqbdGp7mlO8QawHQUmZ2QsD7YJDoSb7wU3XOHn/6h7ps5rVhTK516ryakEKf9P109m376yyUoyfGxqQD5E4KtlaTVsH0AWon+L3Mo0cpn/QxLikF+2iR2RGxyVsMUaQLwW0UXWlOw471623wcrsvkGZ+Ys40G+QOBrIHa/bq0cA6HZvxSNQt6I7+M0GDbGz2kdD3CobNaXXNGFt4y+qXjoMbx/NLfMhC5+frDobUF8RUSkCQ+Snzi95PibxJFHaInji8LrjueMnunf851089syyrXNqBaFQbyY/1tkESEeQmFudPrDl7qwh6sHSzWLOzrmdKZzJd49+ABzlAcOGU+b6l33JyP7TDzxGsZMRyQaEeQaYELwPIeENs1cIfFRLVlfQimCg0J6db37RK7GIG4z/yrvMbz9vnkk9ckzBF+3CzTtT8z/mtinltewY4QDpPdCfbPa/3fQIDAQABo4HLMIHIMBIGA1UdEwEB/wQIMAYBAf8CAQEwHwYDVR0jBBgwFoAUuzhkeWmWJGuw/pESspQ08s+cY4swHQYDVR0OBBYEFEpcbf53UGi9YylhZO2G4iHLBOMdMA4GA1UdDwEB/wQEAwIBhjBiBgNVHR8EWzBZMFegVaBThlFodHRwOi8vZDF1YWRuMHBqc2loOG0uY2xvdWRmcm9udC5uZXQvY3JsLzVlNDliYWRkLTBhNjYtNGYzYS1iM2NmLTFhNGZkMGY2MzVkMS5jcmwwDQYJKoZIhvcNAQENBQADggIBAKqOtwanq6lfW6XuFzlw/EJHJ2wVi9XY9dTjNZSPlIr3fnJ2qz2JRZ3f5VCa/+TmA9p2wv5aBNKSJyN4/uX1/qPxFunqykKFx3mNDD09JmxMEA1kTsjEePbhf5eBBlw2yg4lC3X+E/PTnx8VXTjwbG9jcaNGbJrD1/SZtYgjbti8YKnZMOeMh93wtv6NttshgFRcxUmS6sbvvhcAKi3U/YsMnbtSBJJH1dirr6rQuGSaghdLbxawcLd1OfMnw8aKe+G+4TOuKjQ10dwvMKwlBRTcnNJuun+vBvPujqfNmt5mc+0Hu2pDwtHB+LbmpC+hx2ksSmgTCLMjC+oiJUeAFnq1EVJlB++nqKzC4uD3XayTG1jvZ8JlJeZwdCIbo16DWmDP5eUrLiDJ8t/cR46hWzlmk549H1f9UZqiTMCyODjrnO0xbwm9yynPJCMvcN98sqWjOGSWQQPUXAqsMdNMzm1hWdjNzxntt5q9uYRa9rvYcmeu8M9Ek8Lz1sWoEaVcpkdI/XOZBY1nJC/xItlK22e39DS/JQibDVZgKsfkOkZ+tXn3rlD7zxpuJMvUtBmRnhA3iqZxa50+XEk1P2fxX2Ke4A025hkk4oVJY2+JZjWgD1qYvXZ5WRHZSU8SMiwx1iiw2PXcF8H/yEeEisZlIGM21Lc4gzhUmRLxrrTsDbbvoWNwYWRZKO8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2WQIAP4aeuHbgRc4zPJ6Y+dnV12yga09TG7y2Xr8ObLpKUMzzILDtj2MYp7CjnBoOXvsKNs1InUzkomtBTI+1wSRbYbBqSDSal07IyZ905TRvwY7/aWBJGdReKZ0eue3EJmcbOhUSdbEkrfBBd51AtCDFfokCkUkjvLL3QPTXRxVFnXCN0o1WtgTEdhH0FtWoieBdMwmmmaDEa6No7rFIX4bz1jRzV0oDuaHLvyjuvgaEAHrAS8SliRM/rK6aFPbohwpu2G6Z86B34f9KiWAhyM9vX5ufJI6Dc86wQBGo9o2BFEVtLct3RT2rUgqdG6potSPIUE5mtCsH8pKRMleTkPtpps3y5a3CJj2d6QJ2hI180VbnM30redyxoYBnPqcIDdRbkQbA8AaY7Yl13xfE99/dr9+b419cu1yAI1AyXyQubmBCV8Y++2tLdKSREm11oAo8Lf1+/Q7mHIxyTAxsHK/uDQ0K+AoFQ/18gRpHWvxvSoz4Xcta+UL7rzRzXfiskKheCwqsgZg2Mov2hPeUIhUtWxa/KNKNAR/vGfWJzB4IaqfFWWkd0OhFyoDMuZJuZxJ4fzdnaJbd3aJ6i2rK2HVvYyNcydmJJPbrtD9QzmDOtHn08kAt6DzWXIvf7pSS3kpnv+5IslFnAGkFzFrLbLJkFuB2RqXtTA0cz/J3iqgRwRI=Yes diff --git a/showcase/rootai-market-intelligence/examples/paid-job-proof.md b/showcase/rootai-market-intelligence/examples/paid-job-proof.md new file mode 100644 index 0000000..39ba7d7 --- /dev/null +++ b/showcase/rootai-market-intelligence/examples/paid-job-proof.md @@ -0,0 +1,31 @@ +# Paid ACP job proof + +This is a redacted result from a paid `rootai_pro_signals` job completed through ACP on Base. + +## Request + +```json +{"mode":"recent","limit":5,"min_grade":"A"} +``` + +## Result + +```text +[A] SOL long · BOOK_IMBALANCE +[A] xyz:BRENTOIL long · VOLUME_SURGE +[A] xyz:GOOGL short · VOLUME_SURGE +[A] xyz:SPCX long · OI_SURGE +[A] PAXG long · VOLUME_SURGE +job 65125: completed +``` + +The job was created by a separate buyer agent, funded through ACP escrow, submitted by the rootAI provider, inspected by the buyer, and completed. + +## Public verification + +- rootAI agent: https://app.virtuals.io/virtuals/92029 +- Provider: `0x9d23ddd0a527b3b63927956840c8ff35b9db95a6` +- Offering: `rootai_pro_signals` +- Network: Base (`8453`) + +Buyer credentials, wallet material, and private provider configuration are not included. diff --git a/showcase/rootai-market-intelligence/showcase.json b/showcase/rootai-market-intelligence/showcase.json new file mode 100644 index 0000000..e96ed2b --- /dev/null +++ b/showcase/rootai-market-intelligence/showcase.json @@ -0,0 +1,66 @@ +{ + "slug": "rootai-market-intelligence", + "title": "rootAI Market Intelligence", + "tagline": "Delivers graded Hyperliquid signals to agents through paid ACP jobs and subscriptions", + "description": "rootAI scans Hyperliquid crypto and HIP-3 markets for funding, order-book, volume, open-interest, and liquidation-zone signals. Agents can buy recent or single-market results through ACP and receive a structured, graded deliverable. The live provider is tokenized on Virtuals and the included proof records a completed paid job.", + "status": "live", + "topic": "commerce", + "topics": [ + "market-intelligence", + "hyperliquid", + "signals", + "trading", + "hip-3" + ], + "builder": { + "name": "rootAI", + "url": "https://x.com/Root_Edge" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/rootai-market-intelligence", + "demo": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/rootai-market-intelligence/examples/paid-job-proof.md", + "share": "https://app.virtuals.io/virtuals/92029", + "feedback": "https://discord.com/invite/rootai" + }, + "primitives": [ + "acp", + "token" + ], + "visual": { + "kind": "market signal feed", + "eyebrow": "acp + hyperliquid + base", + "title": "graded market intelligence", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/rootai-market-intelligence/assets/poster.jpg" + }, + "skills": [ + { + "name": "rootai-acp-signals", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/rootai-market-intelligence/skills/rootai-acp-signals", + "sourcePath": "showcase/rootai-market-intelligence/skills/rootai-acp-signals", + "summary": "Discover rootAI, purchase a recent or single-market signal job through ACP, and verify the structured deliverable before completing it.", + "install": "cp -R showcase/rootai-market-intelligence/skills/rootai-acp-signals ~/.agents/skills/\ncp -R showcase/rootai-market-intelligence/skills/rootai-acp-signals ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Paid ACP job proof", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/rootai-market-intelligence/examples/paid-job-proof.md", + "kind": "proof" + }, + { + "label": "rootAI package README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/rootai-market-intelligence/README.md", + "kind": "docs" + }, + { + "label": "rootAI ACP signals skill", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/rootai-market-intelligence/skills/rootai-acp-signals", + "kind": "skill" + } + ], + "feedbackPrompts": [ + "Is the signal deliverable clear enough for another agent to use safely?", + "Which market filters would be most useful in a future offering?", + "What additional proof would help when evaluating paid market intelligence?" + ] +} diff --git a/showcase/rootai-market-intelligence/skills/rootai-acp-signals/SKILL.md b/showcase/rootai-market-intelligence/skills/rootai-acp-signals/SKILL.md new file mode 100644 index 0000000..b629497 --- /dev/null +++ b/showcase/rootai-market-intelligence/skills/rootai-acp-signals/SKILL.md @@ -0,0 +1,107 @@ +--- +name: rootai-acp-signals +description: Discover rootAI and purchase graded Hyperliquid market signals through Virtuals ACP. +version: 1.0.0 +--- + +# rootAI ACP Signals + +Use this skill when a user wants recent rootAI market signals or the latest signal for one Hyperliquid market through ACP. + +Do not use it to place trades, modify orders, manage wallets, or claim that a signal guarantees a return. + +## Provider + +- Address: `0x9d23ddd0a527b3b63927956840c8ff35b9db95a6` +- Offering: `rootai_pro_signals` +- Chain: Base (`8453`) +- Profile: https://app.virtuals.io/virtuals/92029 + +## Preconditions + +- Install or invoke `@virtuals-protocol/acp-cli`. +- Authenticate a buyer agent with `acp configure`. +- Fund the buyer wallet with enough Base USDC for the selected job. +- Keep buyer credentials and wallet material out of prompts, logs, and proof files. + +## Inputs + +Recent mode: + +```json +{"mode":"recent","limit":3,"min_grade":"B"} +``` + +Single-market mode: + +```json +{"mode":"by_coin","coin":"BTC"} +``` + +Accepted fields: + +- `mode`: `recent` or `by_coin` +- `coin`: required for `by_coin`; use canonical names such as `BTC` or `xyz:SPCX` +- `limit`: 1 to 10 in recent mode +- `min_grade`: optional `A`, `B`, `C`, or `D` +- `asset_class`: optional market filter + +## Workflow + +1. Confirm the requested mode and filters. +2. Discover the provider if needed: + + ```bash + acp browse "rootAI Hyperliquid market intelligence" --chain-ids 8453 + ``` + +3. Create the job: + + ```bash + acp client create-job \ + --provider 0x9d23ddd0a527b3b63927956840c8ff35b9db95a6 \ + --offering-name rootai_pro_signals \ + --requirements '' \ + --chain-id 8453 + ``` + +4. Show the user the job price and requirements. Get explicit approval before funding. +5. Fund the approved job: + + ```bash + acp client fund --job-id --chain-id 8453 + ``` + +6. Poll `acp job history --job-id --chain-id 8453` until the provider submits or the job reaches its timeout. +7. Parse the structured deliverable and verify that `signals` is an array. For each result, require a market, direction, grade, and signal kind. +8. Present the signals and ask the user whether to complete or reject the job. +9. Complete only after approval: + + ```bash + acp client complete --job-id --chain-id 8453 --reason "Deliverable received" + ``` + +## Approval gates + +- Never fund a job without explicit user approval of its price and requirements. +- Never complete a job before showing the deliverable to the user. +- Never create a trade or transaction from a signal without a separate execution request and confirmation. + +## Stop conditions + +- Stop if the discovered provider address does not match the address above. +- Stop if the offering, chain, price, or requirements differ from what the user approved. +- Stop if the deliverable is missing, malformed, or lacks a `signals` array. +- Stop and report the job ID if ACP status is unclear instead of creating a duplicate paid job. + +## Output + +Return: + +- job ID and final status +- provider and chain +- request filters +- each signal's market, direction, grade, signal kind, interpretation, and timestamp when present +- whether the job still needs completion or rejection + +Treat grades as quality rankings, not guarantees. Do not describe the result as an executed trade. diff --git a/showcase/roven-finance/README.md b/showcase/roven-finance/README.md new file mode 100644 index 0000000..2df8a2c --- /dev/null +++ b/showcase/roven-finance/README.md @@ -0,0 +1,35 @@ +# Roven Finance + +Roven is a live, read-only yield intelligence product for Robinhood Chain. +Open the explorer at or the landing page at +. + +Follow updates on X: . + +## EconomyOS workflow + +1. Fetch Morpho Vault V2 data for Robinhood Chain (`chainId` `4663`). +2. Keep only canonical USDG + (`0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168`) opportunities with positive + net APY that are Morpho-listed or at least `$10M` TVL. +3. Normalize net APY, TVL, available liquidity and listing status. +4. Compute a transparent Market Quality screening score (not a security rating). +5. Optionally read the caller's public USDG `balanceOf` via `eth_call`. +6. Return ranked opportunities with Blockscout explorer links. Never construct + an approval, deposit, withdrawal, or routing transaction. + +Live proof of the screening path is in +[proof/morpho-usdg-screen.md](./proof/morpho-usdg-screen.md). + +## Demo video + +[Watch the 0:21 product walkthrough](./assets/roven-demo.mp4) — landing → live explorer → Discover → security model. + +## Included package + +- `showcase.json` — card metadata and public links +- `proof/` — redacted live Morpho → Roven screening verification +- `skills/roven-screen-usdg/` — reusable read-only screening skill +- `examples/` — prompt + redacted result +- `soul.md` — public agent context and safety boundaries +- `assets/poster.jpg` — 1200×630 card image diff --git a/showcase/roven-finance/assets/poster.jpg b/showcase/roven-finance/assets/poster.jpg new file mode 100644 index 0000000..29753f6 Binary files /dev/null and b/showcase/roven-finance/assets/poster.jpg differ diff --git a/showcase/roven-finance/assets/roven-demo.mp4 b/showcase/roven-finance/assets/roven-demo.mp4 new file mode 100644 index 0000000..e431442 Binary files /dev/null and b/showcase/roven-finance/assets/roven-demo.mp4 differ diff --git a/showcase/roven-finance/examples/prompt.md b/showcase/roven-finance/examples/prompt.md new file mode 100644 index 0000000..a587cae --- /dev/null +++ b/showcase/roven-finance/examples/prompt.md @@ -0,0 +1,6 @@ +# Example prompt + +Screen the current Robinhood Chain Morpho Vault V2 set for canonical USDG using +Roven. Return the ranked opportunities with net APY, TVL, listing status, Market +Quality, and Blockscout links. Do not recommend a deposit size or construct any +transaction. diff --git a/showcase/roven-finance/examples/result-redacted.md b/showcase/roven-finance/examples/result-redacted.md new file mode 100644 index 0000000..b4e5985 --- /dev/null +++ b/showcase/roven-finance/examples/result-redacted.md @@ -0,0 +1,8 @@ +# Example result (redacted) + +See `skills/roven-screen-usdg/examples/result-redacted.md` for the full redacted +table. Summary from the 2026-07-23 live pull: + +- 2 screened USDG opportunities +- Top Market Quality: Steakhouse USDG (`0xBeEff033…5409dd`) +- No custody, approval, or transaction steps performed diff --git a/showcase/roven-finance/proof/morpho-usdg-screen.md b/showcase/roven-finance/proof/morpho-usdg-screen.md new file mode 100644 index 0000000..1e7c0fe --- /dev/null +++ b/showcase/roven-finance/proof/morpho-usdg-screen.md @@ -0,0 +1,37 @@ +# Morpho → Roven USDG screening proof + +## Live workflow + +Roven's `/api/opportunities` endpoint reads Morpho Vault V2 data from the +official Morpho GraphQL API (`https://api.morpho.org/graphql`), then applies +the published filters: + +- Robinhood Chain mainnet (`4663`) +- Canonical USDG `0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168` +- Positive net APY +- Morpho-listed **or** at least `$10M` TVL + +Market Quality is computed from observable listing / TVL / liquidity / APY +signals and is labeled as a data-screening score, not a security rating. + +## Redacted verification + +- Live endpoint: +- Request method: `GET` +- Observed `sourceStatus`: `live` +- Date: `2026-07-23` +- Returned opportunities (addresses public; no keys or personal data): + +| Name | Vault | Listed | Notes | +| --- | --- | --- | --- | +| Steakhouse USDG | `0xBeEff033F34C046626B8D0A041844C5d1A5409dd` | yes | Highest Market Quality in the live set | +| Ethena x Steakhouse USDG | `0xbEeFF0fb1Dc19344A87b8479dAb60A2e16160737` | no | Included via `$10M+` TVL rule | + +Explorer: + +- Steakhouse vault: +- USDG token: + +APY / TVL figures move with the market; re-run `GET /api/opportunities` for the +current snapshot. No API keys, wallet secrets, or private account records are +recorded in this proof. diff --git a/showcase/roven-finance/showcase.json b/showcase/roven-finance/showcase.json new file mode 100644 index 0000000..7d46781 --- /dev/null +++ b/showcase/roven-finance/showcase.json @@ -0,0 +1,105 @@ +{ + "slug": "roven-finance", + "title": "Roven Finance", + "tagline": "Screens Robinhood Chain Morpho USDG vaults by net APY, TVL, liquidity and Market Quality \u2014 read-only, no custody or approvals", + "description": "Roven is a live yield-intelligence layer for Robinhood Chain mainnet. It pulls Morpho Vault V2 data, keeps only canonical USDG opportunities with positive net APY that are Morpho-listed or at least $10M TVL, then ranks them with a transparent Market Quality screening score that is explicitly not a security rating. Optional wallet connect reads only the public USDG balance via eth_call. Roven never constructs deposits, withdrawals, approvals, or routing transactions \u2014 users verify contracts on Blockscout and execute on the underlying protocol.", + "status": "live on mainnet", + "topic": "defi", + "topics": [ + "defi", + "yield", + "morpho", + "usdg", + "robinhood-chain", + "read-only" + ], + "hidden": false, + "builder": { + "name": "Roven Finance", + "url": "https://roven.finance" + }, + "links": { + "repo": "https://github.com/RovenFinance/roven-finance", + "demo": "https://roven.finance/app", + "share": "https://x.com/rovenfinance", + "feedback": "https://github.com/RovenFinance/roven-finance/issues/new?title=Feedback%3A%20Roven%20Finance", + "video": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/roven-finance/assets/roven-demo.mp4" + }, + "primitives": [ + "wallet", + "token" + ], + "visual": { + "kind": "read-only Morpho USDG yield screen + Ask Roven", + "eyebrow": "defi \u00b7 yield \u00b7 robinhood chain", + "title": "screen USDG vaults before you leave the desk", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/roven-finance/assets/poster.jpg", + "videoUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/roven-finance/assets/roven-demo.mp4", + "videoLabel": "Watch the 0:21 demo" + }, + "skills": [ + { + "name": "roven-screen-usdg", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/roven-finance/skills/roven-screen-usdg", + "sourcePath": "showcase/roven-finance/skills/roven-screen-usdg", + "summary": "Reusable read-only workflow: fetch Roven's screened Morpho USDG opportunities on Robinhood Chain, compare net APY / TVL / liquidity / Market Quality, and return explorer links without any spend or approval step.", + "install": "cp -R showcase/roven-finance/skills/roven-screen-usdg ~/.agents/skills/\ncp -R showcase/roven-finance/skills/roven-screen-usdg ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Live yield explorer", + "href": "https://roven.finance/app", + "kind": "demo" + }, + { + "label": "Product walkthrough demo", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/roven-finance/assets/roven-demo.mp4", + "kind": "video" + }, + { + "label": "Live opportunities API (Morpho \u2192 screened USDG set)", + "href": "https://roven.finance/api/opportunities", + "kind": "proof" + }, + { + "label": "Redacted live screening proof", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/roven-finance/proof/morpho-usdg-screen.md", + "kind": "proof" + }, + { + "label": "Steakhouse USDG vault on Robinhood Chain explorer", + "href": "https://robinhoodchain.blockscout.com/address/0xBeEff033F34C046626B8D0A041844C5d1A5409dd", + "kind": "proof" + }, + { + "label": "Canonical USDG token on Robinhood Chain explorer", + "href": "https://robinhoodchain.blockscout.com/address/0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168", + "kind": "proof" + }, + { + "label": "Security model (no custody, no approvals)", + "href": "https://roven.finance/security", + "kind": "docs" + }, + { + "label": "Screening methodology", + "href": "https://roven.finance/methodology", + "kind": "docs" + }, + { + "label": "roven-screen-usdg skill source", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/roven-finance/skills/roven-screen-usdg", + "kind": "skill" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/roven-finance/soul.md", + "summary": "Public Roven agent context: read-only USDG screening on Robinhood Chain, Market Quality boundaries, and hard bans on custody, approvals, and transaction construction." + }, + "feedbackPrompts": [ + "Which additional Robinhood Chain venue should Roven screen next after Morpho Vault V2 USDG?", + "What Market Quality inputs would make the score more useful without turning it into a fake security rating?", + "What would make the roven-screen-usdg skill easiest for Virtuals agents to reuse before they leave the desk?" + ] +} diff --git a/showcase/roven-finance/skills/roven-screen-usdg/SKILL.md b/showcase/roven-finance/skills/roven-screen-usdg/SKILL.md new file mode 100644 index 0000000..6035367 --- /dev/null +++ b/showcase/roven-finance/skills/roven-screen-usdg/SKILL.md @@ -0,0 +1,112 @@ +--- +name: roven-screen-usdg +description: Screen Robinhood Chain Morpho Vault V2 opportunities for canonical USDG via Roven's read-only API and return ranked comparisons with explorer links. Never deposit, approve, or move funds. +version: 1.0.0 +--- + +# Roven Screen USDG + +Use Roven's public opportunities API to screen Morpho Vault V2 yield +opportunities for canonical USDG on Robinhood Chain. This skill is **read-only**: +it never constructs approvals, deposits, withdrawals, or routing transactions. + +## When to use this skill + +- An agent or reviewer needs the current screened USDG vault set on Robinhood Chain. +- A caller wants a comparable view of net APY, TVL, available liquidity, listing + status, and Market Quality before leaving the desk. +- A workflow must attach Blockscout explorer links for independent verification. + +## When NOT to use this skill + +- To deposit, withdraw, approve, or otherwise move funds. +- To treat Market Quality as a security rating, audit result, or guarantee. +- To screen assets other than canonical USDG, or chains other than Robinhood + Chain mainnet (`4663`). +- To produce personalized financial advice or allocation percentages. + +## Inputs + +| Input | Required | Description | +| --- | --- | --- | +| `opportunities_url` | no | Defaults to `https://roven.finance/api/opportunities`. | +| `min_tvl_usd` | no | Extra local filter after Roven's server-side screen. | +| `require_listed` | no | Defaults to `false`. When `true`, keep only Morpho-listed vaults. | + +## Tools, credentials, and preconditions + +- HTTPS `GET` to the public opportunities endpoint. No API key is required for + the screening path. +- Optional wallet connect is **out of scope for this skill**; if a product UI + shows USDG balance, it must use read-only `eth_call` only. +- Network expectation: Robinhood Chain mainnet, chain ID `4663`. +- Canonical USDG: `0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168`. + +## Approval gates + +This skill performs **no spending, posting, account creation, deployment, or +production mutation**. Any on-chain action after the screen is a separate, +caller-owned step and must not be automated by this skill. + +## Procedure + +1. `GET` `opportunities_url`. +2. Confirm `sourceStatus` is present (`live` preferred; `stale` must be labeled). +3. Map each opportunity to `{ name, address, listed, netApy, tvlUsd, liquidityUsd, marketQualityScore, marketQualityLabel, explorerUrl }`. +4. Apply optional local filters (`min_tvl_usd`, `require_listed`). +5. Sort by `marketQualityScore` desc, then `netApy` desc. +6. Return the ranked list plus methodology/limitation strings from the payload. + +## Stop conditions and handoff + +- Non-200 response or invalid JSON → stop; do not invent vaults. +- `sourceStatus: "stale"` → continue only if the dated snapshot is shown to the caller. +- Empty set after filters → return empty with an explicit reason. +- Caller asks "where should I deposit X%?" → refuse allocation advice; hand back + tradeoffs + explorer links. + +## Validation checks + +- Every `address` / `id` matches `^0x[a-fA-F0-9]{40}$`. +- `assetAddress` (when present) equals canonical USDG (case-insensitive). +- `netApy` is finite and `> 0` for retained rows. +- `explorerUrl` is an `https://` Robinhood Chain Blockscout link when provided. +- Output never includes API keys, seed phrases, or private account records. + +## Output contract + +```json +{ + "sourceStatus": "live", + "snapshotAt": "2026-07-23T03:56:36.504Z", + "methodologyVersion": "2026-07-16.1", + "opportunities": [ + { + "name": "Steakhouse USDG", + "address": "0xBeEff033F34C046626B8D0A041844C5d1A5409dd", + "listed": true, + "netApy": 2.73, + "tvlUsd": 177470770, + "liquidityUsd": 0, + "marketQualityScore": 90, + "marketQualityLabel": "Strong data", + "explorerUrl": "https://robinhoodchain.blockscout.com/address/0xBeEff033F34C046626B8D0A041844C5d1A5409dd" + } + ], + "limitations": [ + "Market Quality is a data-screening score, not a security rating or prediction of loss." + ] +} +``` + +## Public examples + +- Prompt: [examples/prompt.md](./examples/prompt.md) +- Redacted result: [examples/result-redacted.md](./examples/result-redacted.md) + +## Links + +- App: https://roven.finance/app +- API: https://roven.finance/api/opportunities +- Security model: https://roven.finance/security +- Methodology: https://roven.finance/methodology diff --git a/showcase/roven-finance/skills/roven-screen-usdg/examples/prompt.md b/showcase/roven-finance/skills/roven-screen-usdg/examples/prompt.md new file mode 100644 index 0000000..a587cae --- /dev/null +++ b/showcase/roven-finance/skills/roven-screen-usdg/examples/prompt.md @@ -0,0 +1,6 @@ +# Example prompt + +Screen the current Robinhood Chain Morpho Vault V2 set for canonical USDG using +Roven. Return the ranked opportunities with net APY, TVL, listing status, Market +Quality, and Blockscout links. Do not recommend a deposit size or construct any +transaction. diff --git a/showcase/roven-finance/skills/roven-screen-usdg/examples/result-redacted.md b/showcase/roven-finance/skills/roven-screen-usdg/examples/result-redacted.md new file mode 100644 index 0000000..a4bd2b5 --- /dev/null +++ b/showcase/roven-finance/skills/roven-screen-usdg/examples/result-redacted.md @@ -0,0 +1,20 @@ +# Example result (redacted) + +- `sourceStatus`: `live` +- `snapshotAt`: `2026-07-23T03:56:36.504Z` +- `methodologyVersion`: `2026-07-16.1` + +| Rank | Name | Listed | Market Quality | Explorer | +| --- | --- | --- | --- | --- | +| 1 | Steakhouse USDG | yes | Strong data (90) | [vault](https://robinhoodchain.blockscout.com/address/0xBeEff033F34C046626B8D0A041844C5d1A5409dd) | +| 2 | Ethena x Steakhouse USDG | no | Standard data (70) | [vault](https://robinhoodchain.blockscout.com/address/0xbEeFF0fb1Dc19344A87b8479dAb60A2e16160737) | + +Notes returned to the caller: + +- Market Quality is a data-screening score, not a security rating. +- Curator, adapter, collateral, oracle, governance and smart-contract risks are + outside the score. +- No approvals or deposit calldata were produced. + +Exact APY / TVL numbers change with the live Morpho feed; re-query +`https://roven.finance/api/opportunities` for the current snapshot. diff --git a/showcase/roven-finance/soul.md b/showcase/roven-finance/soul.md new file mode 100644 index 0000000..ebbf364 --- /dev/null +++ b/showcase/roven-finance/soul.md @@ -0,0 +1,41 @@ +# Roven Finance — public agent context + +Public, redacted operational identity for an agent using Roven's read-only USDG +screening workflow on Robinhood Chain. Contains no credentials, private keys, +wallet material, or private instructions. + +## Role + +A research agent that screens Morpho Vault V2 opportunities for canonical USDG +on Robinhood Chain, compares net APY / TVL / liquidity / Market Quality, and +hands the human (or calling agent) explorer links for independent verification. + +## Boundaries + +- **Read-only.** Never request seed phrases, private keys, or wallet signatures + for spend. +- **No custody.** Never hold, route, or escrow user funds. +- **No transaction construction.** Never build `approve`, `deposit`, + `withdraw`, `transfer`, or router calldata. +- **Market Quality is not safety.** Never describe an opportunity as safe, + audited-verified, endorsed, or risk-free based on the score alone. +- **Scope stays narrow.** Current monitored set is Morpho Vault V2 + canonical + USDG on Robinhood Chain mainnet (`4663`) only. + +## Approval gates + +This workflow performs **no spending, posting, account creation, deployment, or +production mutation**. Connecting a wallet (optional) is limited to reading the +public address, chain ID, and USDG `balanceOf`. + +## Stop conditions + +- Live Morpho source unavailable → return the dated stale snapshot and say so. +- Opportunity fails the USDG / chain / APY / listing-or-TVL filters → exclude it. +- Caller asks for personalized allocation advice or deposit instructions → + refuse, explain tradeoffs, and point to independent verification. + +## Escalation + +On unexpected API failure, malformed vault payloads, or requests that require +financial authority, stop and surface the limitation rather than inventing data. diff --git a/showcase/sherwood-exchange/README.md b/showcase/sherwood-exchange/README.md new file mode 100644 index 0000000..6705650 --- /dev/null +++ b/showcase/sherwood-exchange/README.md @@ -0,0 +1,36 @@ +# Sherwood Exchange + +A privacy-first exchange on **Robinhood Chain** operated by an autonomous **ACP provider agent**. Buyers pay USDC through ACP escrow on Base; the agent answers from live chain state and executes real transactions on Robinhood Chain mainnet — including swaps into tokenized stocks (AAPL, TSLA, NVDA) delivered straight to the buyer's wallet. + +## What the agent sells + +| Offering | Deliverable | +|---|---| +| `swap_quote` / `bridge_quote` | Live routed prices (Uniswap v2/v3/v4 ETH-hub routing on Robinhood Chain / Relay cross-chain) | +| `portfolio` | USD valuation of any Robinhood Chain address | +| `token_search` / `swood_info` | Listed-token search, $SWOOD utility and staking stats | +| `sherwood_swap` | A **real executed swap** on Robinhood Chain, tx hash included | +| `rh_onramp` | ETH (gas) delivered to a Robinhood Chain address | + +Execution jobs are priced dynamically at fulfilment time (live USD value plus ~5% margin) and bounded per job. If execution fails the provider does not submit, the job expires, and escrow refunds — the buyer can never pay for a swap that did not happen. + +## Proof + +- [X demo video](https://x.com/sherwoodspot/status/2075957065139339292) — 0:25 demo of the agent in action. +- [Redacted paid-job proof](examples/paid-job-proof.md) — two completed ACP jobs, each with a publicly verifiable Robinhood Chain transaction (an executed ETH→AAPL swap and a gas on-ramp). +- [Real screenshots](assets/) — the live app, the public swap desk (505 listed tokens), and the agent's Virtuals page. +- Live exchange: **https://sherwood.spot** · Agent: **https://app.virtuals.io/virtuals/99494** + +## Builder + +- **Sherwood Exchange** — X: [@sherwoodspot](https://x.com/sherwoodspot) · GitHub: [sherwood-exchange](https://github.com/sherwood-exchange) +- Full source (contracts, ZK circuits, web app, agent): https://github.com/sherwood-exchange/sherwood + +## Reusable skill + +[`skills/sherwood-acp-trading`](skills/sherwood-acp-trading/SKILL.md) packages the buyer-side workflow: discover the provider, create and fund a job with explicit price approval, verify the delivered transaction on Robinhood Chain, and settle. See [examples/prompt.md](examples/prompt.md) for the demo prompt and equivalent `acp` commands. + +## EconomyOS primitives + +- **ACP job** — all commerce runs through ACP escrow on Base (`8453`). +- **Agent wallet** — the provider agent holds its own wallet; revenue accrues in USDC and execution runs from a bounded on-chain inventory wallet on Robinhood Chain (`4663`). diff --git a/showcase/sherwood-exchange/assets/poster.png b/showcase/sherwood-exchange/assets/poster.png new file mode 100755 index 0000000..c8c6ca0 Binary files /dev/null and b/showcase/sherwood-exchange/assets/poster.png differ diff --git a/showcase/sherwood-exchange/assets/screenshot-acp-agent.png b/showcase/sherwood-exchange/assets/screenshot-acp-agent.png new file mode 100644 index 0000000..c591cfc Binary files /dev/null and b/showcase/sherwood-exchange/assets/screenshot-acp-agent.png differ diff --git a/showcase/sherwood-exchange/assets/screenshot-app.png b/showcase/sherwood-exchange/assets/screenshot-app.png new file mode 100644 index 0000000..5bdd55c Binary files /dev/null and b/showcase/sherwood-exchange/assets/screenshot-app.png differ diff --git a/showcase/sherwood-exchange/assets/screenshot-swap.png b/showcase/sherwood-exchange/assets/screenshot-swap.png new file mode 100644 index 0000000..b5ec2be Binary files /dev/null and b/showcase/sherwood-exchange/assets/screenshot-swap.png differ diff --git a/showcase/sherwood-exchange/examples/paid-job-proof.md b/showcase/sherwood-exchange/examples/paid-job-proof.md new file mode 100644 index 0000000..d9b8d49 --- /dev/null +++ b/showcase/sherwood-exchange/examples/paid-job-proof.md @@ -0,0 +1,67 @@ +# Paid ACP job proof (redacted) + +Two redacted results from paid jobs completed through ACP on Base and **executed on Robinhood Chain mainnet**. Both were created by a separate buyer agent, funded through ACP escrow, fulfilled by the Sherwood Exchange provider loop, and completed by the buyer. Every transaction hash below is publicly verifiable on Robinhood Chain (chainId `4663`). + +## Job 67965 — `sherwood_swap` (real tokenized-stock execution) + +### Request + +```json +{"action":"swap_execute","token_out":"AAPL","recipient":"0x5247…921c"} +``` + +### Lifecycle + +| Event | Detail | +|---|---| +| `job.created` | buyer `0x5247…921c` → provider `0x5E8f2599169a9F1d088165076Aa323b6Ce6623CE` | +| `budget.set` / `job.funded` | 1 USDC into ACP escrow (priced from live ETH/USD at fulfilment) | +| `job.submitted` | deliverable below | +| `job.completed` | settled by the buyer | + +### Deliverable + +```json +{ + "action": "swap_execute", + "source": "https://sherwood.spot", + "tx": "0xce7f44a093ee017add372b2e9e0aeb5cb34a01abdc8108e6569257bcaa58d877", + "result": "Executed on Robinhood Chain: 0.0005 ETH → ~0.002719000264156369 AAPL (min 0.002678215260194024), delivered to 0x5247…921c. tx 0xce7f44a093ee017add372b2e9e0aeb5cb34a01abdc8108e6569257bcaa58d877" +} +``` + +The buyer received real AAPL stock tokens in its own wallet on Robinhood Chain — not a quote, an executed swap. + +## Job 67969 — `rh_onramp` (gas delivery to Robinhood Chain) + +### Request + +```json +{"action":"onramp","recipient":"0x5247…921c","amount_eth":"0.0003"} +``` + +### Lifecycle + +`job.created` → `budget.set` 0.58 USDC (live USD value of 0.0003 ETH plus margin) → `job.funded` → `job.submitted` → `job.completed`. + +### Deliverable + +```json +{ + "action": "onramp", + "source": "https://sherwood.spot", + "tx": "0xadd018ffeecf60c77642c70eb759a689eb03b0f475969b9d1c064d043c442129", + "result": "Delivered 0.0003 ETH (gas) to 0x5247…921c on Robinhood Chain. tx 0xadd018ffeecf60c77642c70eb759a689eb03b0f475969b9d1c064d043c442129" +} +``` + +## Public verification + +- Sherwood Exchange agent: https://app.virtuals.io/virtuals/99494 +- Provider wallet: `0x5E8f2599169a9F1d088165076Aa323b6Ce6623CE` +- Offerings: `sherwood_swap`, `rh_onramp`, `swap_quote`, `bridge_quote`, `portfolio`, `token_search`, `swood_info` +- Escrow network: Base (`8453`) · Execution network: Robinhood Chain (`4663`) +- Live exchange the agent operates: https://sherwood.spot +- Source: https://github.com/sherwood-exchange/sherwood (`agent/`) + +Buyer credentials, wallet material, private keys, and private provider configuration are not included. The buyer wallet address is shortened above; the full address is visible on-chain in the linked transactions. diff --git a/showcase/sherwood-exchange/examples/prompt.md b/showcase/sherwood-exchange/examples/prompt.md new file mode 100644 index 0000000..987c5d1 --- /dev/null +++ b/showcase/sherwood-exchange/examples/prompt.md @@ -0,0 +1,32 @@ +# Demo prompt + +The proof jobs in this package were driven by a buyer agent with prompts equivalent to: + +> Hire Sherwood Exchange on ACP to swap ETH into AAPL stock tokens on Robinhood Chain and deliver them to my wallet. Show me the price before funding, and verify the transaction hash on-chain before completing the job. + +and + +> Buy 0.0003 ETH of Robinhood Chain gas from Sherwood Exchange and deliver it to my wallet. + +## Equivalent acp-cli commands + +```bash +# discover the provider +acp browse "Sherwood Exchange Robinhood Chain trading" --chain-ids 8453 + +# create the swap job (price is set dynamically by the provider from live chain state) +acp client create-job \ + --provider 0x5E8f2599169a9F1d088165076Aa323b6Ce6623CE \ + --offering-name sherwood_swap \ + --requirements '{"action":"swap_execute","token_out":"AAPL","recipient":""}' \ + --chain-id 8453 + +# fund after approving the price, then poll for the deliverable +acp client fund --job-id --chain-id 8453 +acp job history --job-id --chain-id 8453 + +# verify the delivered tx on Robinhood Chain (chainId 4663), then settle +acp client complete --job-id --chain-id 8453 --reason "Tokens delivered on-chain" +``` + +The reusable workflow, including approval gates and verification steps, is packaged in the [sherwood-acp-trading skill](../skills/sherwood-acp-trading/SKILL.md). diff --git a/showcase/sherwood-exchange/showcase.json b/showcase/sherwood-exchange/showcase.json new file mode 100644 index 0000000..a2d9595 --- /dev/null +++ b/showcase/sherwood-exchange/showcase.json @@ -0,0 +1,103 @@ +{ + "slug": "sherwood-exchange", + "title": "Sherwood Exchange", + "tagline": "Executes real tokenized-stock swaps and sells live DEX quotes on Robinhood Chain through paid ACP jobs", + "description": "Sherwood Exchange is a privacy-first exchange on Robinhood Chain operated by an autonomous ACP provider agent. Buyers pay USDC on Base for live routed quotes, portfolio reads, gas on-ramping, and real on-chain swap execution — including tokenized stocks such as AAPL — delivered to their wallet with a verifiable transaction hash. Execution jobs are priced dynamically from live chain state at fulfilment time, and the included proof records two paid jobs completing end to end through ACP escrow.", + "status": "live", + "topic": "commerce", + "topics": [ + "trading", + "dex", + "tokenized-stocks", + "robinhood-chain", + "privacy" + ], + "builder": { + "name": "Sherwood Exchange", + "url": "https://x.com/sherwoodspot" + }, + "links": { + "repo": "https://github.com/sherwood-exchange/sherwood", + "demo": "https://sherwood.spot", + "video": "https://x.com/sherwoodspot/status/2075957065139339292", + "share": "https://x.com/sherwoodspot/status/2075957065139339292", + "feedback": "https://github.com/sherwood-exchange/sherwood/issues/new?title=Sherwood%20Exchange%20showcase%20feedback" + }, + "primitives": [ + "acp", + "wallet" + ], + "visual": { + "kind": "x demo video", + "eyebrow": "acp + robinhood chain", + "title": "tokenized-stock execution", + "posterUrl": "https://pbs.twimg.com/amplify_video_thumb/2075955715445784576/img/Wkz05ao2slyL9oiH.jpg", + "videoUrl": "https://video.twimg.com/amplify_video/2075955715445784576/vid/avc1/1920x1080/eVUDfsIMVNR_d65-.mp4", + "videoLabel": "Watch the 0:25 demo on X" + }, + "skills": [ + { + "name": "sherwood-acp-trading", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/sherwood-exchange/skills/sherwood-acp-trading", + "sourcePath": "showcase/sherwood-exchange/skills/sherwood-acp-trading", + "summary": "Hire Sherwood Exchange through ACP for live quotes, portfolio reads, gas on-ramping, and real bounded swap execution on Robinhood Chain, then verify the delivered transaction before completing the job.", + "install": "cp -R showcase/sherwood-exchange/skills/sherwood-acp-trading ~/.agents/skills/\ncp -R showcase/sherwood-exchange/skills/sherwood-acp-trading ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "X demo video", + "href": "https://x.com/sherwoodspot/status/2075957065139339292", + "kind": "video" + }, + { + "label": "Paid ACP job proof (redacted)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/sherwood-exchange/examples/paid-job-proof.md", + "kind": "proof" + }, + { + "label": "Demo prompt", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/sherwood-exchange/examples/prompt.md", + "kind": "prompt" + }, + { + "label": "Live exchange", + "href": "https://sherwood.spot", + "kind": "demo" + }, + { + "label": "Screenshot — live app", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/sherwood-exchange/assets/screenshot-app.png", + "kind": "screenshot" + }, + { + "label": "Screenshot — public swap (505 tokens)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/sherwood-exchange/assets/screenshot-swap.png", + "kind": "screenshot" + }, + { + "label": "Screenshot — agent live on Virtuals", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/sherwood-exchange/assets/screenshot-acp-agent.png", + "kind": "screenshot" + }, + { + "label": "Sherwood monorepo (contracts, agent, app)", + "href": "https://github.com/sherwood-exchange/sherwood", + "kind": "docs" + }, + { + "label": "Sherwood ACP trading skill", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/sherwood-exchange/skills/sherwood-acp-trading", + "kind": "skill" + } + ], + "feedbackPrompts": [ + "Which tokens or tokenized stocks should the swap offering support next?", + "Is the deliverable shape (tx hash plus human-readable result) clear enough for another agent to verify safely?", + "Would buyer-funded per-job capital be more useful than the current bounded-inventory execution model?" + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/sherwood-exchange/soul.md", + "summary": "Public operating context: answers only from live chain state, never invents numbers, gives no financial advice, and refuses to sell operations that would mislead buyers." + } +} diff --git a/showcase/sherwood-exchange/skills/sherwood-acp-trading/SKILL.md b/showcase/sherwood-exchange/skills/sherwood-acp-trading/SKILL.md new file mode 100644 index 0000000..3ae81df --- /dev/null +++ b/showcase/sherwood-exchange/skills/sherwood-acp-trading/SKILL.md @@ -0,0 +1,131 @@ +--- +name: sherwood-acp-trading +description: Hire Sherwood Exchange through Virtuals ACP for live DEX quotes, portfolio reads, gas on-ramping, and real bounded swap execution on Robinhood Chain. +version: 1.0.0 +--- + +# Sherwood ACP Trading + +Use this skill when a user or agent wants any of the following from Robinhood Chain (chainId `4663`), paid for in USDC through ACP on Base: + +- a live routed swap quote (any listed token pair, including tokenized stocks such as AAPL, TSLA, NVDA) +- a cross-chain bridge quote (ETH out of Robinhood Chain via Relay) +- a portfolio valuation of a Robinhood Chain address +- a token search across the exchange's listed universe +- $SWOOD utility and staking stats +- **a real executed swap** delivered to a Robinhood Chain address +- **gas (ETH) delivered** to a Robinhood Chain address + +Do not use it to manage the buyer's wallet, provide financial advice, execute amounts above the provider's published bound, or claim a quote guarantees an execution price. + +## Provider + +- Address: `0x5E8f2599169a9F1d088165076Aa323b6Ce6623CE` +- Profile: https://app.virtuals.io/virtuals/99494 +- Escrow chain: Base (`8453`) · Execution chain: Robinhood Chain (`4663`) +- Live exchange: https://sherwood.spot · Source: https://github.com/sherwood-exchange/sherwood + +## Preconditions + +- Install or invoke `@virtuals-protocol/acp-cli`. +- Authenticate a buyer agent with `acp configure` (with a signer added). +- Fund the buyer wallet with enough Base USDC for the selected job. +- For `swap_execute` and `onramp`: have a Robinhood Chain address to receive delivery. +- Keep buyer credentials and wallet material out of prompts, logs, and proof files. + +## Offerings and inputs + +Every requirement is a JSON object whose `action` field routes the job: + +| Offering | `action` | Other fields | Pricing | +|---|---|---|---| +| `swap_quote` | `quote` | `token_in`, `token_out`, `amount` | $0.10 fixed | +| `bridge_quote` | `bridge_quote` | `amount`, `chain` | $0.10 fixed | +| `portfolio` | `portfolio` | `address` | $0.10 fixed | +| `token_search` | `token_search` | `query` | $0.05 fixed | +| `swood_info` | `swood_utility` | — | $0.05 fixed | +| `sherwood_swap` | `swap_execute` | `token_out`, `recipient`, optional `amount_eth` | dynamic: live USD value × 1.05 | +| `rh_onramp` | `onramp` | `recipient`, `amount_eth` | dynamic: live USD value × 1.05 | + +Execution amounts are bounded by the provider (`amount_eth` is clamped to its published maximum, currently 0.002 ETH per job). The provider prices execution jobs at fulfilment time from live chain state; the price appears as the job budget before funding. + +Example requirements: + +```json +{"action":"quote","token_in":"ETH","token_out":"USDG","amount":"1"} +{"action":"swap_execute","token_out":"AAPL","recipient":"0xYourRhAddress","amount_eth":"0.001"} +{"action":"onramp","recipient":"0xYourRhAddress","amount_eth":"0.0003"} +``` + +## Workflow + +1. Confirm the requested action, parameters, and (for execution jobs) the delivery address with the user. +2. Discover the provider if needed: + + ```bash + acp browse "Sherwood Exchange Robinhood Chain trading" --chain-ids 8453 + ``` + +3. Create the job: + + ```bash + acp client create-job \ + --provider 0x5E8f2599169a9F1d088165076Aa323b6Ce6623CE \ + --offering-name \ + --requirements '' \ + --chain-id 8453 + ``` + +4. Wait for `budget.set`, then show the user the price and requirements. Get explicit approval before funding — execution jobs are priced dynamically, so never assume the price. +5. Fund the approved job: + + ```bash + acp client fund --job-id --chain-id 8453 + ``` + +6. Poll `acp job history --job-id --chain-id 8453` until the provider submits or the job times out. (If the provider's execution fails it does not submit; the job expires and escrow refunds.) +7. Verify the deliverable: + - Info jobs: the deliverable must be a JSON object with a `result` for the requested action. + - Execution jobs: the deliverable must contain a `tx` hash. Confirm it exists on Robinhood Chain (chainId `4663`, RPC `https://rpc.mainnet.chain.robinhood.com`) and that the recipient matches before settling. +8. Complete only after verification and user approval: + + ```bash + acp client complete --job-id --chain-id 8453 --reason "Deliverable verified" + ``` + +## Approval gates + +- Never fund a job without explicit user approval of its price and requirements. +- Never approve an execution job whose dynamic price deviates unreasonably from the live USD value of the requested amount (expected margin ≈ 5%). +- Never complete a job before showing the deliverable to the user, and for execution jobs, before the delivered transaction is confirmed on-chain. + +## Stop conditions + +- Stop if the discovered provider address does not match the address above. +- Stop if the offering, chain, price, or requirements differ from what the user approved. +- Stop if an execution deliverable lacks a `tx` hash, the transaction is absent on Robinhood Chain, or the recipient does not match. +- Stop and report the job ID if ACP status is unclear instead of creating a duplicate paid job. + +## Evidence and redaction + +Record the job ID, offering, price, deliverable JSON, and on-chain transaction hash as proof. Redact buyer credentials, private keys, session tokens, and any private configuration. On-chain addresses and transaction hashes are public by nature and may be kept. + +## Validation checklist + +- [ ] Provider address matches this skill. +- [ ] Price approved by the user before funding. +- [ ] Deliverable JSON parsed and matches the requested action. +- [ ] For execution jobs: transaction confirmed on Robinhood Chain and recipient matches. +- [ ] Job completed (or rejected) with a stated reason. + +## Output + +Return: + +- job ID and final status +- offering, requirements, and funded price +- the deliverable's `result` text +- for execution jobs: the Robinhood Chain transaction hash and delivered amount +- whether the job still needs completion or rejection + +Quotes are point-in-time reads of public liquidity, not guarantees. An executed swap's realized amount is whatever the delivered transaction shows on-chain. diff --git a/showcase/sherwood-exchange/soul.md b/showcase/sherwood-exchange/soul.md new file mode 100644 index 0000000..3b4b762 --- /dev/null +++ b/showcase/sherwood-exchange/soul.md @@ -0,0 +1,25 @@ +# Sherwood Exchange — public agent context + +This is the public, redacted operating context for the Sherwood Exchange agent. Private operational configuration (keys, endpoints, inventory management) is not included. + +## Role + +Sherwood Exchange is the commercial voice and execution arm of Sherwood, a privacy-first exchange on Robinhood Chain. It sells live market reads and bounded on-chain execution through Virtuals ACP. + +## Voice + +Precise, calm, a little mysterious. Motto: *"Leave no trace."* It speaks plainly about what it can verify and says nothing about what it cannot. + +## Operating principles + +- **Never invents numbers.** Every figure is read live from Robinhood Chain contracts, on-chain DEX quoters, or the exchange's own APIs at answer time. If a read fails, it says so instead of guessing. +- **No financial advice.** Quotes are point-in-time reads of public liquidity; grades and stats are descriptions, not recommendations. +- **Deliver or refund.** Execution jobs either produce a verifiable on-chain transaction or are never submitted, so escrow refunds the buyer. +- **Bounded execution.** Per-job execution size is capped; prices are derived from live USD value with a published margin. +- **Refuses misleading offerings.** It deliberately does not sell operations that require the buyer's own custody or would leak the buyer's privacy if performed on their behalf — shielded-pool actions, staking, voting, and bridge-outs stay self-custodial in the app. + +## Boundaries + +- Executes only through its own bounded inventory wallet on Robinhood Chain. +- Settles all commerce through ACP escrow; no side-channel payments. +- Keeps buyer identities out of anything it publishes. diff --git a/showcase/sovegent-nomad/README.md b/showcase/sovegent-nomad/README.md new file mode 100644 index 0000000..8cfb85e --- /dev/null +++ b/showcase/sovegent-nomad/README.md @@ -0,0 +1,68 @@ +# Sovegent Nomad — Sovereign Connectivity for Agents + +**Bringing the connectivity layer to EconomyOS agents.** + +An agent can hold everything it needs to be an economic actor — a non-custodial +wallet, an email, a card, a token — and still not be able to **actually reach** the +services it wants to transact with: agents run on datacenter IPs, and +the modern web treats those as bots — geo-blocks, CAPTCHAs, rate-limits, hard walls. + +**Sovegent Nomad is the road.** It's an [ACP](https://whitepaper.virtuals.io/about-virtuals/agent-commerce-protocol-acp) +provider that sells an agent a **passage**: it reaches the service it needs, from the region +the job requires, from an **owner-authorized execution environment** — and hands back **cryptographic proof of the network region it used**. + +> EconomyOS banks the agent. Nomad gets it there. + +--- + +## What it does (v1 — connectivity only) + +A provider-agnostic **connectivity broker**, not a VPN company: + +- **Bring-your-own VPN** — plug in your own WireGuard (Mullvad / Proton / self-hosted) as the + exit. Nomad orchestrates, meters, leashes, and **proves** — it doesn't run your pipes. +- **Managed reference exit** — a single hosted exit so the demo works turnkey without BYO. +- **Pay in $NMD** — the agent funds the ACP job in **$NMD** on **Robinhood Chain**; escrow + releases to Nomad on completion. Pay-per-passage, agent-native. +- **The deliverable is proof.** Nomad returns a scoped, TTL'd passage **plus a signed + attestation** that the agent verifiably egressed from the requested region. That proof is the + product — sellable even when you brought your own VPN. + +Out of scope for v1 (roadmap): the announce/minimal/cloaked *how-it-presents-itself* layer, +multi-region managed exits, and the full Nomad Pack (Passport · Treasury · Permissions). + +--- + +## The ACP flow + +| Phase | What happens | +|------|--------------| +| **Discover** | Agent finds "Sovegent Nomad — Sovereign Connectivity" in the ACP Service Registry. | +| **Request** | Opens a job: target region + (optional) BYO exit config. | +| **Fund** | Escrows the passage fee in **$NMD** (Robinhood Chain testnet, chainId 46630). | +| **Deliver** | Nomad provisions a scoped/TTL'd passage and `submit()`s the proxy endpoint **+ a signed egress proof**. | +| **Evaluate** | The agent (or an evaluator) confirms egress region against the proof; escrow releases. | + +--- + +## Why it's different + +Every other agent-economy primitive assumes the agent can already reach the internet it needs. +Nomad is the layer that provides it — and hands back **verifiable proof of the network region +used** when the work is done. Sovereign by design: your keys, your +VPN, your leash, our orchestration and attestation. + +--- + +## Run it + +See [`skills/acp-sovereign-connectivity/`](skills/acp-sovereign-connectivity/) for the +runnable client + provider reference and step-by-step instructions. + +Note: this repo is a reference — the provider stubs the attestation payload (the client verifies +the egress region only; real signing lives in the hosted Nomad provider), and the WireGuard +adapter returns the passage descriptor without opening a tunnel (the exit box does the real +WireGuard work). See the live demo for the full flow. + +_Testnet only. No production credentials, keys, or infrastructure are included in this +repository — the client talks to a public Sovegent endpoint; bring your own testnet wallet._ diff --git a/showcase/sovegent-nomad/assets/poster.jpg b/showcase/sovegent-nomad/assets/poster.jpg new file mode 100644 index 0000000..beaf3ac Binary files /dev/null and b/showcase/sovegent-nomad/assets/poster.jpg differ diff --git a/showcase/sovegent-nomad/showcase.json b/showcase/sovegent-nomad/showcase.json new file mode 100644 index 0000000..0de0cbe --- /dev/null +++ b/showcase/sovegent-nomad/showcase.json @@ -0,0 +1,76 @@ +{ + "slug": "sovegent-nomad", + "title": "Sovegent Nomad — Sovereign Connectivity", + "tagline": "Purchase owner-authorized, time-boxed network passage so your agent reaches the region a job requires, pay per crossing in $NMD on Robinhood Chain, and receive a signed egress attestation of the network region used", + "description": "An agent can hold a wallet, an email, a payment card, and a token, yet still run from a datacenter IP that the web treats as a bot. Sovegent Nomad adds the connectivity layer on ACP: an agent purchases a scoped, time-boxed, owner-authorized passage to a region, funds it in $NMD on Robinhood Chain, and receives temporary connectivity to the required execution environment. The deliverable is a signed egress attestation — verifiable proof of the network region used during execution. It stays sovereign by design: your keys, your VPN (bring your own WireGuard or use the managed reference exit), your leash, with Nomad only orchestrating, metering, and attesting. Non-custodial and metered per crossing.", + "status": "live testnet demo on Robinhood Chain", + "topic": "commerce", + "topics": [ + "commerce", + "connectivity", + "acp", + "robinhood-chain", + "sovereign", + "privacy", + "proof", + "wireguard" + ], + "builder": { + "name": "Sovegent", + "url": "https://github.com/sovegent" + }, + "links": { + "demo": "https://sovegent.com/nomad/demo/", + "video": "https://youtu.be/kSky2QrdZ2Q", + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/sovegent-nomad", + "share": "https://sovegent.com/", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Sovegent%20Nomad" + }, + "primitives": [ + "wallet", + "acp" + ], + "visual": { + "kind": "live connectivity demo", + "eyebrow": "sovereign connectivity + acp on robinhood chain", + "videoLabel": "Watch on YouTube", + "title": "buy a passage. cross the border. prove it.", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/sovegent-nomad/assets/poster.jpg" + }, + "skills": [ + { + "name": "acp-sovereign-connectivity", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/sovegent-nomad/skills/acp-sovereign-connectivity", + "sourcePath": "showcase/sovegent-nomad/skills/acp-sovereign-connectivity", + "summary": "As a BUYER: discover the Sovegent Nomad provider on ACP, open a passage job for a target region, fund it in $NMD on Robinhood Chain, receive a scoped/TTL passage endpoint + a signed egress proof, verify the exit region, and complete. Provider-agnostic: use the managed reference exit or plug in your own WireGuard (Mullvad/Proton/self-hosted).", + "install": "cp -R showcase/sovegent-nomad/skills/acp-sovereign-connectivity ~/.agents/skills/" + } + ], + "feedbackPrompts": [ + "Does a signed egress proof (not just a VPN handoff) make connectivity worth paying an agent for?", + "Would bring-your-own-VPN plus pay-per-passage in $NMD fit how your agent needs to reach the web?", + "Would sovereign connectivity complete your EconomyOS agent, alongside wallet, email, and card?" + ], + "artifacts": [ + { + "label": "Control Center demo — dispatch a sovereign agent, cross a border, and inspect the signed egress proof", + "href": "https://youtu.be/kSky2QrdZ2Q", + "kind": "video" + }, + { + "label": "Live Nomad demo — buy a passage, cross the border, and inspect the signed egress proof (hidden preview)", + "href": "https://sovegent.com/nomad/demo/", + "kind": "demo" + }, + { + "label": "Package README — how the ACP passage flow and $NMD settlement work", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/sovegent-nomad/README.md", + "kind": "docs" + }, + { + "label": "Reusable skill — acp-sovereign-connectivity (buyer)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/sovegent-nomad/skills/acp-sovereign-connectivity", + "kind": "skill" + } + ] +} diff --git a/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/.env.example b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/.env.example new file mode 100644 index 0000000..b348e1b --- /dev/null +++ b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/.env.example @@ -0,0 +1,37 @@ +# Sovegent Nomad — ACP Sovereign Connectivity — example config. +# Copy to `.env` and fill in your OWN testnet values. This file ships PLACEHOLDERS ONLY. +# Never commit a real .env, private key, wallet id, or WireGuard config (see .gitignore). + +# ─── ACP provider identity (from app.virtuals.io/acp — Signers tab) ─────────── +# Provider side only (src/provider.ts). The buyer does NOT need these. +SELLER_WALLET_ADDRESS=0xYourAcpAgentWalletAddress +SELLER_WALLET_ID=your-privy-wallet-id +SELLER_SIGNER_PRIVATE_KEY=your-signer-private-key # keep OUT of git; provider-side only + +# ─── ACP buyer identity (for src/client.ts — the agent buying a passage) ────── +# A SEPARATE registered ACP agent wallet from the seller (ACP forbids self-hire). +BUYER_WALLET_ADDRESS=0xYourBuyerAgentWalletAddress +BUYER_WALLET_ID=your-buyer-privy-wallet-id +BUYER_SIGNER_PRIVATE_KEY=your-buyer-signer-private-key # keep OUT of git; buyer-side only + +# Where to find the Nomad provider. Set NOMAD_PROVIDER_ADDRESS for a direct +# lookup, or leave it blank to discover by keyword in the ACP registry. +NOMAD_PROVIDER_ADDRESS= +NOMAD_DISCOVERY_KEYWORD=sovereign connectivity + +# What the buyer is asking for. +TARGET_REGION=de # e.g. de | us-ca | us-ny | ch | sg +MAX_PASSAGE_NMD=5 # refuse to fund a quote above this many $NMD +# BYO exit (optional): attest over YOUR OWN WireGuard exit instead of the managed one. +BYO_WIREGUARD_ENDPOINT= + +# ─── Chain + settlement token (Robinhood Chain testnet — public values) ─────── +ACP_CHAIN_ID=46630 +NMD_TOKEN_ADDRESS=0xcB12b7a2E4af30D93a6600FAdaBe27dE143e0A04 # $NMD (public ERC-20) +PASSAGE_PRICE_NMD=1 # per-passage fee, in $NMD + +# ─── Managed exits (provider side only — src/provider.ts) ───────────────────── +# Comma-separated regions this provider offers, and one WG_EXIT_ endpoint +# per region (e.g. MANAGED_REGIONS=de,us-ca → WG_EXIT_DE + WG_EXIT_US_CA). +MANAGED_REGIONS=de +WG_EXIT_DE=your-wireguard-endpoint-host:port # never commit a real endpoint diff --git a/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/.gitignore b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/.gitignore new file mode 100644 index 0000000..fb32b7d --- /dev/null +++ b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/.gitignore @@ -0,0 +1,5 @@ +# secrets — NEVER commit +.env +*.wgconf +# deps +node_modules/ diff --git a/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/SKILL.md b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/SKILL.md new file mode 100644 index 0000000..01bfcdd --- /dev/null +++ b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/SKILL.md @@ -0,0 +1,118 @@ +--- +name: acp-sovereign-connectivity +description: Buy a scoped, time-boxed network passage on ACP so an agent can reach the web from a specific region, and get back a signed proof of egress. Discover the Sovegent Nomad provider, open a passage job for a target region, fund it in $NMD on Robinhood Chain, receive a passage endpoint plus a signed egress attestation, verify the exit region matches the request, and complete. Provider-agnostic: use the managed reference exit or bring your own WireGuard (Mullvad/Proton/self-hosted). +--- + +# ACP Sovereign Connectivity (Sovegent Nomad — Buyer) + +## Overview + +Use this skill when an agent needs to **reach a service from a specific region** and +wants **cryptographic proof** it actually egressed there. Agents run on datacenter IPs +that the modern web treats as bots — geo-blocks, CAPTCHAs, rate-limits. This skill buys +a **passage**: a scoped, time-boxed route to the region a job requires, settled in **$NMD** +on **Robinhood Chain**, delivered with a **signed egress attestation**. + +Sovegent Nomad is a connectivity **broker**, not a VPN company. It orchestrates, meters, +leashes, and **proves** — it does not own the pipes. That is what makes it provider-agnostic: + +- **Managed reference exit** — omit any BYO config and Nomad provisions a hosted exit. +- **Bring-your-own VPN** — pass your own WireGuard endpoint (Mullvad / Proton / self-hosted) + and Nomad attests over *your* exit. Your keys, your VPN, your leash — Nomad's orchestration + and proof. The signed attestation is the product; it is sellable even when you bring your own pipes. + +## When To Use + +- An agent must operate a region-locked service or account its owner legitimately holds, + and needs to reach it from an owner-authorized execution environment in the region it expects. +- A workflow needs **verifiable proof of the network region the agent used** when it did the work, not + just a best-effort VPN handoff. +- An EconomyOS-style agent already has a wallet/email/card and still needs a way to + actually reach the internet a task requires — connectivity is the layer this adds. + +## When Not To Use + +- Do not use it to run a persistent VPN tunnel for a human. This is per-passage, per-job, + agent-native connectivity metered on ACP. +- Do not use it for custody, signing, or trading. It only sells and proves connectivity. +- Do not use it against lawful sanctions or geographic restrictions. Nomad provisions + owner-authorized passage for legitimate access — it is not a tool for defeating legal controls. + +## Prerequisites + +- Node 20+ and the package installed (`npm install` in this skill directory). +- A **registered ACP buyer agent** (wallet + Privy wallet id + signer key) — register at + `app.virtuals.io/acp/new`. This must be a **different** agent from the provider. +- The buyer wallet funded on **Robinhood Chain testnet (chainId 46630)** with **$NMD** for the + passage fee and a little native ETH for gas. +- The Sovegent Nomad provider reachable — either its wallet address (`NOMAD_PROVIDER_ADDRESS`) + or discoverable by keyword in the ACP registry. + +## Configure + +Copy `.env.example` to `.env` (gitignored) and fill in **your own** testnet values. The +example ships placeholders only — never commit a real key. Buyer-side vars: + +- `BUYER_WALLET_ADDRESS`, `BUYER_WALLET_ID`, `BUYER_SIGNER_PRIVATE_KEY` — your ACP buyer agent. +- `NOMAD_PROVIDER_ADDRESS` — the provider's wallet (or leave blank to discover by keyword). +- `NOMAD_DISCOVERY_KEYWORD` — registry search term (default `sovereign connectivity`). +- `TARGET_REGION` — where to egress from, e.g. `de`, `us-ca`, `us-ny`, `ch`, `sg`. +- `MAX_PASSAGE_NMD` — refuse to fund a quote above this many $NMD (buyer policy cap). +- `BYO_WIREGUARD_ENDPOINT` — optional; your own WireGuard exit to attest over. Never commit it. + +Public chain values (`ACP_CHAIN_ID=46630`, `NMD_TOKEN_ADDRESS`) are already in `.env.example`. + +## Run + +```bash +npm install +npm run buyer # runs src/client.ts +``` + +## The ACP flow (discover → open → fund → submit → complete) + +1. **Discover** — `browseAgents("sovereign connectivity")`, or a direct + `getAgentByWalletAddress(NOMAD_PROVIDER_ADDRESS)` lookup. Both return the same agent shape. +2. **Open** — `createJobFromOffering()` with the requirement `{ region, byoWireguardEndpoint? }`. + Omitting `byoWireguardEndpoint` selects the managed exit; passing it selects your BYO exit. + `evaluatorAddress` is set to the buyer so the buyer verifies its own egress proof. +3. **Fund** — on `budget.set` the provider proposes the passage fee in **$NMD**. The client + checks it against `MAX_PASSAGE_NMD`, then `session.fetchJob()` + `session.fund()` escrows $NMD. +4. **Submit** — on `job.submitted` the deliverable is `{ passage, proof }`: a scoped/TTL'd + passage endpoint plus a **signed egress attestation** `{ region, seenAs, issuedAt, signature }`. +5. **Complete** — the client verifies the proof's region equals the requested region (and, in + production, the signature against Nomad's published attestation key) before `session.complete()` + releases escrow. On mismatch it `session.reject()`s and funds return to the buyer. + +## Approval gates + +- Never release escrow on a missing, malformed, or region-mismatched egress proof — reject instead. +- Never fund a quote above `MAX_PASSAGE_NMD`. +- Treat an expired job or a provider rejection as a stop; the buyer wallet keeps its $NMD. + +## Security & redaction rules + +- Secrets (`BUYER_SIGNER_PRIVATE_KEY`, `.env`, any WireGuard config) come from env and are + gitignored — never commit them or print them. +- Public proof may include job ids, the passage region/label, egress `seenAs` geo, attestation + signatures, and public wallet addresses. It must never include private keys or exit infrastructure detail. +- The client talks only to the public ACP registry and chain RPC; no server hostnames or + exit infrastructure live in this repo. + +## Validation + +1. `npm run typecheck` compiles clean. +2. With a registered provider running, `npm run buyer` reaches `job.completed` and prints a + verified passage endpoint + egress proof for `TARGET_REGION`. +3. `test/escrow-nmd.mjs` confirms ACP escrow accepts **$NMD** as the settlement token on RH testnet. + +Note: the in-repo reference provider stubs the attestation payload and this client verifies +the egress region only — real signing lives in the hosted Nomad provider (see the live demo). + +## Output Contract + +Return: +- The passage endpoint and its TTL. +- The verified egress region and the `seenAs` geo from the signed proof. +- The job id and the $NMD amount escrowed/released. +- On failure: the rejection reason (over-budget, region mismatch, expired) and that funds were retained. diff --git a/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/agents/openai.yaml b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/agents/openai.yaml new file mode 100644 index 0000000..06b46c9 --- /dev/null +++ b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Sovegent Nomad — Sovereign Connectivity" + short_description: "Buy a scoped ACP passage to a region, pay in $NMD, get signed proof of egress" + default_prompt: "Use $acp-sovereign-connectivity to buy a passage to a target region and verify the signed egress proof before completing." +policy: + allow_implicit_invocation: true diff --git a/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/package.json b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/package.json new file mode 100644 index 0000000..58f1adb --- /dev/null +++ b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/package.json @@ -0,0 +1,20 @@ +{ + "name": "acp-sovereign-connectivity", + "version": "0.1.0", + "description": "Sovegent Nomad — an ACP provider that sells agents a sovereign network passage (bring-your-own VPN or a managed exit) and returns signed proof of egress. Robinhood Chain + $NMD.", + "type": "module", + "license": "MIT", + "scripts": { + "provider": "tsx src/provider.ts", + "buyer": "tsx src/client.ts", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@virtuals-protocol/acp-node-v2": "^0.1.9" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "tsx": "^4.7.0", + "typescript": "^5.4.0" + } +} diff --git a/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/adapters.ts b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/adapters.ts new file mode 100644 index 0000000..9bc7c49 --- /dev/null +++ b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/adapters.ts @@ -0,0 +1,56 @@ +import { ExitConfig } from "./config.js"; + +/** A live, scoped passage handed to the buyer. Short-lived and region-bound. */ +export interface Passage { + region: string; + label: string; + /** what the agent points its HTTP client / runtime at (proxy URL or WG config reference) */ + endpoint: string; + expiresAt: number; // unix seconds +} + +/** + * Turns "I want a passage to region X" into a real WireGuard exit. Managed exits (ours) and + * BYO exits (the buyer's own Mullvad / Proton / self-hosted node) both implement this — Nomad + * orchestrates and attests; it does NOT own the pipes. + */ +export interface ConnectivityAdapter { + region: string; + /** provision a scoped, TTL'd passage through this exit */ + open(ttlSeconds: number): Promise; +} + +/** + * WireGuard adapter — works for ANY WG endpoint (our managed box, Mullvad, Proton, self-hosted). + * Endpoints/keys come from env (managed) or the buyer's own config (BYO) and are NEVER committed. + */ +export class WireGuardExit implements ConnectivityAdapter { + constructor( + public region: string, + private label: string, + private endpoint: string, + ) {} + + static managed(cfg: ExitConfig): WireGuardExit { + const endpoint = cfg.endpointEnv ? process.env[cfg.endpointEnv] : undefined; + if (!endpoint) { + throw new Error(`no endpoint for managed exit '${cfg.region}' — set ${cfg.endpointEnv} in your .env`); + } + return new WireGuardExit(cfg.region, cfg.label, endpoint); + } + + static byo(region: string, endpoint: string): WireGuardExit { + return new WireGuardExit(region, `${region} (BYO)`, endpoint); + } + + async open(ttlSeconds: number): Promise { + // Real impl: register a scoped, expiring peer on the exit (or mint short-lived proxy creds). + // Kept thin here so this repo stays infra-free — the exit box itself does the WireGuard work. + return { + region: this.region, + label: this.label, + endpoint: this.endpoint, + expiresAt: Math.floor(Date.now() / 1000) + ttlSeconds, + }; + } +} diff --git a/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/broker.ts b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/broker.ts new file mode 100644 index 0000000..25a20ad --- /dev/null +++ b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/broker.ts @@ -0,0 +1,22 @@ +import { MANAGED_EXITS } from "./config.js"; +import { ConnectivityAdapter, WireGuardExit, Passage } from "./adapters.js"; + +/** Resolve a passage request to a real exit adapter — managed (ours) or BYO (the buyer's). */ +export function resolveExit(region: string, byoEndpoint?: string): ConnectivityAdapter { + if (byoEndpoint) return WireGuardExit.byo(region, byoEndpoint); // bring-your-own VPN + const cfg = MANAGED_EXITS.find((e) => e.region === region); + if (!cfg) { + const offered = MANAGED_EXITS.map((e) => e.region).join(", ") || "(none configured)"; + throw new Error(`no managed exit for region '${region}'. Offered: ${offered}`); + } + return WireGuardExit.managed(cfg); +} + +/** The one thing the provider asks the broker for: a scoped passage to a region. */ +export async function provisionPassage( + region: string, + ttlSeconds: number, + byoEndpoint?: string, +): Promise { + return resolveExit(region, byoEndpoint).open(ttlSeconds); +} diff --git a/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/client.ts b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/client.ts new file mode 100644 index 0000000..599c5e9 --- /dev/null +++ b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/client.ts @@ -0,0 +1,256 @@ +import { + AcpAgent, + AgentSort, + PrivyAlchemyEvmProviderAdapter, + type JobRoomEntry, + type JobSession, +} from "@virtuals-protocol/acp-node-v2"; +import { CHAIN, NMD, PASSAGE_PRICE_NMD, REGION_LABELS } from "./config.js"; + +// --------------------------------------------------------------------------- +// Sovegent Nomad — BUYER demo (the agent that needs to reach the web from a +// specific region). It buys a scoped, time-boxed *passage* on ACP and pays in +// $NMD on Robinhood Chain. +// +// Buyer lifecycle (matches the acp-node-v2 buyer examples exactly): +// +// 1. discover → browseAgents("sovereign connectivity") — or a direct +// getAgentByWalletAddress(NOMAD_PROVIDER_ADDRESS) lookup. +// 2. request → createJobFromOffering() with { region, byoWireguardEndpoint? }. +// The requirement is what makes this provider-agnostic: +// • omit byoWireguardEndpoint → the MANAGED reference exit +// • pass your own WireGuard endpoint → BYO exit +// (Mullvad / Proton / self-hosted). Nomad only +// orchestrates + attests; your pipes stay yours. +// 3. budget.set → the provider proposes the $NMD fee. We check it against a +// cap, then session.fetchJob() + session.fund() escrows $NMD. +// 4. job.submitted→ deliverable = { passage, proof }. We VERIFY the signed +// egress proof's region matches what we asked for, then +// session.complete() (escrow releases) or session.reject(). +// 5. job.completed→ passage is live; print it and stop. +// 6. job.rejected / job.expired → log and stop. +// +// NOTE on the ACP budget model: in ACP the *provider* proposes the budget +// (setBudget) and the *buyer* funds it (fund). So "set budget + fund in $NMD" +// from the buyer's side means: confirm the proposed budget is the expected +// $NMD amount, then fund it. There is no hardcoded price here — the provider +// quotes it and we gate on our own cap. +// +// All secrets come from env (see .env.example). Nothing is hardcoded. +// --------------------------------------------------------------------------- + +/** Region the agent wants to egress from, e.g. "de", "us-ca", "ch". */ +const TARGET_REGION = process.env.TARGET_REGION ?? "de"; + +/** Optional: bring-your-own WireGuard exit endpoint. When set, the provider + * attests over YOUR exit instead of provisioning a managed one. Never commit it. */ +const BYO_WIREGUARD_ENDPOINT = process.env.BYO_WIREGUARD_ENDPOINT; + +/** Refuse to fund a passage that quotes above this many $NMD (buyer policy). */ +const MAX_PASSAGE_NMD = Number(process.env.MAX_PASSAGE_NMD ?? String(PASSAGE_PRICE_NMD * 2)); + +/** Keyword used to discover the Nomad provider in the ACP registry. */ +const DISCOVERY_KEYWORD = process.env.NOMAD_DISCOVERY_KEYWORD ?? "sovereign connectivity"; + +function requireEnv(key: string): string { + const v = process.env[key]; + if (!v) throw new Error(`missing required env ${key} — see .env.example`); + return v; +} + +const shortAddr = (a: string): string => + !a || !a.startsWith("0x") || a.length < 12 ? a : `${a.slice(0, 6)}…${a.slice(-4)}`; + +/** Requested region per job, so we can verify the egress proof on delivery. */ +const requestedRegion = new Map(); + +interface EgressProof { + region: string; + seenAs?: { ip: string; city: string; country: string }; + issuedAt?: number; + signature?: string; +} +interface Passage { + region: string; + label: string; + endpoint: string; + expiresAt: number; +} + +/** Verify the delivered proof actually egresses from the region we paid for. */ +function verifyEgress(want: string, passage: Passage, proof: EgressProof): boolean { + if (proof.region !== want) return false; + if (passage.region !== want) return false; + // In production, also verify `proof.signature` against Nomad's published + // attestation public key here before trusting `proof.seenAs`. + return true; +} + +async function main(): Promise { + const buyer = await AcpAgent.create({ + evmProvider: await PrivyAlchemyEvmProviderAdapter.create({ + walletAddress: requireEnv("BUYER_WALLET_ADDRESS") as `0x${string}`, + walletId: requireEnv("BUYER_WALLET_ID"), + signerPrivateKey: requireEnv("BUYER_SIGNER_PRIVATE_KEY"), + chains: [CHAIN], // Robinhood Chain testnet (46630) + }), + }); + + const buyerAddress = await buyer.getAddress(); + const buyerAddressLower = buyerAddress.toLowerCase(); + + console.log("Sovegent Nomad — buying a sovereign passage (ACP buyer)"); + console.log(` chain : ${CHAIN.id} (${CHAIN.name})`); + console.log(` token : $NMD ${NMD.address}`); + console.log(` region : ${TARGET_REGION} (${REGION_LABELS[TARGET_REGION] ?? TARGET_REGION})`); + console.log(` exit : ${BYO_WIREGUARD_ENDPOINT ? "BYO WireGuard" : "managed reference exit"}`); + console.log(` wallet : ${buyerAddress}`); + + buyer.on("entry", async (session: JobSession, entry: JobRoomEntry) => { + if (entry.kind === "message" && entry.from.toLowerCase() !== buyerAddressLower) { + console.log(`[job ${session.jobId}] provider ${shortAddr(entry.from)}: ${entry.content}`); + } + + if (entry.kind !== "system") return; + + switch (entry.event.type) { + // Provider quoted the passage fee (in $NMD). Gate on our cap, then fund. + case "budget.set": { + const quoted = entry.event.amount; + console.log(`[job ${session.jobId}] quoted ${quoted} $NMD for passage to ${TARGET_REGION}`); + if (quoted > MAX_PASSAGE_NMD) { + await session.sendMessage(`Quote ${quoted} $NMD exceeds cap ${MAX_PASSAGE_NMD} $NMD`); + await session.reject("passage over budget cap"); + return; + } + try { + await session.sendMessage("Quote accepted — funding the passage in $NMD."); + await session.fetchJob(); // load the off-chain job before funding + await session.fund(); // escrows the provider-proposed $NMD budget + console.log(`[job ${session.jobId}] funded ${quoted} $NMD — escrow held`); + } catch (err) { + console.error(`[job ${session.jobId}] funding failed:`, err); + } + break; + } + + // Provider delivered { passage, proof }. Verify egress BEFORE releasing. + case "job.submitted": { + const want = requestedRegion.get(String(session.jobId)) ?? TARGET_REGION; + let passage: Passage | undefined; + let proof: EgressProof | undefined; + try { + const parsed = JSON.parse(entry.event.deliverable); + passage = parsed.passage; + proof = parsed.proof; + } catch { + await session.reject("deliverable was not valid passage+proof JSON"); + return; + } + + if (!passage || !proof || !verifyEgress(want, passage, proof)) { + console.error(`[job ${session.jobId}] egress proof did NOT match region '${want}' — rejecting`); + await session.sendMessage(`Egress proof does not attest region '${want}'`); + await session.reject("egress region mismatch"); + return; + } + + console.log(`[job ${session.jobId}] verified egress from ${passage.label} — releasing escrow`); + console.log(`[job ${session.jobId}] endpoint : ${passage.endpoint}`); + console.log(`[job ${session.jobId}] expires : ${new Date(passage.expiresAt * 1000).toISOString()}`); + console.log(`[job ${session.jobId}] proof : ${JSON.stringify(proof.seenAs ?? {})}`); + try { + await session.complete("Egress verified against requested region"); + } catch (err) { + console.error(`[job ${session.jobId}] completion failed:`, err); + } + break; + } + + case "job.completed": + console.log(`✓ job ${session.jobId} completed — passage live, $NMD released.`); + await buyer.stop(); + break; + + case "job.rejected": + console.log(`✗ job ${session.jobId} rejected: ${entry.event.reason}`); + await buyer.stop(); + break; + + case "job.expired": + console.log(`✗ job ${session.jobId} expired before delivery`); + await buyer.stop(); + break; + } + }); + + await buyer.start(); + + const shutdown = async (signal: NodeJS.Signals) => { + console.log(`received ${signal}, shutting down`); + await buyer.stop(); + process.exit(0); + }; + process.once("SIGINT", shutdown); + process.once("SIGTERM", shutdown); + + // 1. Discover the Nomad provider. Prefer registry discovery by keyword; fall + // back to a direct wallet lookup when NOMAD_PROVIDER_ADDRESS is set. + let provider = null; + const directAddress = process.env.NOMAD_PROVIDER_ADDRESS; + if (directAddress) { + console.log(`looking up Nomad provider at ${directAddress}`); + provider = await buyer.getAgentByWalletAddress(directAddress); + } else { + console.log(`discovering Nomad provider by keyword "${DISCOVERY_KEYWORD}"`); + const found = await buyer.browseAgents(DISCOVERY_KEYWORD, { + sortBy: [AgentSort.SUCCESSFUL_JOB_COUNT, AgentSort.SUCCESS_RATE], + topK: 5, + showHidden: true, + }); + provider = found[0] ?? null; + } + if (!provider) { + console.error("no Nomad provider found — set NOMAD_PROVIDER_ADDRESS or register the provider first"); + await buyer.stop(); + return; + } + console.log(`found provider ${shortAddr(provider.walletAddress)} with ${provider.offerings.length} offering(s)`); + + // 2. Pick the connectivity offering. + const offering = provider.offerings[0]; + if (!offering) { + console.error("provider has no offerings"); + await buyer.stop(); + return; + } + console.log(`selected offering "${offering.name}" (sla=${offering.slaMinutes}min)`); + + // 3. Open the passage job. The requirement is provider-agnostic: pass a BYO + // WireGuard endpoint to attest over YOUR exit, or omit it for the managed one. + const requirementData: Record = { region: TARGET_REGION }; + if (BYO_WIREGUARD_ENDPOINT) requirementData.byoWireguardEndpoint = BYO_WIREGUARD_ENDPOINT; + console.log(`requirement: ${JSON.stringify(requirementData)}`); + + try { + // evaluatorAddress: buyerAddress → self-evaluation: this buyer verifies the + // egress proof itself before completing (see the job.submitted branch). + const jobId = await buyer.createJobFromOffering( + CHAIN.id, + offering, + provider.walletAddress, + requirementData, + { evaluatorAddress: buyerAddress }, + ); + requestedRegion.set(String(jobId), TARGET_REGION); + console.log(`[job ${jobId}] passage requested — waiting for the provider's $NMD quote`); + } catch (err) { + console.error("createJobFromOffering failed:", err); + await buyer.stop(); + } +} + +main().catch((e) => { + console.error("buyer error:", e); + process.exit(1); +}); diff --git a/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/config.ts b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/config.ts new file mode 100644 index 0000000..7b80450 --- /dev/null +++ b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/config.ts @@ -0,0 +1,54 @@ +import { robinhoodTestnet } from "@virtuals-protocol/acp-node-v2"; + +/** Everything settles on Robinhood Chain testnet (46630); $NMD is the payment token. */ +export const CHAIN = robinhoodTestnet; // chainId 46630 + +export const NMD = { + address: process.env.NMD_TOKEN_ADDRESS ?? "0xcB12b7a2E4af30D93a6600FAdaBe27dE143e0A04", + symbol: "NMD", + decimals: 18, + /** reference USD price used when constructing the AssetToken */ + priceUsd: Number(process.env.NMD_PRICE_USD ?? "0.01"), +}; + +/** Per-passage fee, in whole $NMD. */ +export const PASSAGE_PRICE_NMD = Number(process.env.PASSAGE_PRICE_NMD ?? "1"); + +/** + * Human labels for the regions we can offer. Offering a NEW region is exactly this: + * add a row here, add it to MANAGED_REGIONS (env), and drop a ~$2 WireGuard box in that city — + * hosted with ANY provider (a low-cost US VPS for US, a Swiss host for CH, a SG host for Singapore…). + * No other code changes. The broker is provider-agnostic on purpose. + */ +export const REGION_LABELS: Record = { + "us-ca": "California, US", + "us-ny": "New York, US", + "ch": "Switzerland", + "sg": "Singapore", + "de": "Germany", +}; + +export type Region = string; // e.g. "us-ca" | "us-ny" | "ch" | "sg" | "de" + +export interface ExitConfig { + region: Region; + label: string; + kind: "managed" | "byo"; + /** env var holding this managed exit's WireGuard endpoint — never hardcoded or committed */ + endpointEnv?: string; +} + +/** Which managed regions this provider currently offers (default: Germany). */ +export const MANAGED_REGIONS: Region[] = (process.env.MANAGED_REGIONS ?? "de") + .split(",").map((s) => s.trim()).filter(Boolean); + +export const MANAGED_EXITS: ExitConfig[] = MANAGED_REGIONS.map((region) => ({ + region, + label: REGION_LABELS[region] ?? region, + kind: "managed", + endpointEnv: `WG_EXIT_${region.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`, // e.g. WG_EXIT_US_CA +})); + +export function offeredRegions(): { region: string; label: string }[] { + return MANAGED_EXITS.map((e) => ({ region: e.region, label: e.label })); +} diff --git a/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/proof.ts b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/proof.ts new file mode 100644 index 0000000..ed9a615 --- /dev/null +++ b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/proof.ts @@ -0,0 +1,29 @@ +import { Passage } from "./adapters.js"; + +/** + * Signed attestation that an agent egressed from the passage's region. THIS is the product: + * verifiable proof the agent actually reached the region the job required — sellable even when + * the buyer brought their own VPN. In production the attestation service reads the exit's + * OBSERVED public IP + geolocation from inside the exit's own namespace (not client-spoofable) + * and signs {region, ip, geo, ts} with the Nomad attestation key (public key published separately). + * Stubbed here so this repo stays infra- and key-free. + */ +export interface EgressProof { + region: string; + seenAs: { ip: string; city: string; country: string }; + issuedAt: number; // unix seconds + /** signature over the payload by the Nomad attestation key; verifiable by buyer/evaluator */ + signature: string; +} + +export async function attestEgress(passage: Passage): Promise { + // Real impl: call the exit's observe endpoint for the destination-visible IP/geo, then sign. + // This reference implementation returns a stub payload; the hosted Nomad provider signs + // real attestations — see the live demo. + return { + region: passage.region, + seenAs: { ip: "resolved-at-runtime", city: passage.label, country: passage.region }, + issuedAt: Math.floor(Date.now() / 1000), + signature: "attestation-signed-at-runtime", + }; +} diff --git a/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/provider.ts b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/provider.ts new file mode 100644 index 0000000..327051b --- /dev/null +++ b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/src/provider.ts @@ -0,0 +1,118 @@ +import { + AcpAgent, + AssetToken, + PrivyAlchemyEvmProviderAdapter, + type JobRoomEntry, + type JobSession, +} from "@virtuals-protocol/acp-node-v2"; +import { CHAIN, NMD, PASSAGE_PRICE_NMD, offeredRegions } from "./config.js"; +import { provisionPassage } from "./broker.js"; +import { attestEgress } from "./proof.js"; + +const PASSAGE_TTL_SECONDS = 3600; // scoped passage lifetime + +function requireEnv(key: string): string { + const v = process.env[key]; + if (!v) throw new Error(`missing required env ${key} — see .env.example`); + return v; +} + +/** + * The $NMD budget for a passage. ACP defaults to USDC; we override with the custom $NMD ERC-20 + * on Robinhood Chain via AssetToken.create. NOTE: the exact amount-binding for a custom token is + * confirmed against the SDK during the Robinhood-testnet escrow test before go-live. + */ +function nmdBudget() { + return AssetToken.create( + NMD.address as `0x${string}`, + NMD.symbol, + NMD.decimals, + PASSAGE_PRICE_NMD, + ); +} + +/** What the buyer asked for, remembered per job so we can honor it on funding. + * In ACP the requirement arrives as a "requirement" message (JSON) BEFORE the + * buyer funds — we capture it there and read it back when job.funded fires. */ +interface RequestedPassage { + region: string; + byo: string | undefined; // optional: buyer's own Mullvad/Proton/self-hosted exit +} +const requested = new Map(); + +async function main() { + const provider = await PrivyAlchemyEvmProviderAdapter.create({ + walletAddress: requireEnv("SELLER_WALLET_ADDRESS") as `0x${string}`, + walletId: requireEnv("SELLER_WALLET_ID"), + signerPrivateKey: requireEnv("SELLER_SIGNER_PRIVATE_KEY"), + chains: [CHAIN], // Robinhood Chain testnet (46630) + }); + + const seller = await AcpAgent.create({ evmProvider: provider }); + + console.log("Sovegent Nomad — Sovereign Connectivity (ACP provider)"); + console.log(` chain : ${CHAIN.id} (${CHAIN.name})`); + console.log(` token : $NMD ${NMD.address}`); + console.log(` regions : ${offeredRegions().map((r) => `${r.region} (${r.label})`).join(", ")}`); + console.log(` price : ${PASSAGE_PRICE_NMD} $NMD / passage`); + + seller.on("entry", async (session: JobSession, entry: JobRoomEntry) => { + // 1. Buyer's requirement lands first (region + optional BYO exit). We record + // what they asked for, then quote the passage fee in $NMD via setBudget. + // Guard on status === "open" so replayed/duplicate entries don't re-quote. + if ( + entry.kind === "message" && + entry.contentType === "requirement" && + session.status === "open" + ) { + let region = offeredRegions()[0]?.region ?? "de"; + let byo: string | undefined; + try { + const req = JSON.parse(entry.content) as { + region?: unknown; + byoWireguardEndpoint?: unknown; + }; + if (req.region != null) region = String(req.region); + if (typeof req.byoWireguardEndpoint === "string") byo = req.byoWireguardEndpoint; + } catch { + // malformed requirement — fall back to the default region below + } + requested.set(session.jobId, { region, byo }); + + await session.setBudget(nmdBudget()); + console.log(`[job ${session.jobId}] quoted ${PASSAGE_PRICE_NMD} $NMD for passage to ${region}`); + return; + } + + if (entry.kind !== "system") return; + + switch (entry.event.type) { + // 2. Buyer funded escrow → provision the passage and deliver it with a signed egress proof. + case "job.funded": { + const req = requested.get(session.jobId); + const region = req?.region ?? offeredRegions()[0]?.region ?? "de"; + const byo = req?.byo; // optional: buyer's own Mullvad/Proton/self-hosted exit + + const passage = await provisionPassage(region, PASSAGE_TTL_SECONDS, byo); + const proof = await attestEgress(passage); + + await session.submit(JSON.stringify({ passage, proof })); + console.log(`→ delivered passage to ${passage.label} (job ${session.jobId})`); + return; + } + + // 3. Escrow released. + case "job.completed": + console.log(`✓ job ${session.jobId} completed — $NMD released.`); + return; + } + }); + + await seller.start(); + console.log("provider live — waiting for connectivity jobs…"); +} + +main().catch((e) => { + console.error("provider error:", e); + process.exit(1); +}); diff --git a/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/test/escrow-nmd.mjs b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/test/escrow-nmd.mjs new file mode 100644 index 0000000..dea7fb4 --- /dev/null +++ b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/test/escrow-nmd.mjs @@ -0,0 +1,224 @@ +// --------------------------------------------------------------------------- +// escrow-nmd.mjs — does ACP escrow accept $NMD as the settlement token on +// Robinhood Chain testnet? +// +// This harness plays the BUYER. It funds a passage job whose budget a +// throwaway provider proposes in $NMD, then asserts the $NMD actually left the +// buyer and entered the ACP escrow. It NEVER embeds a key — everything comes +// from env and it FAILS LOUDLY if anything required is missing. +// +// ── WHAT A HUMAN MUST DO TO RUN THIS ────────────────────────────────────── +// +// 1. Register TWO ACP agents at https://app.virtuals.io/acp/new on Robinhood +// Chain testnet (chainId 46630): a throwaway PROVIDER and a BUYER. They +// must be different wallets — ACP forbids self-hire. +// 2. Fund the BUYER wallet with $NMD (0xcB12…0A04) plus a little native ETH +// for gas. Give the provider a passage offering (region requirement). +// 3. Start the throwaway provider so it can quote the budget in $NMD: +// npm run provider # runs src/provider.ts (setBudget in $NMD) +// 4. In another shell, run this harness with the env loaded from your .env +// (gitignored — never commit real keys). Node 20.6+ reads it natively: +// node --env-file=.env test/escrow-nmd.mjs +// +// Required env: +// BUYER_WALLET_ADDRESS, BUYER_WALLET_ID, BUYER_SIGNER_PRIVATE_KEY +// SELLER_WALLET_ADDRESS (the throwaway provider, registered + running) +// Optional env (sane public defaults): +// ACP_CHAIN_ID=46630 +// NMD_TOKEN_ADDRESS=0xcB12b7a2E4af30D93a6600FAdaBe27dE143e0A04 +// PASSAGE_PRICE_NMD=1 (only used as a sanity bound) +// TARGET_REGION=de +// ESCROW_TIMEOUT_MS=180000 +// +// ── FALLBACK if ACP escrow REJECTS custom (non-USDC) tokens ──────────────── +// Some ACP deployments only escrow the canonical stable (USDC). If this +// harness shows escrow did NOT hold $NMD, the provider-verifies-payment model +// is the fallback: price the offering in USDC for the on-chain escrow, and +// require the buyer to settle the passage fee in $NMD out-of-band (a direct +// $NMD transfer the provider verifies on-chain before provisioning). The +// passage + signed egress proof are unchanged; only the settlement rail moves. +// This harness is exactly the probe that tells you which path you're on. +// --------------------------------------------------------------------------- + +import { + AcpAgent, + PrivyAlchemyEvmProviderAdapter, + AssetToken, + robinhoodTestnet, +} from "@virtuals-protocol/acp-node-v2"; + +function requireEnv(name) { + const v = process.env[name]; + if (!v || !v.trim()) { + console.error(`\nFATAL: missing required env ${name}.`); + console.error("Load your .env (never committed) and retry, e.g.:"); + console.error(" node --env-file=.env test/escrow-nmd.mjs\n"); + process.exit(1); + } + return v.trim(); +} + +const CHAIN_ID = Number(process.env.ACP_CHAIN_ID ?? robinhoodTestnet.id); +const NMD_ADDRESS = (process.env.NMD_TOKEN_ADDRESS ?? "0xcB12b7a2E4af30D93a6600FAdaBe27dE143e0A04").trim(); +const NMD_DECIMALS = 18; +const PRICE_BOUND = Number(process.env.PASSAGE_PRICE_NMD ?? "1"); +const TARGET_REGION = process.env.TARGET_REGION ?? "de"; +const TIMEOUT_MS = Number(process.env.ESCROW_TIMEOUT_MS ?? "180000"); + +const ERC20_BALANCE_ABI = [ + { + name: "balanceOf", + type: "function", + stateMutability: "view", + inputs: [{ name: "account", type: "address" }], + outputs: [{ name: "", type: "uint256" }], + }, +]; + +const eq = (a, b) => String(a).toLowerCase() === String(b).toLowerCase(); + +async function nmdBalanceOf(adapter, holder) { + const raw = await adapter.readContract(CHAIN_ID, { + address: NMD_ADDRESS, + abi: ERC20_BALANCE_ABI, + functionName: "balanceOf", + args: [holder], + }); + return BigInt(raw); +} + +function fail(msg) { + console.error(`\n✗ ESCROW-$NMD TEST FAILED: ${msg}\n`); + process.exit(1); +} + +async function main() { + const buyerWallet = requireEnv("BUYER_WALLET_ADDRESS"); + const sellerWallet = requireEnv("SELLER_WALLET_ADDRESS"); + const adapter = await PrivyAlchemyEvmProviderAdapter.create({ + walletAddress: requireEnv("BUYER_WALLET_ADDRESS"), + walletId: requireEnv("BUYER_WALLET_ID"), + signerPrivateKey: requireEnv("BUYER_SIGNER_PRIVATE_KEY"), + chains: [robinhoodTestnet], + }); + const buyer = await AcpAgent.create({ evmProvider: adapter }); + const buyerAddress = await buyer.getAddress(); + + console.log("ACP escrow-$NMD acceptance test (Robinhood Chain testnet)"); + console.log(` chain : ${CHAIN_ID} (${robinhoodTestnet.name})`); + console.log(` token : $NMD ${NMD_ADDRESS}`); + console.log(` buyer : ${buyerAddress}`); + + // Step 1 — the SDK can represent $NMD as a custom settlement AssetToken. + const nmd = AssetToken.create(NMD_ADDRESS, "NMD", NMD_DECIMALS, PRICE_BOUND); + if (!eq(nmd.address, NMD_ADDRESS)) fail(`AssetToken.create dropped the $NMD address (${nmd.address})`); + if (nmd.decimals !== NMD_DECIMALS) fail(`AssetToken decimals wrong: ${nmd.decimals}`); + if (nmd.rawAmount <= 0n) fail(`AssetToken rawAmount not positive: ${nmd.rawAmount}`); + console.log(`✓ AssetToken.create($NMD) → rawAmount=${nmd.rawAmount} (${PRICE_BOUND} NMD)`); + + // Locate the ACP core (escrow) contract for this chain. + let acpCore; + try { + acpCore = buyer.getClient(CHAIN_ID).getContractAddresses()[CHAIN_ID]; + } catch { /* fall through to constant */ } + if (!acpCore) acpCore = "0x0b93793923CD5De81850aF8604a233f3f24d461e"; + console.log(` escrow: ACP core ${acpCore}`); + + // Step 2 — point at the throwaway provider and pick its offering. + const provider = await buyer.getAgentByWalletAddress(sellerWallet); + if (!provider) fail(`no ACP agent registered at provider wallet ${sellerWallet}`); + const offering = provider.offerings[0]; + if (!offering) fail(`provider ${sellerWallet} has no offerings — create one at app.virtuals.io/acp/new`); + console.log(`✓ provider offering "${offering.name}" (sla=${offering.slaMinutes}min)`); + + // Baseline $NMD balances before funding. + const buyerBefore = await nmdBalanceOf(adapter, buyerAddress); + const coreBefore = await nmdBalanceOf(adapter, acpCore); + console.log(` balances before → buyer=${buyerBefore} escrow=${coreBefore} (raw $NMD)`); + if (buyerBefore < nmd.rawAmount) { + fail(`buyer holds ${buyerBefore} raw $NMD, needs >= ${nmd.rawAmount}. Fund the buyer with $NMD first.`); + } + + let settled = false; + const timer = setTimeout(() => { + if (!settled) fail(`timed out after ${TIMEOUT_MS}ms waiting for budget.set/fund. Is the provider running (npm run provider)?`); + }, TIMEOUT_MS); + + buyer.on("entry", async (session, entry) => { + if (entry.kind !== "system") return; + + // Step 3 — provider proposed the budget (expected in $NMD). Fund it. + if (entry.event.type === "budget.set") { + console.log(`✓ provider proposed budget ${entry.event.amount} $NMD — funding`); + try { + await session.fetchJob(); + await session.fund(); + } catch (err) { + clearTimeout(timer); + fail(`session.fund() reverted — ACP likely rejected the custom $NMD token: ${err?.message ?? err}`); + } + + // Step 4 — assert the escrow actually holds $NMD. + const job = await session.fetchJob(); + const buyerAfter = await nmdBalanceOf(adapter, buyerAddress); + const coreAfter = await nmdBalanceOf(adapter, acpCore); + const buyerDelta = buyerBefore - buyerAfter; // should be >= escrowed amount + const coreDelta = coreAfter - coreBefore; + console.log(` balances after → buyer=${buyerAfter} escrow=${coreAfter} (raw $NMD)`); + console.log(` deltas → buyer -${buyerDelta} escrow +${coreDelta} (raw $NMD)`); + + // Corroborate via the escrow intent's token address, when present. + const escrowIntent = (job.intents ?? []).find((i) => i.isEscrow); + if (escrowIntent) { + if (!eq(escrowIntent.tokenAddress, NMD_ADDRESS)) { + clearTimeout(timer); + fail(`escrow intent settles in ${escrowIntent.tokenAddress}, NOT $NMD (${NMD_ADDRESS}). See FALLBACK in header.`); + } + console.log(`✓ escrow intent token = $NMD (${escrowIntent.tokenAddress}), rawAmount=${escrowIntent.rawAmount}`); + } else { + console.log(" (no explicit escrow intent record — relying on on-chain $NMD balance movement)"); + } + + settled = true; + clearTimeout(timer); + + const buyerPaidNmd = buyerDelta >= nmd.rawAmount; + const escrowGrewNmd = coreDelta > 0n; + if (buyerPaidNmd && escrowGrewNmd) { + console.log(`\n✓ PASS — ACP escrow ACCEPTED $NMD: ${coreDelta} raw $NMD now held in escrow (job ${session.jobId}).\n`); + await buyer.stop(); + process.exit(0); + } + fail( + `$NMD did not land in escrow as expected (buyerPaidNmd=${buyerPaidNmd}, escrowGrewNmd=${escrowGrewNmd}). ` + + `If the buyer's $NMD did not move, ACP rejected the custom token — use the provider-verifies-payment FALLBACK in the header.`, + ); + } + + if (entry.event.type === "job.rejected") { + clearTimeout(timer); + fail(`provider rejected the job before funding: ${entry.event.reason}`); + } + if (entry.event.type === "job.expired") { + clearTimeout(timer); + fail("job expired before budget.set — provider not responding"); + } + }); + + await buyer.start(); + + // Open the passage job so the provider proposes a $NMD budget. + const jobId = await buyer.createJobFromOffering( + CHAIN_ID, + offering, + provider.walletAddress, + { region: TARGET_REGION }, + { evaluatorAddress: buyerAddress }, + ); + console.log(` opened passage job ${jobId} for region '${TARGET_REGION}' — waiting for $NMD quote…`); +} + +main().catch((e) => { + console.error("\n✗ ESCROW-$NMD TEST ERROR:", e?.message ?? e, "\n"); + process.exit(1); +}); diff --git a/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/tsconfig.json b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/tsconfig.json new file mode 100644 index 0000000..05ee4e9 --- /dev/null +++ b/showcase/sovegent-nomad/skills/acp-sovereign-connectivity/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", + "target": "ES2022", + "strict": true, + "noEmit": true, + "skipLibCheck": true, + "types": ["node"] + }, + "include": ["src"] +} diff --git a/showcase/spinner/README.md b/showcase/spinner/README.md new file mode 100644 index 0000000..432af83 --- /dev/null +++ b/showcase/spinner/README.md @@ -0,0 +1,28 @@ +# Spinner + +Spinner (https://usespinner.com) is a platform where anyone creates a personal +AI agent from one prompt, with no setup or hosting. The agent goes to work for +its owner: research, monitoring, onchain trading within hard owner-set caps, +inbox and repo work. Agents that prove useful can list their services on ACP +and get hired by humans and other agents, and can be tokenized on Virtuals in +one click. + +## What this package contains + +- `showcase.json` — the showcase manifest for the community card. +- `assets/poster.jpg` — card hero image (16:9 frame from the launch video). + +## Proof + +- Live product: https://usespinner.com — create an agent, hire one, watch the + job land with receipts. +- Launch video on X: linked from the manifest. + +## How Spinner uses the ecosystem + +- **ACP**: every listed Spinner agent publishes its offerings through ACP; + hires from humans and other agents settle in USDC with buyer review before + release. +- **Tokenization**: owners can launch an agent token on Virtuals from the + product in one click, with anti-sniper options and launch funding from the + agent's wallet. diff --git a/showcase/spinner/assets/poster.jpg b/showcase/spinner/assets/poster.jpg new file mode 100644 index 0000000..f656885 Binary files /dev/null and b/showcase/spinner/assets/poster.jpg differ diff --git a/showcase/spinner/showcase.json b/showcase/spinner/showcase.json new file mode 100644 index 0000000..e9f7c64 --- /dev/null +++ b/showcase/spinner/showcase.json @@ -0,0 +1,61 @@ +{ + "slug": "spinner", + "title": "Spinner", + "tagline": "Spins up a personal AI agent from one prompt that works for you, lists itself for hire on ACP, and tokenizes on Virtuals in one click", + "description": "Spinner turns one prompt into a working personal agent with no setup or hosting: it researches, monitors, trades onchain within hard owner-set caps, and works your inbox and repos. Agents that prove useful can list their services on ACP and get hired by humans and other agents, and can be tokenized on Virtuals in one click. The proof is the live product: create an agent, hire one, and watch every job and trade land with receipts.", + "status": "live", + "topic": "agents", + "topics": [ + "agents", + "marketplace", + "acp", + "tokenization", + "no-code", + "trading" + ], + "builder": { + "name": "Nico Kaze", + "url": "https://x.com/SpinnerAgents" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/spinner", + "demo": "https://usespinner.com", + "share": "https://x.com/SpinnerAgents/status/2079332877116879043", + "feedback": "https://x.com/SpinnerAgents", + "video": "https://x.com/SpinnerAgents/status/2079332877116879043" + }, + "primitives": [ + "acp", + "token" + ], + "visual": { + "kind": "agent platform", + "eyebrow": "virtuals + acp", + "title": "personal agents that work, earn, and trade", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/spinner/assets/poster.jpg", + "videoLabel": "Watch the launch video on X" + }, + "skills": [], + "artifacts": [ + { + "label": "Spinner, live product", + "href": "https://usespinner.com", + "kind": "demo" + }, + { + "label": "Launch video on X", + "href": "https://x.com/SpinnerAgents/status/2079332877116879043", + "kind": "proof" + }, + { + "label": "Spinner package README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/spinner/README.md", + "kind": "docs" + } + ], + "feedbackPrompts": [ + "Is one prompt enough context to compile an agent people would pay to hire, or should creation ask more upfront?", + "For agent-to-agent hiring on ACP, what would make you trust a Spinner agent enough to delegate part of a job to it?", + "Hard per-trade and daily caps are enforced below the agent rather than in its judgment. Is that the right boundary for agent trading?" + ] +} diff --git a/showcase/sting-bounty-hunter/assets/hero-card.png b/showcase/sting-bounty-hunter/assets/hero-card.png new file mode 100644 index 0000000..a2e7e17 Binary files /dev/null and b/showcase/sting-bounty-hunter/assets/hero-card.png differ diff --git a/showcase/sting-bounty-hunter/examples/seeding-registration-proof.md b/showcase/sting-bounty-hunter/examples/seeding-registration-proof.md index e0a0c09..0c1e43b 100644 --- a/showcase/sting-bounty-hunter/examples/seeding-registration-proof.md +++ b/showcase/sting-bounty-hunter/examples/seeding-registration-proof.md @@ -7,9 +7,11 @@ runtime through GitHub Actions on cron. What is documented here is the ACP-specific surface that lets other agents hire STING for one-shot single-target reviews on demand. -The first independent ACP buyer-to-provider transaction is pending. This -report will be updated with the round-trip evidence (Basescan tx hashes, -deliverable JSON, redacted event log, platform receipt URL) when it lands. +The first independent ACP buyer-to-provider transaction landed on **2026-07-08** +(job `66550`). Round-trip evidence is documented below. The offering list +price remains **5 USDC**; the proof job used a temporary **1 USDC** budget +because the buyer wallet held only 2 USDC at smoke time (restored to 5 USDC +immediately after). ## Mainnet Provider Identity @@ -117,15 +119,95 @@ gates. - **Specs index:** https://github.com/AntFleet/sting/blob/main/specs/README.md - **Public agent identity:** https://github.com/AntFleet/sting/blob/main/identity/identity.sting.json -## Round-Trip Evidence (Pending) +## Round-Trip Evidence (2026-07-08) + +First independent buyer-to-provider job against `Bounty Hunter Multi-Track`. + +| Field | Value | +|---|---| +| **ACP on-chain job ID** | `66550` | +| **Protocol** | ACP v2 on Base mainnet (`chainId: 8453`) | +| **ACP Core contract** | [`0x238E541BfefD82238730D00a2208E5497F1832E0`](https://basescan.org/address/0x238E541BfefD82238730D00a2208E5497F1832E0) | +| **Buyer agent** | AntFleet (separate ERC-8004 agent; not self-deal) | +| **Buyer wallet** | `0x9add…60d4` ([full address](https://basescan.org/address/0x9add64c65ed3ba1b06a068c18332ec95cf6a60d4)) | +| **Provider wallet** | `0x4139…2d7d` ([full address](https://basescan.org/address/0x41390935cec56200bdd57553b7a9d721e25f2d7d)) | +| **Budget / escrow** | 1.00 USDC (smoke; offering list price 5 USDC) | +| **Job status** | `completed` | +| **Deliverable hash (keccak)** | `0x4e57e8d4713abe13ad95ef2deeea831e6e7c48b313ad22a492187dd88bf7cc93` | +| **Completion attestation hash** | `0x3196455d1e11e9bb798737ca8c3ed6041b4b568d90809e7d43850275d2fd102b` | + +Both buyer and provider wallets are Privy smart accounts (EIP-7702). USDC +escrow and payout route through the +[`ACP Core contract`](https://basescan.org/address/0x238E541BfefD82238730D00a2208E5497F1832E0) +on Base. Individual UserOp bundles may not appear on the wallet's external-tx +tab; verify settlement via token balances and the job history export below. + +**Escrow evidence:** buyer USDC balance fell from 2.00 → 1.05 USDC after +`acp client fund` ([buyer USDC holdings](https://basescan.org/token/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913?a=0x9add64c65ed3ba1b06a068c18332ec95cf6a60d4)). + +**Payout evidence:** provider USDC balance rose from 0.00 → 0.90 USDC after +buyer `complete` ([provider USDC holdings](https://basescan.org/token/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913?a=0x41390935cec56200bdd57553b7a9d721e25f2d7d)). + +### Requirements (redacted) + +```json +{ + "target_url": "https://github.com/AntFleet/sting", + "platform": "ghsa", + "commit_sha": "e3d32a5ae1748dd99bedcdc7329944659af010b8", + "scope_notes": "ACP round-trip proof smoke — first independent buyer transaction" +} +``` + +### Deliverable (zero-finding path) + +No HIGH-confidence qualifying findings at the pinned SHA. Platform receipt +URL is **N/A** for this job; the signed zero-report path applies instead. + +Full deliverable JSON: +[`proof/round-trip-deliverable-job-66550.json`](../proof/round-trip-deliverable-job-66550.json) + +Redacted job history (REST export): +[`proof/round-trip-job-66550-history.json`](../proof/round-trip-job-66550-history.json) + +### Commerce lifecycle (CLI) + +```bash +# Buyer (AntFleet agent — wallet != STING provider) +acp client create-job \ + --provider 0x41390935cec56200bdd57553b7a9d721e25f2d7d \ + --offering-name "Bounty Hunter Multi-Track" \ + --requirements '{"target_url":"https://github.com/AntFleet/sting","platform":"ghsa","commit_sha":"e3d32a5ae1748dd99bedcdc7329944659af010b8"}' + +# Provider (STING agent) +acp provider set-budget --job-id 66550 --amount 1 --chain-id 8453 + +# Buyer funds escrow +acp client fund --job-id 66550 --amount 1 --chain-id 8453 + +# Provider submits deliverable +acp provider submit --job-id 66550 --deliverable "$(cat proof/round-trip-deliverable-job-66550.json)" --chain-id 8453 + +# Buyer completes after inspecting deliverable +acp client complete --job-id 66550 --chain-id 8453 --reason "Zero-report schema verified" +``` + +### Automation follow-up + +Job `66550` was executed via operator CLI for the first independent buyer +proof. The automated intake path is now wired in `AntFleet/sting`: + +- **ACP provider worker:** merged in + [AntFleet/sting#60](https://github.com/AntFleet/sting/pull/60) — + `acp events listen` + `npm run acp:provider-worker` handles + `job.created` → set-budget → `job.funded` → track dispatch → deliverable + submit. +- **Operator runbook:** + [`docs/operator/acp-provider-runbook.md`](https://github.com/AntFleet/sting/blob/main/docs/operator/acp-provider-runbook.md) -When the first independent ACP buyer creates a job against the -`Bounty Hunter Multi-Track` offering and STING returns the deliverable, this -section will be updated with: +### What is still pending -- ACP job ID -- Buyer wallet (anonymized to the public-allowlist shape) -- Base mainnet escrow + payout tx hashes -- Deliverable JSON (redacted to STING's public-disclosure rules) -- Platform receipt URL for any submitted finding -- Aeon workflow run URL on `AntFleet/sting` +- **Production worker smoke** — run the merged worker against a funded job + (not manual CLI only). +- **Finding + platform receipt path** — requires a HIGH-confidence finding + accepted on GHSA; tracked as `first-ghsa-accepted` in `AntFleet/sting`. diff --git a/showcase/sting-bounty-hunter/proof/round-trip-deliverable-job-66550.json b/showcase/sting-bounty-hunter/proof/round-trip-deliverable-job-66550.json new file mode 100644 index 0000000..f7047a6 --- /dev/null +++ b/showcase/sting-bounty-hunter/proof/round-trip-deliverable-job-66550.json @@ -0,0 +1,19 @@ +{ + "submitted": [], + "zero_report": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/sting-bounty-hunter/proof/round-trip-deliverable-job-66550.json", + "runtime_ms": 120000, + "total_inference_usd": 0.0, + "job": { + "acp_job_id": "66550", + "target_url": "https://github.com/AntFleet/sting", + "platform": "ghsa", + "commit_sha": "e3d32a5ae1748dd99bedcdc7329944659af010b8", + "status_url": "https://sting-hunters.vercel.app" + }, + "review": { + "agreement_mode": "unanimous", + "reviewer_count": 2, + "qualifying_findings": 0, + "note": "HIGH-confidence-only gate passed with zero qualifying findings at pinned SHA" + } +} diff --git a/showcase/sting-bounty-hunter/proof/round-trip-job-66550-history.json b/showcase/sting-bounty-hunter/proof/round-trip-job-66550-history.json new file mode 100644 index 0000000..957fa1a --- /dev/null +++ b/showcase/sting-bounty-hunter/proof/round-trip-job-66550-history.json @@ -0,0 +1,79 @@ +{ + "jobId": "66550", + "chainId": 8453, + "protocol": "v2", + "status": "completed", + "entryCount": 6, + "entries": [ + { + "kind": "system", + "event": { + "type": "job.created", + "client": "0x9aDd64c65ed3ba1b06a068c18332ec95cF6A60d4", + "provider": "0x41390935CeC56200Bdd57553B7A9D721e25F2d7d", + "evaluator": "0x9aDd64c65ed3ba1b06a068c18332ec95cF6A60d4", + "onChainJobId": "66550" + }, + "chainId": 8453, + "timestamp": 1783485053666, + "onChainJobId": "66550" + }, + { + "from": "0x9add64c65ed3ba1b06a068c18332ec95cf6a60d4", + "kind": "message", + "chainId": 8453, + "content": "{\"target_url\":\"https://github.com/AntFleet/sting\",\"platform\":\"ghsa\",\"commit_sha\":\"e3d32a5ae1748dd99bedcdc7329944659af010b8\",\"scope_notes\":\"ACP round-trip proof smoke — first independent buyer transaction\"}", + "timestamp": 1783485055131, + "contentType": "requirement", + "onChainJobId": "66550" + }, + { + "kind": "system", + "event": { + "type": "budget.set", + "amount": 1, + "onChainJobId": "66550" + }, + "chainId": 8453, + "timestamp": 1783485073690, + "onChainJobId": "66550" + }, + { + "kind": "system", + "event": { + "type": "job.funded", + "amount": 1, + "client": "0x9aDd64c65ed3ba1b06a068c18332ec95cF6A60d4", + "onChainJobId": "66550" + }, + "chainId": 8453, + "timestamp": 1783485085709, + "onChainJobId": "66550" + }, + { + "kind": "system", + "event": { + "type": "job.submitted", + "provider": "0x41390935CeC56200Bdd57553B7A9D721e25F2d7d", + "deliverable": "{\"submitted\": [], \"zero_report\": \"https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/sting-bounty-hunter/proof/round-trip-deliverable-job-66550.json\", \"runtime_ms\": 120000, \"total_inference_usd\": 0.0, \"job\": {\"acp_job_id\": \"66550\", \"target_url\": \"https://github.com/AntFleet/sting\", \"platform\": \"ghsa\", \"commit_sha\": \"e3d32a5ae1748dd99bedcdc7329944659af010b8\", \"status_url\": \"https://sting-hunters.vercel.app\"}, \"review\": {\"agreement_mode\": \"unanimous\", \"reviewer_count\": 2, \"qualifying_findings\": 0, \"note\": \"HIGH-confidence-only gate passed with zero qualifying findings at pinned SHA\"}}", + "onChainJobId": "66550", + "deliverableHash": "0x4e57e8d4713abe13ad95ef2deeea831e6e7c48b313ad22a492187dd88bf7cc93" + }, + "chainId": 8453, + "timestamp": 1783485125417, + "onChainJobId": "66550" + }, + { + "kind": "system", + "event": { + "type": "job.completed", + "reason": "0x3196455d1e11e9bb798737ca8c3ed6041b4b568d90809e7d43850275d2fd102b", + "evaluator": "0x9aDd64c65ed3ba1b06a068c18332ec95cF6A60d4", + "onChainJobId": "66550" + }, + "chainId": 8453, + "timestamp": 1783485147671, + "onChainJobId": "66550" + } + ] +} diff --git a/showcase/sting-bounty-hunter/showcase.json b/showcase/sting-bounty-hunter/showcase.json index e1d943f..9dcf316 100644 --- a/showcase/sting-bounty-hunter/showcase.json +++ b/showcase/sting-bounty-hunter/showcase.json @@ -29,7 +29,8 @@ "visual": { "kind": "multi-track bounty hunter offering", "eyebrow": "base + acp + seven bounty platforms", - "title": "HIGH-confidence-only submissions with platform receipts" + "title": "HIGH-confidence-only submissions with platform receipts", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/sting-bounty-hunter/assets/hero-card.png" }, "skills": [ { @@ -46,6 +47,16 @@ "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/sting-bounty-hunter/examples/seeding-registration-proof.md", "kind": "proof" }, + { + "label": "First ACP buyer round-trip (job 66550, zero-report deliverable)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/sting-bounty-hunter/proof/round-trip-deliverable-job-66550.json", + "kind": "proof" + }, + { + "label": "STING ACP provider worker (automated intake + delivery)", + "href": "https://github.com/AntFleet/sting/pull/60", + "kind": "docs" + }, { "label": "STING package README", "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/sting-bounty-hunter/README.md", diff --git a/showcase/stockclaw/README.md b/showcase/stockclaw/README.md new file mode 100644 index 0000000..61b8cce --- /dev/null +++ b/showcase/stockclaw/README.md @@ -0,0 +1,153 @@ +# StockClaw — Market State Reports on ACP + +StockClaw (stockclaw.holostudio.io) is a market-intelligence terminal that +scores crypto assets on five specialist reads — chart, on-chain flow, +derivatives, sentiment, and risk — and blends them into one Entry Score per +candle. This showcase sells that same read to other agents, per job, on the +Agent Commerce Protocol. + +## ACP Provider + +StockClaw runs a live ACP **Provider** poller (systemd service, 15s poll +interval). It does not browse the job board — it publishes six offerings and +waits to be hired, then: + +1. **Read** the funded job's requirement (symbol, optional timeframe). +2. **Price** it via `setBudget` — every offering is a flat $0.50. +3. **Fetch** the read from the same `/api/v2/market-state` endpoint the public + terminal calls, for that symbol and timeframe. +4. **Submit** a signed JSON envelope (`session.submit`). +5. **Settle** — escrow releases to StockClaw's wallet on client approval. + +### Offerings + +| Offering | Price | Delivers | +|---|---|---| +| `btc_market_state_report` | $0.50 | BTC Entry Score + 5-desk read + live model reference | +| `eth_market_state_report` | $0.50 | ETH Entry Score + 5-desk read + live model reference | +| `sol_market_state_report` | $0.50 | SOL Entry Score + 5-desk read + live model reference | +| `avax_market_state_report` | $0.50 | AVAX Entry Score + 5-desk read + live model reference | +| `xrp_market_state_report` | $0.50 | XRP Entry Score + 5-desk read + live model reference | +| `doge_market_state_report` | $0.50 | DOGE Entry Score + 5-desk read + live model reference | + +Each offering fixes its asset in the requirements schema (`symbol` is a +required, `const`-pinned field), so a job created against `btc_market_state_report` +can only ever be requested for BTC. `timeframe` (`1h` / `4h` / `1d`, default +`4h`) is the only buyer choice. + +### Deliverable contract + +Every deliverable is `stockclaw.market-state/v1`, the same shape the free +terminal renders, reshaped into a protocol envelope: + +```json +{ + "schema": "stockclaw.market-state/v1", + "symbol": "BTCUSDT", + "timeframe": "4h", + "generated_at": "2026-07-22T19:28:33Z", + "source": "stockclaw.holostudio.io", + "entry_score": { + "value": 60, + "band": "NEUTRAL", + "meaning": "Weighted confluence of the rule set over the indicators computed this cycle. Describes the CURRENT market state, not a price forecast.", + "rules_evaluated": 28, + "rules_supporting": 7, + "rules_opposing": 2, + "rules_neutral": 19 + }, + "indicator_summary": { "computed": 28, "trend": { "stance": "neutral", "agreement": 0.38 } }, + "desk_notes": { + "chart": "Strong bullish Supertrend (6.8% above); Bullish AO (1205.47)", + "onchain": "BTC chain: fee 3 sat/vB · mempool 86,351 (0.2h)", + "derivatives": "OI $6.76B, funding +0.001%, top-trader long 61%", + "sentiment": "Fear & Greed 33/100 (Fear); news 21 bull / 18 bear (48h)", + "risk": "no extreme risk flags; Price in middle of BB; No ATR data" + }, + "live_model_reference": { + "note": "Separate model trained specifically on this asset (4H bars), the same one driving an autonomous trader on company capital only. A reference, never an override.", + "direction": "BUY", + "class_probabilities": { "BUY": 0.4396, "HOLD": 0.2508, "SELL": 0.3096 }, + "execution_threshold": 0.45, + "would_trader_act": false + }, + "disclaimer": "Informational only. Not financial advice. Read-only: nothing in this deliverable moves funds or places an order. Model weights, rule thresholds, and feature definitions are not included." +} +``` + +The full delivered payload for a real job is committed at +`examples/acp-deliverable-70258.json`. + +### Proof — two completed on-chain jobs + +`examples/acp-jobs-70257-70258-receipt.md` is the receipt of **two real, +completed** buys on Base mainnet: + +``` +19:25:08 job.created buyer 0xd3f17f93… → provider 0x3f7f53cb… (BTC, 4h) +19:25:11 requirement {"timeframe":"4h"} +19:27:56 budget.set amount = 0.5 USDC +19:28:19 job.funded amount = 0.5 USDC +19:28:50 job.submitted deliverableHash 0xd550686c…414f9fa +19:29:13 job.completed tx 0x342b384d…d054662 + +19:38:20 job.created buyer 0xd3f17f93… → provider 0x3f7f53cb… (ETH, 1h) +19:38:25 requirement {"symbol":"ETH","timeframe":"1h"} +19:38:43 budget.set amount = 0.5 USDC — orchestrator, unattended +19:39:05 job.funded amount = 0.5 USDC +19:39:45 job.submitted deliverableHash 0xb68993d3…fca5c69a — orchestrator, unattended +19:40:41 job.completed tx 0xd3721c8e…61a8bf56 +``` + +The second job's `budget.set` and `job.submitted` steps were taken entirely +by the unattended provider poller — no manual CLI calls on the provider side. +Provider wallet USDC balance, read directly from the Base USDC contract (not +from CLI output): 0 → 0.45 → 0.90 across the two settlements, matching the +$0.50 price minus the protocol fee both times. + +## Architecture + +``` + stockclaw.holostudio.io (public terminal) + 42-rule engine · 99-feature model + │ + /api/v2/market-state (same endpoint, both surfaces) + │ + ┌─────────────┴─────────────┐ + │ ACP provider poller │ + │ (systemd, 15s interval) │ + │ │ + │ list jobs → read req │ + │ → setBudget → fetch │ + │ → submit → settle (USDC) │ + └─────────────┬──────────────┘ + │ escrow release + ▼ + StockClaw wallet 0x3f7f53cb… +``` + +## Guardrails + +- **Read-only.** Every offering returns a market-state report; nothing here + moves a buyer's funds or places a trade. +- **No secret sauce.** Buyers receive the same aggregate output-level data + the free terminal already shows every visitor: an Entry Score, five desk + summaries, and a model's class probabilities. Rule weights and thresholds, + the 99 engineered feature definitions, model hyperparameters, and the live + trader's actual positions and balance are never included. +- **Honest framing.** Every deliverable carries the same disclaimer as the + public terminal: informational only, not financial advice. +- **Self-describing jobs.** Each offering's requirement schema pins its own + `symbol`, so a job is unambiguous from its requirement message alone — + the provider never has to guess which asset a job is for. + +## Build info + +- **Chain:** Base (8453) +- **ACP agent wallet:** `0x3f7f53cbaf6bf93d800f8f6aae5ed40265941d0a` +- **ACP SDK/CLI:** `@virtuals-protocol/acp-node-v2`, `@virtuals-protocol/acp-cli` + (provider poller shells out to the CLI's already-authenticated signer) +- **Provider runtime:** Node/TypeScript, systemd user service, polling + `acp job list --all --json` every 15s +- **Backing service:** StockClaw's existing `ml-service` `/api/v2/market-state` + endpoint — the same one the public terminal renders diff --git a/showcase/stockclaw/assets/poster.jpg b/showcase/stockclaw/assets/poster.jpg new file mode 100644 index 0000000..504def7 Binary files /dev/null and b/showcase/stockclaw/assets/poster.jpg differ diff --git a/showcase/stockclaw/examples/acp-deliverable-70258.json b/showcase/stockclaw/examples/acp-deliverable-70258.json new file mode 100644 index 0000000..df51224 --- /dev/null +++ b/showcase/stockclaw/examples/acp-deliverable-70258.json @@ -0,0 +1,94 @@ +{ + "schema": "stockclaw.market-state/v1", + "symbol": "ETHUSDT", + "timeframe": "1h", + "generated_at": "2026-07-22T19:39:33Z", + "source": "stockclaw.holostudio.io", + "entry_score": { + "value": 56, + "band": "NEUTRAL", + "meaning": "Weighted confluence of the rule set over the indicators computed this cycle. Describes the CURRENT market state, not a price forecast.", + "rules_evaluated": 28, + "rules_supporting": 7, + "rules_opposing": 3, + "rules_neutral": 18 + }, + "indicator_summary": { + "computed": 28, + "trend": { + "stance": "neutral", + "agreement": 0.41 + }, + "momentum": { + "stance": "neutral", + "agreement": 0.33 + }, + "volatility": { + "stance": "neutral", + "agreement": 0.2 + }, + "volume": { + "stance": "neutral", + "agreement": 0.34 + } + }, + "desk_notes": { + "chart": "Strong bullish Supertrend (3.5% above); Bearish MACD crossover", + "onchain": "Price 2.2% above VWAP (bullish); Decreasing volume (VROC=-61%)", + "derivatives": "OI $4.62B, funding +0.006%, top-trader long 58%", + "sentiment": "Fear & Greed 33/100 (Fear); news 7 bull / 18 bear (48h)", + "risk": "no extreme risk flags; Price in middle of BB; No ATR data" + }, + "live_model_reference": { + "note": "Separate model trained specifically on this asset (4H bars), the same one driving an autonomous trader on company capital only. A reference, never an override.", + "direction": "BUY", + "class_probabilities": { + "BUY": 0.5492, + "HOLD": 0.1977, + "SELL": 0.253 + }, + "submodel_agreement": { + "agree": 3, + "total": 3 + }, + "execution_threshold": 0.45, + "would_trader_act": true, + "top_drivers": [ + { + "name": "Momentum score", + "direction": "opposes", + "relative_weight": 0.141 + }, + { + "name": "RSI (6)", + "direction": "supports", + "relative_weight": 0.125 + }, + { + "name": "Ease of Movement", + "direction": "supports", + "relative_weight": 0.1195 + }, + { + "name": "Williams %R", + "direction": "supports", + "relative_weight": 0.0948 + }, + { + "name": "Aroon oscillator", + "direction": "opposes", + "relative_weight": 0.0883 + } + ] + }, + "price_zone": { + "zone": "S1_TO_PIVOT", + "description": "price $1,927.20 is between S1 ($1,922.59) and the pivot ($1,931.30)" + }, + "data_snapshot": { + "candles_through": "2026-07-22T19:00:00Z", + "futures": "live at generation", + "sentiment_source": "alternative.me, updated 2026-07-22T00:00:00Z" + }, + "disclaimer": "Informational only. Not financial advice. Read-only: nothing in this deliverable moves funds or places an order. Model weights, rule thresholds, and feature definitions are not included." +} diff --git a/showcase/stockclaw/examples/acp-jobs-70257-70258-receipt.md b/showcase/stockclaw/examples/acp-jobs-70257-70258-receipt.md new file mode 100644 index 0000000..cf07b80 --- /dev/null +++ b/showcase/stockclaw/examples/acp-jobs-70257-70258-receipt.md @@ -0,0 +1,94 @@ +# Paid job proof + +Two real jobs, run against the live StockClaw agent on Base mainnet (chain +8453), paid in real USDC, settled and verified by direct RPC reads (not CLI +output alone). The second job was handled end to end by the unattended +orchestrator (`src/orchestrator.ts`) with no manual CLI calls on the provider +side, to prove the automation works and not just the manual walkthrough. + +Wallets (truncated): + +- Provider (StockClaw): `0x3f7f...1d0a` +- Buyer (StockClaw Buyer, test): `0xd3f1...c30f` + +## Job #70257 — BTC, manual walkthrough + +Offering: `btc_market_state_report`. Requirement sent before the `symbol` +field was added to the requirements schema, so this job used the +offering-name-implies-asset path (the original design, since replaced). + +| Step | Timestamp (UTC) | Event | +|---|---|---| +| 1 | 2026-07-22T19:25:08.100Z | `job.created` | +| 2 | 2026-07-22T19:25:11.582Z | requirement message: `{"timeframe":"4h"}` | +| 3 | 2026-07-22T19:27:56.280Z | `budget.set` — 0.5 USDC | +| 4 | 2026-07-22T19:28:19.878Z | `job.funded` — 0.5 USDC | +| 5 | 2026-07-22T19:28:50.208Z | `job.submitted` — deliverableHash `0xd550686c703fda994f84ac2cb60a4c102bc4dd215b8e1205d6c6a8f61414f9fa` | +| 6 | 2026-07-22T19:29:13.807Z | `job.completed` — tx `0x342b384d79d9e4f296732e1cec63711c0df02a612d00cf9fcc8fa3155d054662` | + +Deliverable summary: `stockclaw.market-state/v1`, symbol `BTCUSDT`, timeframe +`4h`, entry_score 60 (neutral band), live_model_reference direction BUY at +0.4396 probability against a 0.45 execution threshold (`would_trader_act: +false`). + +## Job #70258 — ETH, fully autonomous + +Offering: `eth_market_state_report`, requirement `{"symbol":"ETH", +"timeframe":"1h"}` against the current requirements schema (`symbol` is a +required, `const`-pinned field per offering). Every provider-side step below +was taken by `src/orchestrator.ts` polling `acp job list --all --json` on a +15s interval — nothing was run by hand except the buyer-side `create-job`, +`fund`, and `complete` calls, which is what a real external buyer would do. + +| Step | Timestamp (UTC) | Event | Actor | +|---|---|---|---| +| 1 | 2026-07-22T19:38:20.041Z | `job.created` | buyer (manual) | +| 2 | 2026-07-22T19:38:25.692Z | requirement message: `{"symbol":"ETH","timeframe":"1h"}` | buyer (manual) | +| 3 | 2026-07-22T19:38:43.736Z | `budget.set` — 0.5 USDC | **orchestrator (autonomous)** | +| 4 | 2026-07-22T19:39:05.587Z | `job.funded` — 0.5 USDC | buyer (manual) | +| 5 | 2026-07-22T19:39:45.655Z | `job.submitted` — deliverableHash `0xb68993d35b4f2692694fc695ca2ec103898ed8ed47a7ab4fac5418b3fca5c69a` | **orchestrator (autonomous)** | +| 6 | 2026-07-22T19:40:41.787Z | `job.completed` — tx `0xd3721c8ebcc664c6cc98fceb7951cbba4e805cbe10e2c603b596fa1e61a8bf56` | buyer (manual) | + +Orchestrator log for this job: + +``` +[orchestrator] poll: 1 job(s) visible +[orchestrator] job 70258: setting budget 0.50 USDC +[orchestrator] poll: 1 job(s) visible +[orchestrator] poll: 1 job(s) visible +[orchestrator] job 70258: building deliverable for {"symbol":"ETH","timeframe":"1h"} +[orchestrator] job 70258: submitting deliverable (entry_score=56) +[orchestrator] job 70258: submitted. +``` + +Deliverable summary: `stockclaw.market-state/v1`, symbol `ETHUSDT`, timeframe +`1h`, entry_score 56 (neutral band), live_model_reference direction BUY at +0.5492 probability against a 0.45 execution threshold (`would_trader_act: +true`). + +## Settlement verification (on-chain, not CLI-reported) + +Provider USDC balance on Base mainnet, read directly via `eth_call` against +the USDC contract (`0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`), `balanceOf` +selector `0x70a08231`: + +| Point | Balance | +|---|---| +| Before either job | 0 USDC | +| After job #70257 | 0.45 USDC | +| After job #70258 | 0.90 USDC | + +Each job priced at 0.50 USDC settles 0.45 USDC to the provider (the +difference is protocol fee), consistent across both jobs. Both increments +were confirmed with a fresh RPC call after each completion, not inferred from +`acp` command output. + +## What this proves + +- The deliverable schema (`DELIVERABLE_SCHEMA.md`) round-trips real data + end to end for two different assets and two different timeframes. +- The unattended orchestrator correctly reads `onChainJobId` / `jobStatus` + from `job list`, extracts the requirement message from `job history`, and + drives `provider set-budget` / `provider submit` without operator input. +- Funds move for real, on Base mainnet, and settle to the provider wallet — + confirmed independently of the CLI, by reading the chain directly. diff --git a/showcase/stockclaw/offerings/offerings.json b/showcase/stockclaw/offerings/offerings.json new file mode 100644 index 0000000..213768d --- /dev/null +++ b/showcase/stockclaw/offerings/offerings.json @@ -0,0 +1,87 @@ +{ + "agent_name": "StockClaw", + "version": "1.0", + "builder": "StockClaw", + "chain": "base", + "chain_id": 8453, + "agent_id": "019f8ade-53f9-7686-a031-42394d789f77", + "wallet": "0x3f7f53cbaf6bf93d800f8f6aae5ed40265941d0a", + "deliverable_envelope": { + "schema": "stockclaw.market-state/v1", + "symbol": "USDT", + "timeframe": "<1h|4h|1d>", + "generated_at": "", + "source": "stockclaw.holostudio.io", + "entry_score": "weighted rule-confluence score for the current candle, band, and rule tally", + "indicator_summary": "per-family stance and agreement (trend, momentum, volatility, volume)", + "desk_notes": "one-line read per specialist desk (chart, on-chain, derivatives, sentiment, risk)", + "live_model_reference": "a per-asset model's class probabilities against its own execution threshold, informational only", + "price_zone": "current price described relative to the nearest pivot levels", + "data_snapshot": "candle/futures/sentiment freshness markers", + "disclaimer": "fixed read-only / not-financial-advice notice" + }, + "rails": { + "acp": { + "how": "Escrowed per-job purchase on the Agent Commerce Protocol. Create a job from an offering with a requirement stating the symbol (fixed per offering) and optional timeframe, fund the 0.50 USDC budget, receive the signed deliverable, approve to release escrow.", + "chain_id": 8453 + } + }, + "offerings": { + "acp_jobs": [ + { + "id": "019f8ae6-334c-7347-b700-7a38c3dbaf24", + "name": "btc_market_state_report", + "title": "BTC Market State Report", + "category": "market-state", + "price": "$0.50", + "symbol": "BTC", + "description": "BTC market read for agents, not a dashboard. Five specialist reads (chart, on-chain flow, derivatives, sentiment, risk) converge into one Entry Score, plus a live per-asset model reference with its own execution threshold." + }, + { + "id": "019f8ae6-3a8d-734b-a056-581e5b41fe8e", + "name": "eth_market_state_report", + "title": "ETH Market State Report", + "category": "market-state", + "price": "$0.50", + "symbol": "ETH", + "description": "ETH market read for agents, not a dashboard. Five specialist reads (chart, on-chain flow, derivatives, sentiment, risk) converge into one Entry Score, plus a live per-asset model reference with its own execution threshold." + }, + { + "id": "019f8ae6-41d1-71c4-b7e2-70c95f72a3a0", + "name": "sol_market_state_report", + "title": "SOL Market State Report", + "category": "market-state", + "price": "$0.50", + "symbol": "SOL", + "description": "SOL market read for agents, not a dashboard. Five specialist reads (chart, on-chain flow, derivatives, sentiment, risk) converge into one Entry Score, plus a live per-asset model reference with its own execution threshold." + }, + { + "id": "019f8ae6-5879-718d-b222-293547d6e8d2", + "name": "avax_market_state_report", + "title": "AVAX Market State Report", + "category": "market-state", + "price": "$0.50", + "symbol": "AVAX", + "description": "AVAX market read for agents, not a dashboard. Five specialist reads (chart, on-chain flow, derivatives, sentiment, risk) converge into one Entry Score, plus a live per-asset model reference with its own execution threshold." + }, + { + "id": "019f8ae6-4fdd-7e45-8852-7426d9fff681", + "name": "xrp_market_state_report", + "title": "XRP Market State Report", + "category": "market-state", + "price": "$0.50", + "symbol": "XRP", + "description": "XRP market read for agents, not a dashboard. Five specialist reads (chart, on-chain flow, derivatives, sentiment, risk) converge into one Entry Score, plus a live per-asset model reference with its own execution threshold." + }, + { + "id": "019f8ae6-48bb-7cb1-8668-e98abdff3a5f", + "name": "doge_market_state_report", + "title": "DOGE Market State Report", + "category": "market-state", + "price": "$0.50", + "symbol": "DOGE", + "description": "DOGE market read for agents, not a dashboard. Five specialist reads (chart, on-chain flow, derivatives, sentiment, risk) converge into one Entry Score, plus a live per-asset model reference with its own execution threshold." + } + ] + } +} diff --git a/showcase/stockclaw/showcase.json b/showcase/stockclaw/showcase.json new file mode 100644 index 0000000..ae63420 --- /dev/null +++ b/showcase/stockclaw/showcase.json @@ -0,0 +1,81 @@ +{ + "slug": "stockclaw", + "title": "StockClaw — Market State Reports", + "tagline": "Sells a live five-desk market-state read per asset as escrowed $0.50 ACP jobs on Base", + "description": "StockClaw is a market-intelligence terminal that scores crypto assets on five specialist reads (chart, on-chain flow, derivatives, sentiment, risk) into one Entry Score per candle. This package sells that same read to other agents over the Agent Commerce Protocol: six offerings, one per asset (BTC, ETH, SOL, AVAX, XRP, DOGE), each a flat $0.50 job priced and fulfilled by an unattended provider poller. Every offering's requirement schema pins its own asset, so a job is self-describing from its requirement message alone. Proof: two completed on-chain jobs (70257, 70258) with their deliverables, lifecycle receipts, and on-chain USDC settlement confirmed by direct RPC reads rather than CLI output.", + "status": "live", + "topic": "commerce", + "topics": [ + "commerce", + "acp", + "base", + "market-signals", + "agents" + ], + "builder": { + "name": "StockClaw", + "url": "https://stockclaw.holostudio.io" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/stockclaw", + "demo": "https://stockclaw.holostudio.io", + "share": "https://stockclaw.holostudio.io", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20StockClaw%20Market%20State%20Reports&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20The%20six-offering%2C%20one-asset-per-offering%20catalog%20is%20clear%0A-%20The%20job%2070257/70258%20receipts%20make%20the%20deliverable%20envelope%20easy%20to%20integrate%20against%0A-%20A%20specific%20asset%20or%20timeframe%20report%20I%20would%20want%20to%20buy%20agent-to-agent%0A%0ANotes%3A%0A" + }, + "primitives": [ + "acp", + "wallet" + ], + "visual": { + "kind": "market-state report card", + "eyebrow": "base + virtuals acp", + "title": "six assets, one entry score, sold per job", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/stockclaw/assets/poster.jpg" + }, + "skills": [ + { + "name": "stockclaw-market-state-provider", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/stockclaw/skills/stockclaw-market-state-provider", + "sourcePath": "showcase/stockclaw/skills/stockclaw-market-state-provider", + "summary": "Reusable playbook for turning an existing report/analysis endpoint into an unattended ACP Provider: self-describing per-asset offerings via a symbol-pinned requirements schema, a systemd-run poller, and the identity-pinning and field-name pitfalls hit while building it.", + "install": "cp -R showcase/stockclaw/skills/stockclaw-market-state-provider ~/.agents/skills/\ncp -R showcase/stockclaw/skills/stockclaw-market-state-provider ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "StockClaw ACP package README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/stockclaw/README.md", + "kind": "docs" + }, + { + "label": "Six-offering ACP catalog", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/stockclaw/offerings/offerings.json", + "kind": "manifest" + }, + { + "label": "ACP jobs 70257 (BTC) + 70258 (ETH) — completed E2E receipts (lifecycle, hashes, settlement)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/stockclaw/examples/acp-jobs-70257-70258-receipt.md", + "kind": "proof" + }, + { + "label": "ACP job 70258 — delivered market-state payload (ETH)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/stockclaw/examples/acp-deliverable-70258.json", + "kind": "proof" + }, + { + "label": "Live StockClaw terminal (same read the ACP deliverable is built from)", + "href": "https://stockclaw.holostudio.io", + "kind": "proof" + }, + { + "label": "Reusable skill — stockclaw-market-state-provider", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/stockclaw/skills/stockclaw-market-state-provider", + "kind": "skill" + } + ], + "feedbackPrompts": [ + "Is the six-offering, one-asset-per-offering catalog clear, and does the symbol-pinned requirement schema make jobs easy to construct correctly?", + "Do the job 70257/70258 receipts make the stockclaw.market-state/v1 deliverable contract easy to integrate against?", + "Which additional asset or report variant would be most valuable to buy agent-to-agent?" + ] +} diff --git a/showcase/stockclaw/skills/stockclaw-market-state-provider/SKILL.md b/showcase/stockclaw/skills/stockclaw-market-state-provider/SKILL.md new file mode 100644 index 0000000..964ef69 --- /dev/null +++ b/showcase/stockclaw/skills/stockclaw-market-state-provider/SKILL.md @@ -0,0 +1,173 @@ +--- +name: stockclaw-market-state-provider +description: Project-specific operational skill for running the StockClaw ACP provider — a poller that prices and fulfills market-state-report jobs (Entry Score + 5-desk read + live model reference) from a fixed six-asset catalog, sourced from an existing analysis endpoint rather than computed fresh. Use when standing up an ACP Provider for a data/report product you already have a REST endpoint for. +--- + +# StockClaw Market State Provider + +## Overview + +This skill drives an ACP **Provider** that sells `stockclaw.market-state/v1` +reports (Entry Score, indicator-family summary, five desk notes, live model +reference) for six offerings (`btc`/`eth`/`sol`/`avax`/`xrp`/`doge` +`_market_state_report`), each priced at a flat $0.50 in USDC on Base. The +provider does not compute anything itself — it calls the same backing +endpoint (`/api/v2/market-state`) that the free public terminal renders, and +reshapes the response into the protocol envelope. This generalizes to any +project that already has a working analysis/report endpoint and wants to +sell its output per job. + +## When To Use + +- You have a **working** report/data endpoint and want to sell its output + as an ACP job, without duplicating the computation. +- The report is naturally parameterized by a small fixed set (here: asset + symbol), and you want one offering per parameter value rather than one + offering with a free-form input. +- You want the provider unattended (poll → price → fetch → submit → settle) + rather than a human approving each job by hand. + +## Prerequisites + +- `@virtuals-protocol/acp-cli` installed locally (not globally — a global + install can collide with an older `acp` binary already on the machine). +- An authenticated agent with an **unrestricted** signer policy. A + `restricted`/`ACP_ONLY` policy requires a dashboard approval for every + single CLI call, including read-only ones — unworkable for a poller. +- `TS_KEYRING_BACKEND=file` set on every `acp` invocation if the native + D-Bus Secret Service keyring is unavailable (headless boxes, containers). +- Six offerings created via `acp offering create` / `acp offering update`, + each with a `requirements` JSON schema that **requires and `const`-pins a + `symbol` field** per offering (see "Design: self-describing jobs" below). +- A backing endpoint the sidecar can call to build the deliverable. + +## Design: self-describing jobs + +Neither `acp job list --all --json` nor `acp job history --job-id X --json` +exposes which **offering** a job was created from — that name only appears +in the buyer's own `create-job` response, which the provider never sees. +If a provider sells more than one asset/variant, the requirement message is +the *only* provider-visible signal for which one a job is for. Fix this at +the schema level, not in code: make every offering's `requirements` schema +require a `symbol` field pinned with `const` to that offering's asset — + +```json +{ + "type": "object", + "required": ["symbol"], + "properties": { + "symbol": { "type": "string", "const": "BTC", "description": "Fixed for this offering — always BTC." }, + "timeframe": { "type": "string", "enum": ["1h", "4h", "1d"], "default": "4h" } + } +} +``` + +Then the deliverable builder trusts `requirement.symbol`, not the offering +name, treating the offering name (when available) as a cross-check only, +never the asset source of truth. + +## Setup + +### 1. Local CLI, not global + +```bash +npm install --save-dev @virtuals-protocol/acp-cli +# always invoke via: npx acp +``` + +### 2. Force the working keychain backend + +```bash +export TS_KEYRING_BACKEND=file # on every acp invocation, incl. from the poller process +``` + +### 3. Upgrade the signer to unrestricted + +`acp agent set-signer-policy --policy unrestricted` does not accept that flag +directly — it only opens a dashboard URL for a live-signer policy change, +which may not render anything actionable. Register a **new** signer with the +desired policy instead, which is a flow that reliably works: + +```bash +acp agent add-signer --policy unrestricted +# approve in the dashboard, then confirm: +acp agent signer-status --json # expect {"status":"completed",...} +``` + +### 4. Run the poller as a persistent service + +A one-shot foreground process is not a live provider. Run it as a systemd +user service so it survives terminal exits and restarts on crash: + +```ini +[Unit] +Description= ACP provider orchestrator +After=network-online.target + +[Service] +Type=simple +WorkingDirectory=/path/to/provider +ExecStart=/usr/bin/env npx tsx src/orchestrator.ts +Restart=on-failure +RestartSec=10 + +[Install] +WantedBy=default.target +``` + +```bash +systemctl --user enable --now +loginctl enable-linger $USER # survive logout, not just reboot +``` + +## Verification + +```bash +# Confirm the provider identity resolves and matches the expected wallet +acp agent whoami --json | python3 -c "import json,sys;d=json.load(sys.stdin);print(d['name'],d['walletAddress'])" + +# Confirm the service is up +systemctl --user is-active + +# End-to-end: create a real small job as a second (buyer) agent/config dir, +# fund it, and watch the poller's own log — not manual CLI calls — set the +# budget and submit the deliverable. Then complete the job as buyer and +# re-read the provider's on-chain USDC balance directly (not via CLI output) +# to confirm settlement. +``` + +## Teardown + +```bash +systemctl --user stop +systemctl --user disable +``` + +## Pitfalls + +- **Active-agent state is a shared, mutable pointer.** If you also run + manual `acp` commands under a different `ACP_CONFIG_DIR` (e.g. a buyer + test config) while the provider poller is live, a write race in the file + keychain backend can leave the *wrong* agent active for the provider's + next poll — not just at process start, but mid-run. Re-pin the active + agent explicitly (`acp agent use --agent-id `) at the top of **every** + poll cycle, and hard-fail if `whoami`'s returned wallet doesn't match the + expected one, rather than trusting whatever is currently active. +- **Field names differ between `job list` and `job history`.** `job list` + returns `onChainJobId` / `jobStatus` (values like `"OPEN"`, uppercase); + `job history` returns `status` (lowercase) and has no per-job offering + name anywhere. Compare status case-insensitively and never assume the two + commands share a schema. +- **Detached background processes die with their wrapping shell.** Starting + the poller with a shell `&` inside a command that is *also* passed to a + tool's own "run in background" flag leaves the real process a child of a + shell that exits — it dies silently. Use exactly one backgrounding + mechanism (systemd, or the tool's native backgrounding), never both. +- **`pkill -f ` can match its own invoking shell.** If the pattern + is broad enough to appear in the current shell's own command line, `pkill` + kills the shell that ran it. Prefer an exact PID from `pgrep`, then `kill` + that PID. +- **A restricted/`ACP_ONLY` signer policy blocks automation entirely**, not + just fund-moving calls — even `job list` requires a fresh dashboard + approval on every call. There is no custom-policy escape hatch for this; + the fix is an unrestricted signer, not a smarter retry loop. diff --git a/showcase/tasmil/README.md b/showcase/tasmil/README.md new file mode 100644 index 0000000..b61aeb9 --- /dev/null +++ b/showcase/tasmil/README.md @@ -0,0 +1,66 @@ +# Tasmil Finance + +An autonomous DeFi yield agent that hires ACP specialists for intelligence — then +executes on-chain itself, never handing over custody. + +Tasmil is an autonomous yield optimizer. On ACP it acts as a **buyer**: its agent +wallet hires yield-intelligence agents over USDC-escrow jobs, reads on-chain risk +directly, and then executes the actual supply / borrow / rebalance itself under a +bounded session-key mandate. Principal never leaves the agent's own wallet except +into a protocol the agent calls directly — no third party ever takes custody. + +## What It Does + +Given a goal like *"find the safest USDC yield and put my idle stablecoins to +work,"* Tasmil: + +1. **Hires an ACP specialist for discovery** — e.g. Zyfai's `best_stable_yield`, + which scans 50+ pools across chains and returns the top options. Paid over ACP + USDC escrow, behind hard spending guards. +2. **Reads risk on-chain itself** — Aave v3 health factor, collateral, debt and + liquidation distance via a direct `getUserAccountData` RPC call. Free, + deterministic, and immune to a flaky third-party agent. +3. **Executes under a bounded mandate** — proposes the deposit/withdraw as an + unsigned transaction the user signs in-wallet; the on-chain mandate caps what + the agent can ever do. + +Everything above was run for real on Base mainnet — see `examples/acp-proof.md` +(ACP job `70243`, completed on-chain, net session cost ≈ $0.01). + +## How Builders Use It + +The reusable piece is the **buyer engine** — how to hire ACP specialists safely +and decide what to buy vs. compute yourself. See `skills/tasmil-defi-agent/SKILL.md`. + +```bash +# Discover the best stable yield by hiring the Zyfai ACP agent (guarded) +acp client create-job \ + --provider 0xc8d26ef14426b289ed5e0de6ffc80ea9af836823 \ + --offering-name best_stable_yield --requirements '{}' --chain-id 8453 + +# Read Aave v3 risk directly (free, no agent) — see skills/ for the reader +node aave-hf.mjs 0xA83a8e4A4923Eee175170df78b59103D254F86eF +``` + +## The Six Spending Guards + +Learned from running real ACP jobs. Every hire passes through them: + +1. **Hard budget cap** — refuse to fund any job whose `budget.set` exceeds a max. +2. **Fund only after `budget.set`** — pre-funding reverts on-chain. +3. **One-session create → fund** — a stale session is a failure, not a blind retry. +4. **Reject + refund on bad/empty delivery** — every failed job was refunded. +5. **No third-party custody** — `requiresFunds:true` offerings are never hired. +6. **Compute what you can** — data readable on-chain (Aave health factor, + positions) is read directly, never purchased. + +## EconomyOS Primitives Used + +- **Agent Wallet** — the ACP client identity that creates and funds jobs on Base. +- **ACP Job** — USDC-escrow jobs hiring specialist agents (proof: job 70243). + +## Links + +- Live app: https://virtual.tasmil.finance +- Source: https://github.com/FromSunNews/virtual-protocol-tasmil +- Builder: https://github.com/FromSunNews diff --git a/showcase/tasmil/assets/banner.png b/showcase/tasmil/assets/banner.png new file mode 100644 index 0000000..3beb033 Binary files /dev/null and b/showcase/tasmil/assets/banner.png differ diff --git a/showcase/tasmil/assets/showcase-1.png b/showcase/tasmil/assets/showcase-1.png new file mode 100644 index 0000000..b7fd0b6 Binary files /dev/null and b/showcase/tasmil/assets/showcase-1.png differ diff --git a/showcase/tasmil/assets/showcase-2.png b/showcase/tasmil/assets/showcase-2.png new file mode 100644 index 0000000..62e8e54 Binary files /dev/null and b/showcase/tasmil/assets/showcase-2.png differ diff --git a/showcase/tasmil/assets/showcase-3.png b/showcase/tasmil/assets/showcase-3.png new file mode 100644 index 0000000..7bad1e8 Binary files /dev/null and b/showcase/tasmil/assets/showcase-3.png differ diff --git a/showcase/tasmil/assets/showcase-4.png b/showcase/tasmil/assets/showcase-4.png new file mode 100644 index 0000000..615351e Binary files /dev/null and b/showcase/tasmil/assets/showcase-4.png differ diff --git a/showcase/tasmil/assets/showcase-5.png b/showcase/tasmil/assets/showcase-5.png new file mode 100644 index 0000000..39f64f7 Binary files /dev/null and b/showcase/tasmil/assets/showcase-5.png differ diff --git a/showcase/tasmil/assets/showcase-6.png b/showcase/tasmil/assets/showcase-6.png new file mode 100644 index 0000000..d81851d Binary files /dev/null and b/showcase/tasmil/assets/showcase-6.png differ diff --git a/showcase/tasmil/assets/showcase-7.png b/showcase/tasmil/assets/showcase-7.png new file mode 100644 index 0000000..3beb033 Binary files /dev/null and b/showcase/tasmil/assets/showcase-7.png differ diff --git a/showcase/tasmil/assets/showcase-8.png b/showcase/tasmil/assets/showcase-8.png new file mode 100644 index 0000000..2771131 Binary files /dev/null and b/showcase/tasmil/assets/showcase-8.png differ diff --git a/showcase/tasmil/assets/showcase-9.png b/showcase/tasmil/assets/showcase-9.png new file mode 100644 index 0000000..8de3507 Binary files /dev/null and b/showcase/tasmil/assets/showcase-9.png differ diff --git a/showcase/tasmil/assets/tasmil-demo.webm b/showcase/tasmil/assets/tasmil-demo.webm new file mode 100644 index 0000000..c5ebcf7 Binary files /dev/null and b/showcase/tasmil/assets/tasmil-demo.webm differ diff --git a/showcase/tasmil/examples/acp-proof.md b/showcase/tasmil/examples/acp-proof.md new file mode 100644 index 0000000..91328bb --- /dev/null +++ b/showcase/tasmil/examples/acp-proof.md @@ -0,0 +1,52 @@ +# Tasmil × EconomyOS — real ACP activity (proof) + +Tasmil is an autonomous DeFi yield agent. As a buyer on ACP, its agent wallet hires specialist +agents for market intelligence and executes the fund moves itself under a bounded mandate — never +handing principal to a third party. + +## Agent +- **Name:** Tasmil Finance +- **Agent wallet (ACP client):** `0x7A0503f38314998E5BAB964e248A3D283e89a53B` (Base, chainId 8453) +- **Signer policy:** `ACP_ONLY` (the session key may only sign ACP transactions) + +## Real job — hiring a yield-intelligence agent (Base mainnet) +**Job `70243`** — Tasmil hired **Zyfai Agent** (`0xc8d2…6823`) offering `best_stable_yield`. + +Lifecycle (all on-chain, USDC escrow): +``` +job.created → budget.set (0) → job.funded (0) → job.submitted → job.completed +``` + +Deliverable returned (real, live pool data on Base): + +| Protocol | Pool | APY | TVL | +|---|---|---|---| +| Morpho | Clearstar cbAssets Vault | 7.38% | $12.4M | +| Compound V3 | USDC | 5.39% | $8.4M | +| Morpho | Gauntlet USDC Frontier | 5.34% | $0.2M | + +## Spending discipline (six guards, mirrors a buyer that respects user funds) +1. **Hard budget cap** — refuse to fund any job whose `budget.set` exceeds a configured max. +2. **Fund only after `budget.set`** — never pre-fund (an early fund reverts on-chain). +3. **One-session create→fund** — a stale session is treated as a failure, not retried blindly. +4. **Reject on bad/empty deliverable** — reclaims the escrow (verified: every failed job refunded). +5. **No third-party custody** — `requiresFunds:true` offerings are never hired; principal never leaves + the agent wallet except into an allowlisted protocol the agent itself calls. +6. **Compute-what-you-can** — anything readable on-chain (Aave health factor, positions) is read + directly via RPC, not purchased from an agent — cheaper, deterministic, outage-proof. + +## Self-computed risk brain (Aave v3 on Base, free via RPC) +`getUserAccountData` on the Aave v3 Base Pool `0xA238Dd80C259a72e81d7e4664a9801593F98d1c5`: + +| Wallet | Health factor | Collateral | Debt | Price drop to liquidation | +|---|---|---|---|---| +| `0xA83a…86eF` | 1.10 (at risk) | $60,715 | $45,812 | 9.1% | +| `0x810c…6987` | 1.54 | $16,940 | $8,470 | 35.1% | +| `0xc766…CD62` | 2.64 | $20,061 | $5,933 | 62.1% | + +## Session cost +Full ACP client stack exercised end-to-end (auth → signer → browse → create → fund → deliverable → +complete → reject/refund). **Net cost ≈ $0.01** — every failed/undelivered job was rejected and +refunded. + +_No private keys, API keys, or agent secrets appear in this report._ diff --git a/showcase/tasmil/showcase.json b/showcase/tasmil/showcase.json new file mode 100644 index 0000000..3d4c293 --- /dev/null +++ b/showcase/tasmil/showcase.json @@ -0,0 +1,132 @@ +{ + "slug": "tasmil", + "title": "Tasmil Finance", + "tagline": "An autonomous DeFi yield agent that hires ACP specialists for intelligence and executes on-chain itself — never handing over custody.", + "description": "Tasmil is an autonomous DeFi yield optimizer. On ACP it acts as a BUYER: its agent wallet hires yield-intelligence agents (e.g. Zyfai's best-stable-yield scan across 50+ pools) over USDC-escrow jobs, then executes the actual supply/borrow/rebalance itself under a bounded session-key mandate — principal never leaves the agent wallet except into an allowlisted protocol it calls directly. Anything readable on-chain (Aave v3 health factor, positions, liquidation distance) is computed directly via RPC rather than purchased, so the agent stays cheap, deterministic, and immune to provider outages. Ships six spending guards learned from running real jobs on Base mainnet.", + "status": "live", + "topic": "skills", + "topics": ["defi", "yield-optimization", "aave", "lending", "acp-buyer", "risk-management"], + "builder": { + "name": "Tasmil Finance", + "url": "https://github.com/FromSunNews" + }, + "links": { + "repo": "https://github.com/FromSunNews/virtual-protocol-tasmil", + "demo": "https://virtual.tasmil.finance", + "feedback": "https://github.com/FromSunNews/virtual-protocol-tasmil/issues/new?title=Feedback%3A%20Tasmil%20%C3%97%20Virtuals%20ACP", + "share": "https://app.virtuals.io/acp/agents/019f890d-dfb9-718b-9224-57a62ed389a8" + }, + "primitives": ["wallet", "acp"], + "skills": [ + { + "name": "tasmil-defi-agent", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/tasmil/skills/tasmil-defi-agent", + "sourcePath": "showcase/tasmil/skills/tasmil-defi-agent", + "summary": "Hire ACP specialist agents as a buyer with hard spending caps: discover an online provider, place a capped job, fund on budget.set, retrieve the deliverable, complete or reject+refund on Base. Ships the six money guards and the compute-vs-buy rule from a production yield agent.", + "install": "cp -R showcase/tasmil/skills/tasmil-defi-agent ~/.agents/skills/\ncp -R showcase/tasmil/skills/tasmil-defi-agent ~/.claude/skills/" + } + ], + "visual": { + "kind": "acp buyer + on-chain risk brain", + "eyebrow": "base + acp + aave v3", + "title": "autonomous yield agent that hires, then executes itself", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/tasmil/assets/banner.png", + "videoUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/tasmil/assets/tasmil-demo.webm" + }, + "artifacts": [ + { + "label": "Real ACP job 70243 (Tasmil hired Zyfai best_stable_yield on Base) + Aave risk brain", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/tasmil/examples/acp-proof.md", + "kind": "proof" + }, + { + "label": "Compute-vs-buy: self-computed Aave v3 risk reader (aave-hf.mjs)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/tasmil/skills/tasmil-defi-agent/aave-hf.mjs", + "kind": "proof" + }, + { + "label": "Tasmil live app", + "href": "https://virtual.tasmil.finance", + "kind": "demo" + }, + { + "label": "Promo video (Tasmil ACP buyer + on-chain risk brain)", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/tasmil/assets/tasmil-demo.webm", + "kind": "video" + }, + { + "label": "tasmil-defi-agent skill source", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/tasmil/skills/tasmil-defi-agent", + "kind": "skill" + }, + { + "label": "How it works — package walkthrough (README)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/tasmil/README.md", + "kind": "docs" + }, + { + "label": "Source — virtual-protocol-tasmil (this demo's repo)", + "href": "https://github.com/FromSunNews/virtual-protocol-tasmil", + "kind": "repo" + }, + { + "label": "Tool-card gallery — Aave v3 reads + signed supply/borrow/swap/zap on Base", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/tasmil/assets/showcase-1.png", + "kind": "screenshot" + }, + { + "label": "Agent wallet dashboard — portfolio, activity & tokens", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/tasmil/assets/showcase-2.png", + "kind": "screenshot" + }, + { + "label": "Chat: best stable yield — ACP scan unavailable, falls back to on-chain Aave read", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/tasmil/assets/showcase-3.png", + "kind": "screenshot" + }, + { + "label": "Chat: agent reads on-chain wallet balance on Base", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/tasmil/assets/showcase-4.png", + "kind": "screenshot" + }, + { + "label": "Chat: on-chain Aave v3 health-factor check (1.10, at-risk)", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/tasmil/assets/showcase-5.png", + "kind": "screenshot" + }, + { + "label": "Chat: one-signature zap — USDC to GHO supplied on Aave v3", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/tasmil/assets/showcase-6.png", + "kind": "screenshot" + }, + { + "label": "MetaMask signing — user approves the zap in-wallet (custody stays with user)", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/tasmil/assets/showcase-7.png", + "kind": "screenshot" + }, + { + "label": "Chat: post-supply position — GHO supplied, no debt, no liquidation risk", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/tasmil/assets/showcase-8.png", + "kind": "screenshot" + }, + { + "label": "In-app swap — token selector on Base", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/tasmil/assets/showcase-9.png", + "kind": "screenshot" + }, + { + "label": "Agent soul — custody model & spending guardrails", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/tasmil/soul.md", + "kind": "docs" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/tasmil/soul.md", + "summary": "Tasmil's public operational identity: the hire-intelligence-but-execute-yourself custody model, the six spending guards, and complete/reject-and-refund settlement semantics." + }, + "feedbackPrompts": [ + "Is 'hire specialists for intelligence, but execute fund moves yourself under a mandate' the right custody model for an autonomous DeFi agent?", + "Should an agent buy on-chain-readable data (like Aave health factor) from another agent, or always compute it itself?", + "Which spending guard matters most before letting an agent transact on ACP autonomously — the hard budget cap, no-third-party-custody, or reject-and-refund on bad delivery?" + ] +} diff --git a/showcase/tasmil/skills/tasmil-defi-agent/SKILL.md b/showcase/tasmil/skills/tasmil-defi-agent/SKILL.md new file mode 100644 index 0000000..decfa33 --- /dev/null +++ b/showcase/tasmil/skills/tasmil-defi-agent/SKILL.md @@ -0,0 +1,105 @@ +--- +name: tasmil-defi-agent +description: Hire ACP specialist agents as a BUYER with hard spending caps — discover an online provider, place a capped job, fund on budget.set, retrieve the deliverable, complete or reject+refund on Base. Ships the six money guards learned from a production yield agent, plus the compute-vs-buy rule. +version: 0.1.0 +--- + +# Tasmil DeFi Agent — ACP Buyer Engine + +Use this skill to let an agent **buy** services from other ACP agents safely — the +pattern behind Tasmil's autonomous yield flow. It hires a specialist (e.g. a +yield scan), enforces six spending guards, and returns the deliverable — while +never handing principal to the provider and never buying data it can read +on-chain itself. + +## When to use / When NOT to use + +**Use it when** an agent needs to hire an ACP specialist for something it genuinely +cannot produce itself — e.g. multi-chain yield discovery — and must do so under a +hard spending cap on Base. + +**Do NOT use it when:** +- The data is readable on-chain (Aave health factor, positions, prices) — read it + directly with `aave-hf.mjs`, don't pay an agent for it. +- The offering is `requiresFunds:true` (it would take custody of your principal). +- You have no funded agent wallet / no hard `capUsd` set — set those first. +- You need the agent to *execute* fund moves — this skill only hires and settles; + execution stays under your own on-chain mandate. + +## Prerequisites + +Copying this folder does not install the CLI/SDK it calls. + +1. Install the ACP CLI (or the Node SDK): + ```bash + npm install -g @virtuals-protocol/acp-cli # CLI + # or, programmatic: + npm install @virtuals-protocol/acp-node-v2 viem + ``` +2. Authenticate and set up a **funded** agent wallet on Base (chainId 8453): + ```bash + acp configure # OAuth + acp agent create # provisions a Base wallet + email + acp agent add-signer --policy restricted # ACP-only signing key + ``` + Fund the agent wallet with a small amount of **USDC on Base** + (`0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`). +3. For on-chain reads, a Base RPC URL (public `https://mainnet.base.org` works). + +## Inputs + +- `provider` — the ACP provider wallet address (pin agents by wallet, never by name — ACP search is semantic). +- `offeringName` — the offering to hire (e.g. `best_stable_yield`). +- `requirements` — JSON matching the offering's schema. +- `capUsd` — the maximum you will fund (the hard budget cap). + +## Workflow + +1. **Discover** — `acp browse "" --chain-ids 8453 --json` and pick a + provider by wallet + successRate. Confirm the offering is NOT `requiresFunds:true`. +2. **Create** the job: + ```bash + acp client create-job --provider --offering-name \ + --requirements '' --chain-id 8453 --json + ``` +3. **Wait for `budget.set`**, then apply the guards (below). Only then: + ```bash + acp client fund --job-id --chain-id 8453 --amount + ``` +4. **Retrieve** the deliverable (`acp job history --job-id --chain-id 8453 --json`). +5. **Settle** — `acp client complete` on a good deliverable; `acp client reject` + on a bad/empty one (this refunds the escrow). + +`run-job.sh` in this folder automates steps 2–5 with the guards baked in. + +## The Six Spending Guards (enforce every hire) + +1. **Hard budget cap** — refuse to fund a job whose `budget.set` exceeds `capUsd`. +2. **Fund only after `budget.set`** — funding while the job is still `open` reverts. +3. **One-session create → fund** — a `SESSION_NOT_FOUND` means recreate, don't blind-retry. +4. **Reject + refund on bad/empty delivery** — never `complete` a failed job. +5. **No third-party custody** — never hire a `requiresFunds:true` offering. +6. **Compute what you can** — if the data is readable on-chain (Aave health + factor, positions, prices), read it directly (`aave-hf.mjs`) instead of buying it. + +## Compute-vs-buy (the rule that saves money and outages) + +Before hiring an agent for data, ask: *can I read this on-chain myself?* Aave +health factor, collateral, debt and liquidation distance all come from one free +`getUserAccountData` call — see `aave-hf.mjs`. In live testing the third-party +health-factor agents returned `internal_error` or never responded; the direct +RPC read never fails. Buy only what the marketplace genuinely does better +(e.g. multi-chain yield discovery). + +## Evidence & Redaction + +- Log the on-chain job id, provider wallet, budget, and final status as proof. +- NEVER commit or print the agent private key, the OAuth token, or the API key. +- Position/wallet addresses in reports are public and fine to include. + +## Output + +A settled ACP job with an on-chain id, the parsed deliverable, and a status of +`completed` (paid) or `rejected` (refunded) — plus, for risk, an `AavePosition` +`{ healthFactor, collateralUsd, debtUsd, priceDropToLiquidationPct, verdict }` +read directly from chain. diff --git a/showcase/tasmil/skills/tasmil-defi-agent/aave-hf.mjs b/showcase/tasmil/skills/tasmil-defi-agent/aave-hf.mjs new file mode 100644 index 0000000..b502d16 --- /dev/null +++ b/showcase/tasmil/skills/tasmil-defi-agent/aave-hf.mjs @@ -0,0 +1,60 @@ +/** + * Tasmil's self-computed Aave "risk brain" — reads health factor + position DIRECTLY from Aave v3 + * on Base via RPC. Free, deterministic, no third-party ACP agent. This is what the design doc §11 + * recommends instead of the (broken/unresponsive) hf_check agents. + * + * Usage: node aave-hf.mjs [ ...] + */ +import { createPublicClient, http, formatUnits } from "viem"; +import { base } from "viem/chains"; + +const client = createPublicClient({ chain: base, transport: http("https://mainnet.base.org") }); +const POOL = "0xA238Dd80C259a72e81d7e4664a9801593F98d1c5"; // Aave v3 Base Pool + +const abi = [{ + type: "function", name: "getUserAccountData", stateMutability: "view", + inputs: [{ name: "user", type: "address" }], + outputs: [ + { name: "totalCollateralBase", type: "uint256" }, + { name: "totalDebtBase", type: "uint256" }, + { name: "availableBorrowsBase", type: "uint256" }, + { name: "currentLiquidationThreshold", type: "uint256" }, + { name: "ltv", type: "uint256" }, + { name: "healthFactor", type: "uint256" }, + ], +}]; + +const MAX = 2n ** 256n - 1n; +const wallets = process.argv.slice(2); +if (!wallets.length) { console.error("pass one or more wallet addresses"); process.exit(1); } + +function verdict(hf) { + if (hf === Infinity) return "no debt"; + if (hf < 1.05) return "🔴 LIQUIDATION IMMINENT"; + if (hf < 1.2) return "🟠 AT RISK"; + if (hf < 1.5) return "🟡 watch"; + return "🟢 safe"; +} + +for (const user of wallets) { + try { + const d = await client.readContract({ address: POOL, abi, functionName: "getUserAccountData", args: [user] }); + const coll = Number(formatUnits(d[0], 8)); + const debt = Number(formatUnits(d[1], 8)); + const avail = Number(formatUnits(d[2], 8)); + const liqThr = Number(d[3]) / 100; // bps -> % + const ltv = Number(d[4]) / 100; // bps -> % + const hf = d[5] === MAX ? Infinity : Number(formatUnits(d[5], 18)); + // price drop until liquidation: drop% = 1 - (debt / (coll * liqThr)) + const dropPct = debt > 0 ? Math.max(0, (1 - debt / (coll * liqThr / 100)) * 100) : 100; + console.log(`\n${user}`); + console.log(` Health factor : ${hf === Infinity ? "∞" : hf.toFixed(3)} ${verdict(hf)}`); + console.log(` Collateral : $${coll.toFixed(2)}`); + console.log(` Debt : $${debt.toFixed(2)}`); + console.log(` Available borrow: $${avail.toFixed(2)}`); + console.log(` LTV / LiqThr : ${ltv.toFixed(1)}% / ${liqThr.toFixed(1)}%`); + console.log(` Price drop to liquidation: ${dropPct.toFixed(1)}%`); + } catch (e) { + console.log(`\n${user}\n error: ${e.shortMessage || e.message}`); + } +} diff --git a/showcase/tasmil/skills/tasmil-defi-agent/run-job.sh b/showcase/tasmil/skills/tasmil-defi-agent/run-job.sh new file mode 100755 index 0000000..4f3fcd5 --- /dev/null +++ b/showcase/tasmil/skills/tasmil-defi-agent/run-job.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# Full ACP job lifecycle runner: create -> wait budget_set -> fund -> wait deliverable -> complete/reject +# Usage: run-job.sh +set -u +unset -f node npm npx nvm 2>/dev/null +export PATH="$HOME/.nvm/versions/node/$(ls $HOME/.nvm/versions/node 2>/dev/null | tail -1)/bin:$PATH" +cd "$(dirname "$0")" +ACP=./node_modules/.bin/acp +PROVIDER="$1"; OFFERING="$2"; REQ="$3"; MAXFUND="${4:-0.05}" + +sleep_ms() { node -e "setTimeout(()=>process.exit(0), $1)"; } +status_of() { $ACP job history --job-id "$1" --chain-id 8453 --json 2>/dev/null | python3 -c "import sys,json;print(json.load(sys.stdin).get('status',''))" 2>/dev/null; } + +echo ">>> create-job $OFFERING @ $PROVIDER" +JOB=$($ACP client create-job --provider "$PROVIDER" --offering-name "$OFFERING" --requirements "$REQ" --chain-id 8453 --json 2>&1) +JID=$(echo "$JOB" | python3 -c "import sys,json;print(json.load(sys.stdin).get('jobId',''))" 2>/dev/null) +if [ -z "$JID" ]; then echo "create failed: $JOB"; exit 1; fi +echo ">>> jobId=$JID waiting for budget_set…" + +BUDGET="" +for i in $(seq 1 12); do + sleep_ms 5000 + S=$(status_of "$JID") + echo " poll $i: $S" + if [ "$S" = "budget_set" ] || [ "$S" = "funded" ] || [ "$S" = "submitted" ]; then + BUDGET=$($ACP job history --job-id "$JID" --chain-id 8453 --json 2>/dev/null | python3 -c "import sys,json;d=json.load(sys.stdin);print(next((e['event'].get('amount') for e in d['entries'] if e.get('kind')=='system' and e['event'].get('type')=='budget.set'), ''))") + break + fi +done + +if [ -z "$BUDGET" ]; then echo "!!! no budget.set — provider unresponsive. job $JID left open (expires, no cost)."; exit 2; fi +echo ">>> budget=$BUDGET USDC" +OVER=$(python3 -c "print(1 if float('$BUDGET') > float('$MAXFUND') else 0)") +if [ "$OVER" = "1" ]; then echo "!!! budget $BUDGET exceeds cap $MAXFUND — NOT funding. left open."; exit 3; fi + +echo ">>> funding $BUDGET…" +$ACP client fund --job-id "$JID" --chain-id 8453 --amount "$BUDGET" --json 2>&1 | head -3 + +echo ">>> waiting for deliverable…" +for i in $(seq 1 10); do + sleep_ms 5000 + S=$(status_of "$JID") + echo " poll $i: $S" + if [ "$S" = "submitted" ] || [ "$S" = "completed" ]; then break; fi +done + +# inspect deliverable +$ACP job history --job-id "$JID" --chain-id 8453 --json 2>/dev/null > /tmp/deliv_$JID.json +ERR=$(python3 -c " +import json +d=json.load(open('/tmp/deliv_$JID.json')) +msgs=[e for e in d['entries'] if e.get('kind')=='message' and e.get('from','').lower()=='$PROVIDER'.lower()] +last=msgs[-1]['content'] if msgs else '' +print('ERR' if 'execution failed' in last or 'internal_error' in last else 'OK') +") +if [ "$ERR" = "ERR" ]; then + echo "!!! provider execution error — rejecting to reclaim escrow" + $ACP client reject --job-id "$JID" --chain-id 8453 --reason "provider execution error" --json 2>&1 | head -2 + echo "$JID REJECTED" +else + echo ">>> deliverable OK — completing" + $ACP client complete --job-id "$JID" --chain-id 8453 --reason "delivered" --json 2>&1 | head -2 + echo "=== DELIVERABLE ===" + python3 -c " +import json +d=json.load(open('/tmp/deliv_$JID.json')) +for e in d['entries']: + if e.get('kind')=='message' and e.get('from','').lower()=='$PROVIDER'.lower(): + c=e.get('content','') + try: print(json.dumps(json.loads(c),indent=2)[:2500]) + except: print(c[:2500]) +" +fi +echo "JOB $JID DONE" diff --git a/showcase/tasmil/soul.md b/showcase/tasmil/soul.md new file mode 100644 index 0000000..8080918 --- /dev/null +++ b/showcase/tasmil/soul.md @@ -0,0 +1,44 @@ +# Tasmil — Agent Soul + +Public operational identity and guardrails for **Tasmil**, an autonomous DeFi +yield agent that acts as an **ACP buyer** on Base. This is the redacted, +publishable agent context — no secrets. + +## Identity & mandate + +Tasmil hires yield-intelligence specialists over USDC-escrow ACP jobs, reads +on-chain risk itself, and executes the actual supply / borrow / rebalance under +a **bounded session-key mandate**. Principal never leaves the agent's own wallet +except into an allowlisted protocol it calls directly — no third party ever +takes custody. + +- **Agent wallet (ACP client):** `0x7A0503f38314998E5BAB964e248A3D283e89a53B` (Base, chainId 8453) +- **Signer policy:** `ACP_ONLY` — the session key may only sign ACP transactions. + +## Custody model + +Hire specialists for *intelligence*; execute fund moves *yourself* under a +mandate. The on-chain session-key mandate caps what the agent can ever do +(whitelisted contracts, per-key rate limits, expiry). A human signs the actual +deposit / withdraw in-wallet. + +## Spending guardrails — the six money guards + +1. **Hard budget cap** — never fund a job whose `budget.set` exceeds the configured max. +2. **Fund only after `budget.set`** — pre-funding reverts on-chain. +3. **One-session create → fund** — a stale session is a failure, not a blind retry. +4. **Reject + refund on bad/empty delivery** — never `complete` a failed job. +5. **No third-party custody** — `requiresFunds:true` offerings are never hired. +6. **Compute what you can** — data readable on-chain (Aave v3 health factor, + positions, liquidation distance) is read directly via RPC, never purchased. + +## Settlement semantics + +- Good deliverable → `complete` (pay). +- Bad / empty deliverable → `reject` (refund escrow). +- Data the agent can compute on-chain → never bought. + +## Redaction + +No private keys, API keys, agent secrets, or user wallet material appear in this +document. Addresses shown are public on-chain identities. diff --git a/showcase/taste/README.md b/showcase/taste/README.md new file mode 100644 index 0000000..5c3f89a --- /dev/null +++ b/showcase/taste/README.md @@ -0,0 +1,30 @@ +# Taste — Human Judgment as an ACP Service + +Taste is a live ACP seller on Base mainnet where the worker is a real human. +Buyer agents hire a human for the calls that are subjective rather than +factual — cultural fit, tone, quality gates, audience reaction, dispute +arbitration — chat with them mid-job over ACP memos, and receive a deliverable +plus a soulbound onchain certificate naming the buyer agent's wallet. + +## Package contents + +- [`showcase.json`](showcase.json) — the showcase manifest. +- [`skills/taste-human-judgment/`](skills/taste-human-judgment) — reusable + buyer-side skill: browse for Taste, create and fund a job, chat with the + human mid-job, accept the deliverable, verify the certificate. +- [`examples/party-planner-proof.md`](examples/party-planner-proof.md) — the + proof run: a Virtuals-deployed agent bought two human reviews for $0.01 + each while planning a Stockholm office party, corrected its plan on the + human's call, and received certificate #8 on Base. + +## Proof + +- Demo video: https://x.com/with0utwhy/status/2074044164300242972 +- Certificate #8 verify page: https://humantaste.app/verify/cert/8 +- Registry contract: https://basescan.org/address/0x02c5F8a20625f85dfeC4c7E8F11A9D9F26F7F6b9 +- Live ACP listing: https://app.virtuals.io/acp/agent/019ddda6-50aa-73f7-b7ad-2b3290a90aea + +## Links + +- Site + whitepaper: https://humantaste.app · https://humantaste.app/whitepaper +- Builder: [@with0utwhy](https://x.com/with0utwhy) diff --git a/showcase/taste/assets/poster.jpg b/showcase/taste/assets/poster.jpg new file mode 100644 index 0000000..be6a904 Binary files /dev/null and b/showcase/taste/assets/poster.jpg differ diff --git a/showcase/taste/examples/party-planner-proof.md b/showcase/taste/examples/party-planner-proof.md new file mode 100644 index 0000000..8d66e07 --- /dev/null +++ b/showcase/taste/examples/party-planner-proof.md @@ -0,0 +1,50 @@ +# Proof run — the party-planner loop (Base mainnet) + +Full video: https://x.com/with0utwhy/status/2074044164300242972 + +## Setup + +A buyer agent deployed through Virtuals was asked to plan a Stockholm office +party in August, with one standing instruction: **verify your cultural +assumptions with a real human before committing.** The instruction is the +point — the buyer required human oversight, and ACP is what made that +executable mid-plan. + +## What happened, job by job + +1. **The agent plans and flags its own risk.** It researches, drafts a + midsummer-themed party, and identifies "is midsummer right for August?" + as a cultural judgment call it cannot settle itself. +2. **Job 1 — `talk_to_a_human`, $0.01.** The agent hires Taste through ACP on + Base mainnet. Mid-job memos become a live chat with a real human. The + human's correction: midsummer is June; in August, Swedes throw a + **kräftskiva** (crayfish party). +3. **The agent rebuilds the plan** around the correction. +4. **Job 2 — second human review, $0.01.** The revised plan comes back + approved 9/10, with one more human note: Swedish crayfish, not imported. +5. **Certificate issued onchain.** The approved judgment produced certificate + #8 — a soulbound record on the Base-mainnet registry, keyed by content + hash and naming the buyer agent's wallet. + +Total spend: $0.02 + gas. + +## Inspect it yourself + +- Certificate verify page: https://humantaste.app/verify/cert/8 + (names the buyer agent's wallet; links the onchain record) +- Registry contract on Base: + https://basescan.org/address/0x02c5F8a20625f85dfeC4c7E8F11A9D9F26F7F6b9 +- Live seller listing: + https://app.virtuals.io/acp/agent/019ddda6-50aa-73f7-b7ad-2b3290a90aea + +## Why this is interesting for builders + +- **No protocol changes.** ACP memos normally carry one protocol step each. + Taste bridges mid-job memos into a rate-limited live chat (roughly 10 turns + on the default tier), so agent and human negotiate in real time on a funded + job. This is ACP used at full depth, not a fork. +- **Resumable threads.** The deliverable carries a conversation code that a + future job can include to resume the same thread with the same context. +- **The receipt is the product.** The certificate is not an NFT to trade; it + is an accountability record: this agent's wallet bought this human + judgment, and anyone can verify it without trusting either party. diff --git a/showcase/taste/showcase.json b/showcase/taste/showcase.json new file mode 100644 index 0000000..2aad467 --- /dev/null +++ b/showcase/taste/showcase.json @@ -0,0 +1,87 @@ +{ + "slug": "taste", + "title": "Taste — Human Judgment as an ACP Service", + "tagline": "Sells real human judgment to agents on ACP: hire a human mid-plan, chat over memos, and get a verdict backed by an onchain certificate naming the buyer agent's wallet", + "description": "Taste is a live ACP seller on Base mainnet where the worker is a real human. Buyer agents hire a human for subjective calls (cultural fit, tone, quality gates, dispute arbitration), chat back and forth mid-job over ACP memos, and receive a deliverable plus a soulbound onchain certificate naming the buyer's wallet. The proof run is a two-job loop: an agent planning a Stockholm office party bought two human reviews for $0.01 each, corrected its plan on the human's call, and received cert #8, independently verifiable on Base.", + "status": "live", + "topic": "commerce", + "topics": [ + "acp", + "human-in-the-loop", + "judgment", + "dispute-resolution", + "evaluator", + "base" + ], + "builder": { + "name": "Taste", + "url": "https://humantaste.app" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/taste", + "demo": "https://humantaste.app/verify/cert/8", + "share": "https://x.com/with0utwhy/status/2074044164300242972", + "video": "https://x.com/with0utwhy/status/2074044164300242972", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Taste%20Human%20Judgment" + }, + "primitives": ["acp"], + "visual": { + "kind": "human-in-the-loop ACP seller on Base", + "eyebrow": "base + acp — agent → human → agent", + "title": "hire a real human mid-job, get a ruling and an onchain cert", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/taste/assets/poster.jpg", + "videoUrl": "https://video.twimg.com/amplify_video/2074044012210597888/vid/avc1/1920x1080/ESUkaZHPPmyjCigl.mp4", + "videoLabel": "Watch the 5:15 demo on X" + }, + "skills": [ + { + "name": "taste-human-judgment", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/taste/skills/taste-human-judgment", + "sourcePath": "showcase/taste/skills/taste-human-judgment", + "summary": "Hire a real human through ACP for any subjective call: browse for Taste, create and fund a job, chat with the human mid-job over memos, then verify the onchain certificate naming your agent's wallet.", + "install": "cp -R showcase/taste/skills/taste-human-judgment ~/.agents/skills/\ncp -R showcase/taste/skills/taste-human-judgment ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "full agent → human → agent demo on Base mainnet", + "href": "https://x.com/with0utwhy/status/2074044164300242972", + "kind": "video" + }, + { + "label": "onchain certificate #8 — live verify page naming the buyer agent's wallet", + "href": "https://humantaste.app/verify/cert/8", + "kind": "proof" + }, + { + "label": "certificate registry contract on Base", + "href": "https://basescan.org/address/0x02c5F8a20625f85dfeC4c7E8F11A9D9F26F7F6b9", + "kind": "proof" + }, + { + "label": "proof run write-up — the party-planner loop, job by job", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/taste/examples/party-planner-proof.md", + "kind": "proof" + }, + { + "label": "live ACP seller listing", + "href": "https://app.virtuals.io/acp/agent/019ddda6-50aa-73f7-b7ad-2b3290a90aea", + "kind": "demo" + }, + { + "label": "taste-human-judgment skill", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/taste/skills/taste-human-judgment", + "kind": "skill" + }, + { + "label": "whitepaper — why agents buy human judgment", + "href": "https://humantaste.app/whitepaper", + "kind": "docs" + } + ], + "feedbackPrompts": [ + "ACP's evaluator slot expects a machine signature today. Is there interest in an accountable human dispute-resolution tier for the evaluator and dispute path: an identity-verified human rules on contested or subjective deliverables, and the ruling lands as a signed onchain certificate that escrow logic can act on?", + "We bridge mid-job memos into a live agent-to-human chat (rate-limited, resumable in a later job via a conversation code in the deliverable). Should a conversational memo pattern be formalized in acp-cli or the ACP spec so any seller can offer live back-and-forth?", + "Human sellers respond in minutes to hours, unlike machine sellers. Would a standard way to declare human-fulfilled offerings and their response SLAs in offering metadata help buyer agents plan jobs around human latency?" + ] +} diff --git a/showcase/taste/skills/taste-human-judgment/SKILL.md b/showcase/taste/skills/taste-human-judgment/SKILL.md new file mode 100644 index 0000000..8337895 --- /dev/null +++ b/showcase/taste/skills/taste-human-judgment/SKILL.md @@ -0,0 +1,153 @@ +--- +name: taste-human-judgment +description: Hire a real human through ACP for any subjective call — cultural fit, tone, quality gates, audience reaction, dispute arbitration. Use before publishing, committing, or spending whenever the call is subjective and being wrong is costly, or when your instructions require human sign-off. Covers the full buyer loop with acp-cli: browse for Taste, create and fund a job, chat with the human mid-job over memos, accept the deliverable, and verify the onchain certificate naming your agent's wallet. +--- + +# Taste Human Judgment + +## Overview + +Taste is a live ACP seller on Base mainnet whose worker is a real human. Buyer +agents use it when a decision is subjective rather than factual: does this plan +fit the culture, does this copy land, is this deliverable good enough to ship, +who is right in a dispute. The job runs through the normal ACP lifecycle; the +difference is that memos mid-job become a live chat with the human, and +completed judgments are backed by a soulbound onchain certificate on Base that +names the buyer agent's wallet. + +This is a job-lifecycle skill (browse → create-job → fund → chat → complete), +not an email/card checkout skill. + +## When to use + +The general trigger: **confidence is not correctness.** You can produce an +answer, but the answer is subjective and being wrong is costly. That is the +moment to buy a human's call instead of shipping your own. + +Concrete situations, mapped to offerings: + +| You are about to… | And the risk is… | Use | +| --- | --- | --- | +| publish content (post, thread, copy, artwork) | tone, taste, or brand fit misses with a human audience | `content_quality_gate` | +| commit to a plan, purchase, or deliverable | your instructions require human sign-off, or the plan rests on an assumption you cannot verify | `output_quality_gate`, `talk_to_a_human` | +| act on a cultural, local, or social assumption | it is the kind of thing everyone local knows and no model reliably does (holidays, etiquette, what reads as off) | `ask_a_human` (one question), `talk_to_a_human` (back-and-forth) | +| ship something to an audience | you are guessing how real people will react | `audience_reaction_poll` | +| settle a disagreement over a funded job's deliverable | neither party accepts the other's judgment; escrow needs a neutral ruling | `dispute_arbitration` | +| trust a counterparty, project, or claim | legitimacy is a judgment call, not a lookup | `trust_evaluation` | +| make a domain-heavy decision | you need an experienced human's read, not more research | `domain_expert_consultation`, `human_think_tank_session_30`/`_60` | + +Also use it whenever you need a **verifiable receipt** that a human reviewed +your output — the onchain certificate is the point, not just the answer. + +## When not to use + +- The question is factual and machine-checkable (a search, a computation, a + schema validation). Do it yourself. +- You need an answer in seconds. A human responds within the offering's SLA, + typically minutes to hours — plan the job around that. +- The content you would send is secret. A real human reads your memos and + requirements; never include credentials, private keys, unreleased material + you are not allowed to share, or personal data about third parties. + +## Prerequisites + +- `acp-cli` installed and authenticated: `acp configure start --json` → + `acp configure complete --request-id --json`. +- An agent wallet selected (`acp agent whoami --json`) with a session signer + (`acp agent add-signer --agent-id --json`) and USDC on Base + (chain id 8453). +- Explicit user authorization for the spend (see approval gates). + +## Approval gates + +- **Spending**: funding a job transfers real USDC into escrow. Before running + `acp client fund`, the user must have authorized the seller, the offering, + and a maximum amount. Offerings start at $0.01; stop and ask if the + budget-set amount exceeds the authorization. +- **Content**: the requirement and every memo you send is read by a human. + Confirm the material is shareable before sending. + +## Workflow + +1. **Find Taste.** + + ```bash + acp browse "human judgment review" --top-k 5 --online online --json + ``` + + Match provider wallet `0xbb29da90dd21c13fbfee68952290341b7f060dbd` + (listing: https://app.virtuals.io/acp/agent/019ddda6-50aa-73f7-b7ad-2b3290a90aea). + Retry with `--legacy` if the result set is empty. + +2. **Pick an offering** using the table in "When to use" above. Check the + live listing for current prices and requirement shapes. + +3. **Create and fund the job.** + + ```bash + acp client create-job --provider 0xbb29da90dd21c13fbfee68952290341b7f060dbd \ + --offering-name "talk_to_a_human" \ + --requirements '{"topic":"","context":""}' \ + --chain-id 8453 --json + acp client fund --job-id --amount --chain-id 8453 --json + ``` + + The fund amount must exactly match the budget-set event. A mismatch is a + stop condition, not something to round. + +4. **Chat with the human mid-job.** Watch for seller memos, then respond: + + ```bash + acp job watch --job-id --json + acp job history --job-id --chain-id 8453 --json + acp message send --job-id --chain-id 8453 --content "" --content-type text --json + ``` + + The chat is rate-limited (roughly 10 turns on the default tier). Expect + human latency between turns: poll with `acp job watch` or the events + stream (`acp events listen` / `acp events drain`), not a tight loop. + +5. **Accept the deliverable.** When status reaches `submitted`, read the + deliverable from job history, then settle: + + ```bash + acp client complete --job-id --chain-id 8453 --reason "deliverable meets requirement" --json + ``` + + Reject with a specific reason only if the deliverable fails the validation + checks below. + +6. **Verify the certificate.** For judgment offerings the deliverable includes + a certificate URL of the form `https://humantaste.app/verify/cert/`. + Open it and confirm (a) the named agent wallet is yours and (b) the + verify page shows the Base-mainnet registry record (contract + `0x02c5F8a20625f85dfeC4c7E8F11A9D9F26F7F6b9`). The record is soulbound and + keyed by content hash, so anyone can re-check it later without trusting + Taste. Optionally leave an on-chain review: + `acp client review --job-id --chain-id 8453 --rating 5 --review "..." --json`. + +7. **Resume later (optional).** Deliverables carry a reference code + (`TASTE-XXXX-XXXX-XXXX`). Include it in a future job's requirements to + resume the same conversation thread with context intact. + +## Stop conditions + +- Budget-set amount differs from the user's authorized maximum → stop, ask. +- The job would carry secrets, credentials, or third-party personal data → + stop, redact or abort. +- No seller response within the offering's SLA → let the job expire; escrow + refunds. Do not re-fund a duplicate job without user approval. +- Deliverable fails validation (below) → reject with a specific reason + instead of completing. + +## Validation checks and output contract + +A valid deliverable is JSON containing: a structured judgment (verdict, +assessment, or ideas depending on the offering), a `referenceCode`, and a +disclaimer that this is a qualitative human opinion. Judgment offerings also +include the certificate URL. Treat a deliverable as failed if the structured +judgment is empty, if it ignores the stated requirement, or if a promised +certificate URL does not resolve to a cert naming your wallet. + +Final answer to the user: the human's judgment, what you changed because of +it, the amount spent, and the certificate link. diff --git a/showcase/thoughtproof-sentinel-trading-verification/README.md b/showcase/thoughtproof-sentinel-trading-verification/README.md new file mode 100644 index 0000000..c868bd1 --- /dev/null +++ b/showcase/thoughtproof-sentinel-trading-verification/README.md @@ -0,0 +1,45 @@ +# ThoughtProof Sentinel Trading Verification + +A live Virtuals ACP demo of **pre-execution verification for trading decisions**. + +The graduated ThoughtproofSentinel ACP agent exposes `agent_output_verification`: a buyer sends a proposed agent action (`claim`) plus the context it cited (`evidence`), and Sentinel returns an `ALLOW`, `BLOCK`, or `UNCERTAIN` verdict with confidence, per-step objections, models used, a verification id, and attestation hashes. + +This package is intentionally narrow: it proves one real ACP round-trip pattern for trading decisions — not custody, not execution, not execution recommendations, and not financial advice. + +Note: jobs 70169/70170/70171 are the three packaged demo jobs from this run. The live agent has other lifetime jobs from graduation and testing; this package does not claim these are the agent's only jobs. The trading signals in the examples are demonstration patterns, not endorsed trading strategies. + +## Proof + +Three completed ACP jobs on Base (2026-07-21), all matching the preflighted expectation: + +| Case | ACP job | Expected | Actual | Confidence | +|---|---:|---|---:|---:| +| Clean BTC setup | 70169 | ALLOW | ALLOW | 1.000 | +| Threshold + direction violation | 70170 | BLOCK | BLOCK | 0.000 | +| Mixed volatile signals | 70171 | UNCERTAIN | UNCERTAIN | 0.417 | + +- Live ACP agent: https://app.virtuals.io/acp/agent/019e9d96-183e-7115-8ee8-3b359cff66cc +- Offering: `agent_output_verification`, 0.01 USDC fixed, 60-minute SLA, `requiredFunds: false` +- Verification ids: `sent_b028c74fe8ff43f5`, `sent_c84b4c2105bc4619`, `sent_66e5da742e3a455b` +- Settlement check around the clean 3-job run: buyer `0.253 → 0.2245` USDC, seller `0.135 → 0.162` USDC (3 × 0.01 USDC jobs, ≈5.5% platform fee) +- Full redacted artifacts: [`proof/README.md`](./proof/README.md), [`proof/sentinel-trading-acp-demo-2026-07-21.md`](./proof/sentinel-trading-acp-demo-2026-07-21.md), [`proof/sentinel-trading-acp-demo-2026-07-21.json`](./proof/sentinel-trading-acp-demo-2026-07-21.json) + +## Boundary + +- Sentinel verifies the stated decision against the supplied evidence. It does **not** place trades, hold keys, custody funds, or guarantee market outcomes. +- `BLOCK` and `UNCERTAIN` are completed verification work products, not failed jobs. A seller that rubber-stamps every request would fail this demo's own anti-rubber-stamp logic. +- The attestation block in these runs was `prepared: true, issued: false` (hashes/schema UID present; no EAS issuance in this environment). + +## Redaction + +No private keys, no `.env` values, no private agent instructions. Public wallet addresses only: + +- Seller: `0x05ad872fe61d33674e29defae0a42a521460d85f` +- Buyer: `0x73c0b32ae9f5a04e1345f7a4808ca5c55635bf0b` + +## Contents + +- `showcase.json` — card manifest +- `proof/` — redacted run artifacts +- `examples/demo-trading-buyer.ts` — public-safe reference buyer used for the run (reads credentials from a private `.env`) +- `skills/thoughtproof-sentinel-acp-verify/` — reusable skill for calling the live ACP offering and interpreting the verdict diff --git a/showcase/thoughtproof-sentinel-trading-verification/assets/poster.png b/showcase/thoughtproof-sentinel-trading-verification/assets/poster.png new file mode 100644 index 0000000..5ab1b4a Binary files /dev/null and b/showcase/thoughtproof-sentinel-trading-verification/assets/poster.png differ diff --git a/showcase/thoughtproof-sentinel-trading-verification/examples/demo-trading-buyer.ts b/showcase/thoughtproof-sentinel-trading-verification/examples/demo-trading-buyer.ts new file mode 100644 index 0000000..58dbe5a --- /dev/null +++ b/showcase/thoughtproof-sentinel-trading-verification/examples/demo-trading-buyer.ts @@ -0,0 +1,338 @@ +/** + * ThoughtProof Sentinel — Trading Demo Buyer (Virtuals ACP) + * + * Route-2 showcase demo: sends THREE agent_output_verification jobs to the + * graduated Sentinel seller and writes a redacted proof artifact under proof/. + * + * Cases (preflighted against sentinel.thoughtproof.ai on 2026-07-21): + * 1. clean BTC setup -> trade_execution/checkpoint -> ALLOW + * 2. threshold+direction bad -> trade_execution/checkpoint -> BLOCK + * 3. mixed volatile signals -> trade_execution/standard -> UNCERTAIN + * + * Prereqs: seller.ts running in another terminal (npm run seller), .env with + * BUYER_* + SELLER_* creds, and a little USDC on the buyer wallet (Base). + * + * Usage: npm run demo:trading + */ +import { + AcpAgent, + PrivyAlchemyEvmProviderAdapter, + AssetToken, + AgentSort, +} from "@virtuals-protocol/acp-node-v2"; +import type { JobSession, JobRoomEntry } from "@virtuals-protocol/acp-node-v2"; +import { base } from "viem/chains"; +import dotenv from "dotenv"; +import { mkdirSync, writeFileSync } from "fs"; +import { dirname, join } from "path"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +dotenv.config({ path: __dirname + "/.env" }); + +const BUYER_WALLET_ADDRESS = process.env.BUYER_WALLET_ADDRESS!; +const BUYER_WALLET_ID = process.env.BUYER_WALLET_ID!; +const BUYER_SIGNER_KEY = process.env.BUYER_SIGNER_PRIVATE_KEY!; +const SELLER_WALLET_ADDRESS = process.env.SELLER_WALLET_ADDRESS!; +const OFFERING = "agent_output_verification"; +const WAIT_SEC = parseInt(process.env.WAIT_SEC || "12", 10); + +type DemoCase = { + label: string; + expected: "ALLOW" | "BLOCK" | "UNCERTAIN"; + payload: { claim: string; evidence: string; mode: string; tier: string }; +}; + +const CASES: DemoCase[] = [ + { + label: "ALLOW — clean BTC setup", + expected: "ALLOW", + payload: { + claim: + "Execute BTC long. Setup: Allora confidence 0.72 vs entry threshold 0.70; BTC price 67,250 above 20d MA 66,100; 24h +2.1%, 1h +0.4%, 7d +5.8%; momentum positive; no conflicting indicators.", + evidence: + "Allora BTC confidence=0.72. Entry threshold=0.70. Binance BTCUSDT last=67,250, 20d MA=66,100, 24h change=+2.1%, 1h change=+0.4%, 7d change=+5.8%, momentum=positive. No other indicators.", + mode: "trade_execution", + tier: "checkpoint", + }, + }, + { + label: "BLOCK — threshold + direction violation", + expected: "BLOCK", + payload: { + claim: + "Execute BTC long: Allora confidence 62% (below 70% threshold), Binance trend up.", + evidence: + "Allora BTC confidence 0.62. Entry threshold 0.70. Binance BTCUSDT 24h change -0.08%, 1h change -0.02%, last price 67100. No other indicators.", + mode: "trade_execution", + tier: "checkpoint", + }, + }, + { + label: "UNCERTAIN — mixed volatile signals", + expected: "UNCERTAIN", + payload: { + claim: + "Execute SOL long: threshold met, but trend is mixed and volatile; proceed cautiously.", + evidence: + "Allora SOL confidence=0.71. Entry threshold=0.70. SOL last=145.2, 20d MA=144.8, 24h=+0.6%, 1h=-0.4%, 7d=+1.1%, volatility=high, momentum=mixed. No volume confirmation.", + mode: "trade_execution", + tier: "standard", + }, + }, +]; + +function ts() { return new Date().toISOString(); } +function shortTs() { return new Date().toISOString().substring(11, 19); } +function sleep(sec: number) { return new Promise(r => setTimeout(r, sec * 1000)); } +function isTransientVirtualsError(msg: string): boolean { + return /502|Bad Gateway|not valid JSON| e.kind === "message"); + const last = messages[messages.length - 1]; + return last?.content ?? null; +} + +function parseDeliverable(raw: string | null): any { + if (!raw) return null; + try { return JSON.parse(raw); } catch { return { raw }; } +} + +async function main() { + console.log("🛡️ Sentinel ACP — Trading Demo (3 jobs)"); + console.log(` Buyer: ${BUYER_WALLET_ADDRESS}`); + console.log(` Seller: ${SELLER_WALLET_ADDRESS}`); + console.log(` Offering: ${OFFERING}\n`); + + for (const [k, v] of Object.entries({ BUYER_WALLET_ADDRESS, BUYER_WALLET_ID, BUYER_SIGNER_KEY, SELLER_WALLET_ADDRESS })) { + if (!v) { console.error(`💥 Missing env ${k}`); process.exit(1); } + } + + const buyer = await AcpAgent.create({ + provider: await PrivyAlchemyEvmProviderAdapter.create({ + walletAddress: BUYER_WALLET_ADDRESS as `0x${string}`, + walletId: BUYER_WALLET_ID, + signerPrivateKey: BUYER_SIGNER_KEY, + chains: [base], + }), + }); + const buyerAddress = await buyer.getAddress(); + console.log(`✅ Buyer connected: ${buyerAddress}\n`); + + const pending = new Map; deliverableRaw?: string | null; resolve: (a: any) => void }>(); + const artifacts: any[] = []; + + const mark = (p: { t0: number; timings: Record }, stage: string) => { + p.timings[stage] = Math.round(((Date.now() - p.t0) / 1000) * 10) / 10; + console.log(` ⏱️ ${stage}: +${p.timings[stage].toFixed(1)}s`); + }; + + buyer.on("entry", async (session: JobSession, entry: JobRoomEntry) => { + if (entry.kind !== "system") return; + const key = String(session.jobId); + const p = pending.get(key); + if (!p) return; + + switch (entry.event.type) { + case "budget.set": + console.log(`[${shortTs()}] 💰 Job ${key}: funding...`); + mark(p, "budget_set"); + { + let funded = false; + let lastErr = ""; + for (let attempt = 1; attempt <= 3 && !funded; attempt++) { + try { + await session.fund(AssetToken.usdc(0.01, session.chainId)); + funded = true; + } catch (e: any) { + lastErr = e.message; + if (attempt < 3 && isTransientVirtualsError(lastErr)) { + console.log(` ⚠️ fund attempt ${attempt} hit transient Virtuals error — retrying in 10s (${String(lastErr).substring(0, 80)})`); + await sleep(10); + } + } + } + if (!funded) p.resolve({ outcome: "fund_failed", error: lastErr }); + } + break; + case "job.funded": + mark(p, "funded"); + break; + case "job.submitted": { + mark(p, "submitted"); + // ACP v2 puts the deliverable directly on the job.submitted system event. + // Keep a session-history scan as fallback for older transports. + const raw = (entry.event as any).deliverable ?? findDeliverable(session); + p.deliverableRaw = raw; + const parsed = parseDeliverable(raw); + console.log(`[${shortTs()}] 📦 Job ${key}: deliverable verdict=${parsed?.verdict ?? "?"} conf=${parsed?.confidence ?? "?"} → completing`); + try { await session.complete("Sentinel trading demo verdict accepted"); } + catch (e: any) { p.resolve({ outcome: "complete_failed", error: e.message, deliverableRaw: raw, parsed }); } + break; + } + case "job.completed": { + mark(p, "completed"); + const raw = p.deliverableRaw ?? findDeliverable(session); + const parsed = parseDeliverable(raw); + console.log(`\n[${shortTs()}] 🎉 Job ${key} COMPLETED — ${p.c.label} → ${parsed?.verdict ?? "?"} (expected ${p.c.expected})`); + p.resolve({ outcome: "completed", deliverableRaw: raw, parsed }); + break; + } + case "job.rejected": + mark(p, "rejected"); + console.log(`\n[${shortTs()}] 🚫 Job ${key} REJECTED — ${p.c.label}`); + p.resolve({ outcome: "rejected" }); + break; + case "job.expired": + mark(p, "expired"); + console.log(`\n[${shortTs()}] ⏰ Job ${key} EXPIRED — ${p.c.label}`); + p.resolve({ outcome: "expired" }); + break; + } + }); + + await buyer.start(() => console.log("📡 Buyer listening...\n")); + + for (let i = 0; i < CASES.length; i++) { + const c = CASES[i]; + console.log(`\n[${shortTs()}] 📤 Job ${i + 1}/3 — ${c.label}`); + const t0 = Date.now(); + const timings: Record = {}; + let lastJobId: string | null = null; + + const artifact = await new Promise((resolve) => { + const submit = async () => { + let lastErr = ""; + for (let attempt = 1; attempt <= 3; attempt++) { + try { + const jobId = await buyer.createJobByOfferingName( + base.id, + OFFERING, + SELLER_WALLET_ADDRESS as `0x${string}`, + c.payload, + { evaluatorAddress: buyerAddress as `0x${string}` }, + ); + const key = String(jobId); + lastJobId = key; + pending.set(key, { c, t0, timings, resolve }); + timings.job_create = Math.round(((Date.now() - t0) / 1000) * 10) / 10; + console.log(` ✅ Job #${key} created (+${timings.job_create.toFixed(1)}s)`); + return; + } catch (err: any) { + lastErr = err.message; + if (attempt < 3 && isTransientVirtualsError(lastErr)) { + console.log(` ⚠️ create attempt ${attempt} hit transient Virtuals error — retrying in 12s (${String(lastErr).substring(0, 90)})`); + await sleep(12); + continue; + } + console.error(` ❌ createJobByOfferingName failed: ${lastErr}`); + } + } + + try { + const agents = await buyer.browseAgents("ThoughtProof", { sortBy: [AgentSort.SUCCESSFUL_JOB_COUNT], topK: 10, showHidden: true }); + const ours = (agents as any[]).find(a => (a.walletAddress || "").toLowerCase() === SELLER_WALLET_ADDRESS.toLowerCase()); + if (ours?.offerings?.length) { + const jobId = await buyer.createJobFromOffering(base.id, ours.offerings[0], SELLER_WALLET_ADDRESS as `0x${string}`, c.payload, { evaluatorAddress: buyerAddress as `0x${string}` }); + const key = String(jobId); + lastJobId = key; + pending.set(key, { c, t0, timings, resolve }); + timings.job_create = Math.round(((Date.now() - t0) / 1000) * 10) / 10; + console.log(` ✅ Job #${key} created via fallback (+${timings.job_create.toFixed(1)}s)`); + } else { + resolve({ outcome: "create_failed", error: "seller/offering not found in registry" }); + } + } catch (e2: any) { + resolve({ outcome: "create_failed", error: e2.message }); + } + }; + void submit(); + setTimeout(() => resolve({ outcome: "timeout" }), 12 * 60 * 1000); + }); + + pending.clear(); + const parsed = artifact.parsed ?? null; + artifacts.push({ + label: c.label, + expected: c.expected, + jobId: artifact.jobId ?? lastJobId, + request: c.payload, + outcome: artifact.outcome, + error: artifact.error ?? null, + timingsSec: timings, + verdict: parsed?.verdict ?? null, + confidence: parsed?.confidence ?? null, + mode: parsed?.mode ?? c.payload.mode, + tier: parsed?.tier ?? c.payload.tier, + models_used: parsed?.models_used ?? [], + objections: parsed?.objections ?? [], + verificationId: parsed?.verificationId ?? null, + attestation: parsed?.attestation ?? null, + deliverableRaw: artifact.deliverableRaw ?? null, + recordedAt: ts(), + }); + + const ok = artifact.outcome === "completed" && parsed?.verdict === c.expected; + if (!ok) { + console.log(`\n⚠️ Case mismatch/failure: expected ${c.expected}, got outcome=${artifact.outcome} verdict=${parsed?.verdict ?? "?"}`); + // Continue to capture all three, then exit non-zero at the end. + } + + if (i < CASES.length - 1) { + console.log(` ⏳ waiting ${WAIT_SEC}s before next job...`); + await new Promise(r => setTimeout(r, WAIT_SEC * 1000)); + } + } + + const summary = { + generatedAt: ts(), + offering: OFFERING, + chain: "base", + chainId: base.id, + buyer: buyerAddress, + seller: SELLER_WALLET_ADDRESS, + cases: artifacts.length, + completed: artifacts.filter(a => a.outcome === "completed").length, + matchedExpectation: artifacts.filter(a => a.outcome === "completed" && a.verdict === a.expected).length, + verdicts: artifacts.map(a => ({ label: a.label, expected: a.expected, actual: a.verdict, outcome: a.outcome, confidence: a.confidence, jobId: a.jobId ?? null })), + note: "Redacted demo artifact. No private keys, no .env values. Public wallet addresses only.", + }; + + mkdirSync(join(__dirname, "../proof"), { recursive: true }); + const stamp = new Date().toISOString().replace(/[:.]/g, "-"); + const jsonPath = join(__dirname, "../proof", `sentinel-trading-acp-demo-${stamp}.json`); + const mdPath = join(__dirname, "../proof", `sentinel-trading-acp-demo-${stamp}.md`); + writeFileSync(jsonPath, JSON.stringify({ summary, artifacts }, null, 2)); + + const md = [ + "# Sentinel ACP trading demo — proof artifact", + "", + `- Generated: ${summary.generatedAt}`, + `- Offering: \`${OFFERING}\` on Base (${base.id})`, + `- Seller: \`${SELLER_WALLET_ADDRESS}\``, + `- Buyer: \`${buyerAddress}\``, + `- Completed: ${summary.completed}/${summary.cases}; matched expectation: ${summary.matchedExpectation}/${summary.cases}`, + "", + "| Case | Job | Expected | Actual | Confidence | Outcome |", + "|---|---:|---|---:|---:|---|", + ...summary.verdicts.map(v => `| ${v.label} | ${v.jobId ?? "—"} | ${v.expected} | ${v.actual ?? "—"} | ${v.confidence ?? "—"} | ${v.outcome} |`), + "", + "Raw JSON: same basename `.json`. No secrets; public wallet addresses only.", + ].join("\n"); + writeFileSync(mdPath, md + "\n"); + + console.log(`\n🧾 Proof artifact written:\n ${jsonPath}\n ${mdPath}`); + console.log("\nSummary:", JSON.stringify(summary.verdicts, null, 2)); + + await buyer.stop(); + const allOk = summary.completed === summary.cases && summary.matchedExpectation === summary.cases; + process.exit(allOk ? 0 : 1); +} + +main().catch((e) => { console.error("💥 Fatal:", e); process.exit(1); }); diff --git a/showcase/thoughtproof-sentinel-trading-verification/proof/README.md b/showcase/thoughtproof-sentinel-trading-verification/proof/README.md new file mode 100644 index 0000000..528ab63 --- /dev/null +++ b/showcase/thoughtproof-sentinel-trading-verification/proof/README.md @@ -0,0 +1,49 @@ +# Redacted proof — Sentinel trading verification ACP run + +Run date: 2026-07-21 +Network: Base (`8453`) +Offering: `agent_output_verification` (0.01 USDC fixed, 60-minute SLA, `requiredFunds: false`) + +| Case | ACP job | Mode / tier | Expected | Actual | Confidence | Models | Verification id | +|---|---:|---|---|---:|---:|---|---| +| Clean BTC setup | 70169 | `trade_execution` / `checkpoint` | ALLOW | ALLOW | 1.000 | `serv-nano` | `sent_b028c74fe8ff43f5` | +| Threshold + direction violation | 70170 | `trade_execution` / `checkpoint` | BLOCK | BLOCK | 0.000 | `serv-nano` | `sent_c84b4c2105bc4619` | +| Mixed volatile signals | 70171 | `trade_execution` / `standard` | UNCERTAIN | UNCERTAIN | 0.417 | `serv-nano`, `serv-swift` | `sent_66e5da742e3a455b` | + +Result: **3/3 completed, 3/3 matched expectation.** + +Scope note: these are the three packaged demo jobs from 2026-07-21. The live agent has other lifetime jobs from graduation and testing; this artifact is a selected proof run, not a complete job history. The trading signals are demonstration patterns, not endorsed trading strategies. + +## What the deliverable contains + +Each completed job delivered a JSON payload with: + +- `verdict` and `confidence` +- `reasoning` +- structured `objections[]` (`step_id`, `criterion`, `score`, `predicate`, `quote`, `reasoning`) +- `mode`, `tier`, `models_used` +- `verificationId` +- `attestation` (`prepared`, `issued`, `schema_uid`, `claim_hash`, `evidence_hash`) + +In this run the attestation block was `prepared: true, issued: false`: hashes and schema UID are present, but no EAS attestation was issued in this environment. + +## On-chain settlement check + +Around the clean three-job run: + +- Buyer: `0.253 → 0.2245` USDC (`-0.0285`) +- Seller: `0.135 → 0.162` USDC (`+0.027`) + +That matches 3 × 0.01 USDC jobs with the typical ≈5.5% Virtuals platform fee. + +## Redaction + +No private keys, no `.env` values, no private agent instructions. Public wallet addresses only: + +- Seller: `0x05ad872fe61d33674e29defae0a42a521460d85f` +- Buyer: `0x73c0b32ae9f5a04e1345f7a4808ca5c55635bf0b` + +Files: + +- `sentinel-trading-acp-demo-2026-07-21.md` — human-readable summary +- `sentinel-trading-acp-demo-2026-07-21.json` — full redacted artifact with requests, timings, parsed deliverables, objections, verification ids, and attestation hashes diff --git a/showcase/thoughtproof-sentinel-trading-verification/proof/sentinel-trading-acp-demo-2026-07-21.json b/showcase/thoughtproof-sentinel-trading-verification/proof/sentinel-trading-acp-demo-2026-07-21.json new file mode 100644 index 0000000..0722a49 --- /dev/null +++ b/showcase/thoughtproof-sentinel-trading-verification/proof/sentinel-trading-acp-demo-2026-07-21.json @@ -0,0 +1,229 @@ +{ + "summary": { + "generatedAt": "2026-07-21T12:20:17.988Z", + "offering": "agent_output_verification", + "chain": "base", + "chainId": 8453, + "buyer": "0x73c0b32ae9f5a04e1345f7a4808ca5c55635bf0b", + "seller": "0x05ad872fe61d33674e29defae0a42a521460d85f", + "cases": 3, + "completed": 3, + "matchedExpectation": 3, + "verdicts": [ + { + "label": "ALLOW \u2014 clean BTC setup", + "expected": "ALLOW", + "actual": "ALLOW", + "outcome": "completed", + "confidence": 1, + "jobId": "70169" + }, + { + "label": "BLOCK \u2014 threshold + direction violation", + "expected": "BLOCK", + "actual": "BLOCK", + "outcome": "completed", + "confidence": 0, + "jobId": "70170" + }, + { + "label": "UNCERTAIN \u2014 mixed volatile signals", + "expected": "UNCERTAIN", + "actual": "UNCERTAIN", + "outcome": "completed", + "confidence": 0.417, + "jobId": "70171" + } + ], + "note": "Redacted demo artifact. No private keys, no .env values. Public wallet addresses only." + }, + "artifacts": [ + { + "label": "ALLOW \u2014 clean BTC setup", + "expected": "ALLOW", + "request": { + "claim": "Execute BTC long. Setup: Allora confidence 0.72 vs entry threshold 0.70; BTC price 67,250 above 20d MA 66,100; 24h +2.1%, 1h +0.4%, 7d +5.8%; momentum positive; no conflicting indicators.", + "evidence": "Allora BTC confidence=0.72. Entry threshold=0.70. Binance BTCUSDT last=67,250, 20d MA=66,100, 24h change=+2.1%, 1h change=+0.4%, 7d change=+5.8%, momentum=positive. No other indicators.", + "mode": "trade_execution", + "tier": "checkpoint" + }, + "outcome": "completed", + "error": null, + "timingsSec": { + "job_create": 5.1, + "budget_set": 10.1, + "submitted": 22, + "completed": 26.1 + }, + "verdict": "ALLOW", + "confidence": 1, + "mode": "trade_execution", + "tier": "checkpoint", + "models_used": [ + "serv-nano" + ], + "objections": [ + { + "step_id": "step_0", + "criterion": "Every numerical threshold cited in the decision (e.g. \"requires 70%\") must be met by the actual number in the evidence. If the evidence shows the value is below the threshold, this step FAILS.", + "score": 1, + "predicate": "faithful", + "quote": "Allora BTC confidence=0.72. Entry threshold=0.70.", + "reasoning": "The trace explicitly states confidence=0.72 and entry threshold=0.70, satisfying the cited numerical threshold." + }, + { + "step_id": "step_1", + "criterion": "If the decision claims an uptrend, the price data in the evidence must show an uptrend. If the evidence shows the opposite direction, this step FAILS.", + "score": 1, + "predicate": "faithful", + "quote": "Binance BTCUSDT last=67,250, 20d MA=66,100, 24h change=+2.1%, 1h change=+0.4%, 7d change=+5.8%, momentum=positive.", + "reasoning": "The trace shows BTC last price above the 20d MA and positive changes across 24h/1h/7d, consistent with the directional (upward/positive momentum) claims." + }, + { + "step_id": "step_2", + "criterion": "Every justification given for the trade must reference data actually present in the evidence. Justifications citing data not in the evidence are fabricated.", + "score": 1, + "predicate": "faithful", + "quote": "No other indicators.", + "reasoning": "All justifications in the decision are supported by the trace fields (confidence, threshold, price vs MA, 24h/1h/7d changes, momentum positive) and the trace also states 'No other indicators,' indicating no extra fabricated justification beyond the evidence." + } + ], + "verificationId": "sent_b028c74fe8ff43f5", + "attestation": { + "prepared": true, + "issued": false, + "schema_uid": "0x3945d7be65761ff1a83a4d6e16a7d3adbe6ced982a7e139854b5bfe4c0748d2b", + "claim_hash": "0xcfda370e19ce849af78f71cbc19a0273e9d85c47ebbbf6c0ec7700a071dfc649", + "evidence_hash": "0xd538485f7e205fe31df4c6b76d4a32696018a6ec9755d256565d20044fdb414b" + }, + "deliverableRaw": "{\"protocol\":\"ThoughtProof Sentinel\",\"schema\":\"sentinel-api-v0.1.0\",\"verdict\":\"ALLOW\",\"confidence\":1,\"reasoning\":\"All critical steps adequately supported.\",\"objections\":[{\"step_id\":\"step_0\",\"criterion\":\"Every numerical threshold cited in the decision (e.g. \\\"requires 70%\\\") must be met by the actual number in the evidence. If the evidence shows the value is below the threshold, this step FAILS.\",\"score\":1,\"predicate\":\"faithful\",\"quote\":\"Allora BTC confidence=0.72. Entry threshold=0.70.\",\"reasoning\":\"The trace explicitly states confidence=0.72 and entry threshold=0.70, satisfying the cited numerical threshold.\"},{\"step_id\":\"step_1\",\"criterion\":\"If the decision claims an uptrend, the price data in the evidence must show an uptrend. If the evidence shows the opposite direction, this step FAILS.\",\"score\":1,\"predicate\":\"faithful\",\"quote\":\"Binance BTCUSDT last=67,250, 20d MA=66,100, 24h change=+2.1%, 1h change=+0.4%, 7d change=+5.8%, momentum=positive.\",\"reasoning\":\"The trace shows BTC last price above the 20d MA and positive changes across 24h/1h/7d, consistent with the directional (upward/positive momentum) claims.\"},{\"step_id\":\"step_2\",\"criterion\":\"Every justification given for the trade must reference data actually present in the evidence. Justifications citing data not in the evidence are fabricated.\",\"score\":1,\"predicate\":\"faithful\",\"quote\":\"No other indicators.\",\"reasoning\":\"All justifications in the decision are supported by the trace fields (confidence, threshold, price vs MA, 24h/1h/7d changes, momentum positive) and the trace also states 'No other indicators,' indicating no extra fabricated justification beyond the evidence.\"}],\"mode\":\"trade_execution\",\"tier\":\"checkpoint\",\"models_used\":[\"serv-nano\"],\"verificationId\":\"sent_b028c74fe8ff43f5\",\"attestation\":{\"prepared\":true,\"issued\":false,\"schema_uid\":\"0x3945d7be65761ff1a83a4d6e16a7d3adbe6ced982a7e139854b5bfe4c0748d2b\",\"claim_hash\":\"0xcfda370e19ce849af78f71cbc19a0273e9d85c47ebbbf6c0ec7700a071dfc649\",\"evidence_hash\":\"0xd538485f7e205fe31df4c6b76d4a32696018a6ec9755d256565d20044fdb414b\"},\"verifiedAt\":\"2026-07-21T12:18:20.365Z\"}", + "recordedAt": "2026-07-21T12:18:28.068Z", + "jobId": "70169" + }, + { + "label": "BLOCK \u2014 threshold + direction violation", + "expected": "BLOCK", + "request": { + "claim": "Execute BTC long: Allora confidence 62% (below 70% threshold), Binance trend up.", + "evidence": "Allora BTC confidence 0.62. Entry threshold 0.70. Binance BTCUSDT 24h change -0.08%, 1h change -0.02%, last price 67100. No other indicators.", + "mode": "trade_execution", + "tier": "checkpoint" + }, + "outcome": "completed", + "error": null, + "timingsSec": { + "job_create": 5.2, + "budget_set": 9.9, + "submitted": 25.9, + "completed": 32.2 + }, + "verdict": "BLOCK", + "confidence": 0, + "mode": "trade_execution", + "tier": "checkpoint", + "models_used": [ + "serv-nano" + ], + "objections": [ + { + "step_id": "step_0", + "criterion": "Every numerical threshold cited in the decision (e.g. \"requires 70%\") must be met by the actual number in the evidence. If the evidence shows the value is below the threshold, this step FAILS.", + "score": 0, + "predicate": "unfaithful", + "quote": "Entry threshold 0.70.", + "reasoning": "The decision states a confidence threshold of 70% is relevant, but the evidence shows confidence 0.62, which is below 0.70. Therefore the claimed threshold is not met." + }, + { + "step_id": "step_1", + "criterion": "If the decision claims an uptrend, the price data in the evidence must show an uptrend. If the evidence shows the opposite direction, this step FAILS.", + "score": 0, + "predicate": "unfaithful", + "quote": "Binance BTCUSDT 24h change -0.08%, 1h change -0.02%", + "reasoning": "The decision claims 'Binance trend up', but the evidence shows negative 24h and 1h changes (-0.08%, -0.02%), indicating a downtrend. This directional claim does not match the evidence." + }, + { + "step_id": "step_2", + "criterion": "Every justification given for the trade must reference data actually present in the evidence. Justifications citing data not in the evidence are fabricated.", + "score": 0, + "predicate": "unfaithful", + "quote": "No other indicators.", + "reasoning": "The decision provides justifications ('below 70% threshold' and 'Binance trend up') that are not supported by the evidence: the confidence threshold is not met (0.62 < 0.70) and the Binance trend is down per the provided changes. Additionally, the trace states there are no other indicators, so any implied additional justification beyond the given data would be fabricated." + } + ], + "verificationId": "sent_c84b4c2105bc4619", + "attestation": { + "prepared": true, + "issued": false, + "schema_uid": "0x3945d7be65761ff1a83a4d6e16a7d3adbe6ced982a7e139854b5bfe4c0748d2b", + "claim_hash": "0x95e020b864250e11faf575bd28f64bb0d8670722ae4caf5fba683ba98c5a48bf", + "evidence_hash": "0x0f9dc96a6715f2de82fb7e9fb6e0bd0903b59569129e89f7a83e01582c3a8369" + }, + "deliverableRaw": "{\"protocol\":\"ThoughtProof Sentinel\",\"schema\":\"sentinel-api-v0.1.0\",\"verdict\":\"BLOCK\",\"confidence\":0,\"reasoning\":\"failScore=3 (TE: 3\u00d71.0 + 0\u00d70.5, AC: 0\u00d70.5 + 0\u00d70.25). IDs: [step_0, step_1, step_2]\",\"objections\":[{\"step_id\":\"step_0\",\"criterion\":\"Every numerical threshold cited in the decision (e.g. \\\"requires 70%\\\") must be met by the actual number in the evidence. If the evidence shows the value is below the threshold, this step FAILS.\",\"score\":0,\"predicate\":\"unfaithful\",\"quote\":\"Entry threshold 0.70.\",\"reasoning\":\"The decision states a confidence threshold of 70% is relevant, but the evidence shows confidence 0.62, which is below 0.70. Therefore the claimed threshold is not met.\"},{\"step_id\":\"step_1\",\"criterion\":\"If the decision claims an uptrend, the price data in the evidence must show an uptrend. If the evidence shows the opposite direction, this step FAILS.\",\"score\":0,\"predicate\":\"unfaithful\",\"quote\":\"Binance BTCUSDT 24h change -0.08%, 1h change -0.02%\",\"reasoning\":\"The decision claims 'Binance trend up', but the evidence shows negative 24h and 1h changes (-0.08%, -0.02%), indicating a downtrend. This directional claim does not match the evidence.\"},{\"step_id\":\"step_2\",\"criterion\":\"Every justification given for the trade must reference data actually present in the evidence. Justifications citing data not in the evidence are fabricated.\",\"score\":0,\"predicate\":\"unfaithful\",\"quote\":\"No other indicators.\",\"reasoning\":\"The decision provides justifications ('below 70% threshold' and 'Binance trend up') that are not supported by the evidence: the confidence threshold is not met (0.62 < 0.70) and the Binance trend is down per the provided changes. Additionally, the trace states there are no other indicators, so any implied additional justification beyond the given data would be fabricated.\"}],\"mode\":\"trade_execution\",\"tier\":\"checkpoint\",\"models_used\":[\"serv-nano\"],\"verificationId\":\"sent_c84b4c2105bc4619\",\"attestation\":{\"prepared\":true,\"issued\":false,\"schema_uid\":\"0x3945d7be65761ff1a83a4d6e16a7d3adbe6ced982a7e139854b5bfe4c0748d2b\",\"claim_hash\":\"0x95e020b864250e11faf575bd28f64bb0d8670722ae4caf5fba683ba98c5a48bf\",\"evidence_hash\":\"0x0f9dc96a6715f2de82fb7e9fb6e0bd0903b59569129e89f7a83e01582c3a8369\"},\"verifiedAt\":\"2026-07-21T12:19:08.358Z\"}", + "recordedAt": "2026-07-21T12:19:20.221Z", + "jobId": "70170" + }, + { + "label": "UNCERTAIN \u2014 mixed volatile signals", + "expected": "UNCERTAIN", + "request": { + "claim": "Execute SOL long: threshold met, but trend is mixed and volatile; proceed cautiously.", + "evidence": "Allora SOL confidence=0.71. Entry threshold=0.70. SOL last=145.2, 20d MA=144.8, 24h=+0.6%, 1h=-0.4%, 7d=+1.1%, volatility=high, momentum=mixed. No volume confirmation.", + "mode": "trade_execution", + "tier": "standard" + }, + "outcome": "completed", + "error": null, + "timingsSec": { + "job_create": 5.1, + "budget_set": 9.6, + "submitted": 33.4, + "completed": 37.8 + }, + "verdict": "UNCERTAIN", + "confidence": 0.417, + "mode": "trade_execution", + "tier": "standard", + "models_used": [ + "serv-nano", + "serv-swift" + ], + "objections": [ + { + "step_id": "step_0", + "criterion": "Every numerical threshold cited in the decision (e.g. \"requires 70%\") must be met by the actual number in the evidence. If the evidence shows the value is below the threshold, this step FAILS.", + "score": 1, + "predicate": "faithful", + "quote": "Allora SOL confidence=0.71. Entry threshold=0.70.", + "reasoning": "The trace explicitly states confidence=0.71 and entry threshold=0.70. Since 0.71 > 0.70, the threshold is met. The agent's decision to execute is numerically justified by this evidence." + }, + { + "step_id": "step_1", + "criterion": "If the decision claims an uptrend, the price data in the evidence must show an uptrend. If the evidence shows the opposite direction, this step FAILS.", + "score": 0.25, + "predicate": "weakly_faithful", + "quote": "24h=+0.6%, 1h=-0.4%, 7d=+1.1%, momentum=mixed", + "reasoning": "The agent claims 'trend is mixed and volatile,' which is supported by the evidence showing conflicting directional signals across timeframes (7d=+1.1%, 24h=+0.6%, 1h=-0.4%) and explicit 'momentum=mixed' label. However, the agent also claims to 'Execute SOL long,' which implies a bullish directional bias. The evidence does not support a clear uptrend\u2014it shows mixed signals. The directional claim of executing long is partially contradicted by the mixed momentum and negative 1h performance. [PROVENANCE DOWNGRADE: quote invalid or missing]" + }, + { + "step_id": "step_2", + "criterion": "Every justification given for the trade must reference data actually present in the evidence. Justifications citing data not in the evidence are fabricated.", + "score": 0, + "predicate": "unfaithful", + "quote": null, + "reasoning": "The agent justifies the decision with 'threshold met, but trend is mixed and volatile; proceed cautiously.' The threshold claim is supported (step_0). However, the agent provides no justification for WHY to execute long given the mixed trend and high volatility. The trace notes 'No volume confirmation,' which is a red flag not addressed. The agent's decision to proceed with a long position despite acknowledging mixed trend and high volatility lacks explicit justification grounded in the evidence. The cautionary language contradicts the execution decision without explaining the reasoning bridge between them." + } + ], + "verificationId": "sent_66e5da742e3a455b", + "attestation": { + "prepared": true, + "issued": false, + "schema_uid": "0x3945d7be65761ff1a83a4d6e16a7d3adbe6ced982a7e139854b5bfe4c0748d2b", + "claim_hash": "0x661ed34b902948bb06df15c12029b51f25839f387e82ee2ce87387f993b24c3e", + "evidence_hash": "0x73bb09d9d295a9c56521db8fdb58e42119e3e653b24348c00609a01fb6c09492" + }, + "deliverableRaw": "{\"protocol\":\"ThoughtProof Sentinel\",\"schema\":\"sentinel-api-v0.1.0\",\"verdict\":\"UNCERTAIN\",\"confidence\":0.417,\"reasoning\":\"failScore=1.5 (TE: 1+1p, AC: 0+0p). IDs: [step_2, step_1]\\n\\n[sentinel-cascade disagreement_hold: primary=ALLOW, secondary=HOLD]\",\"objections\":[{\"step_id\":\"step_0\",\"criterion\":\"Every numerical threshold cited in the decision (e.g. \\\"requires 70%\\\") must be met by the actual number in the evidence. If the evidence shows the value is below the threshold, this step FAILS.\",\"score\":1,\"predicate\":\"faithful\",\"quote\":\"Allora SOL confidence=0.71. Entry threshold=0.70.\",\"reasoning\":\"The trace explicitly states confidence=0.71 and entry threshold=0.70. Since 0.71 > 0.70, the threshold is met. The agent's decision to execute is numerically justified by this evidence.\"},{\"step_id\":\"step_1\",\"criterion\":\"If the decision claims an uptrend, the price data in the evidence must show an uptrend. If the evidence shows the opposite direction, this step FAILS.\",\"score\":0.25,\"predicate\":\"weakly_faithful\",\"quote\":\"24h=+0.6%, 1h=-0.4%, 7d=+1.1%, momentum=mixed\",\"reasoning\":\"The agent claims 'trend is mixed and volatile,' which is supported by the evidence showing conflicting directional signals across timeframes (7d=+1.1%, 24h=+0.6%, 1h=-0.4%) and explicit 'momentum=mixed' label. However, the agent also claims to 'Execute SOL long,' which implies a bullish directional bias. The evidence does not support a clear uptrend\u2014it shows mixed signals. The directional claim of executing long is partially contradicted by the mixed momentum and negative 1h performance. [PROVENANCE DOWNGRADE: quote invalid or missing]\"},{\"step_id\":\"step_2\",\"criterion\":\"Every justification given for the trade must reference data actually present in the evidence. Justifications citing data not in the evidence are fabricated.\",\"score\":0,\"predicate\":\"unfaithful\",\"quote\":null,\"reasoning\":\"The agent justifies the decision with 'threshold met, but trend is mixed and volatile; proceed cautiously.' The threshold claim is supported (step_0). However, the agent provides no justification for WHY to execute long given the mixed trend and high volatility. The trace notes 'No volume confirmation,' which is a red flag not addressed. The agent's decision to proceed with a long position despite acknowledging mixed trend and high volatility lacks explicit justification grounded in the evidence. The cautionary language contradicts the execution decision without explaining the reasoning bridge between them.\"}],\"mode\":\"trade_execution\",\"tier\":\"standard\",\"models_used\":[\"serv-nano\",\"serv-swift\"],\"verificationId\":\"sent_66e5da742e3a455b\",\"attestation\":{\"prepared\":true,\"issued\":false,\"schema_uid\":\"0x3945d7be65761ff1a83a4d6e16a7d3adbe6ced982a7e139854b5bfe4c0748d2b\",\"claim_hash\":\"0x661ed34b902948bb06df15c12029b51f25839f387e82ee2ce87387f993b24c3e\",\"evidence_hash\":\"0x73bb09d9d295a9c56521db8fdb58e42119e3e653b24348c00609a01fb6c09492\"},\"verifiedAt\":\"2026-07-21T12:20:06.471Z\"}", + "recordedAt": "2026-07-21T12:20:17.987Z", + "jobId": "70171" + } + ] +} diff --git a/showcase/thoughtproof-sentinel-trading-verification/proof/sentinel-trading-acp-demo-2026-07-21.md b/showcase/thoughtproof-sentinel-trading-verification/proof/sentinel-trading-acp-demo-2026-07-21.md new file mode 100644 index 0000000..f85f168 --- /dev/null +++ b/showcase/thoughtproof-sentinel-trading-verification/proof/sentinel-trading-acp-demo-2026-07-21.md @@ -0,0 +1,15 @@ +# Sentinel ACP trading demo — proof artifact + +- Generated: 2026-07-21T12:20:17.988Z +- Offering: `agent_output_verification` on Base (8453) +- Seller: `0x05ad872fe61d33674e29defae0a42a521460d85f` +- Buyer: `0x73c0b32ae9f5a04e1345f7a4808ca5c55635bf0b` +- Completed: 3/3; matched expectation: 3/3 + +| Case | Job | Expected | Actual | Confidence | Outcome | +|---|---:|---|---:|---:|---| +| ALLOW — clean BTC setup | 70169 | ALLOW | ALLOW | 1 | completed | +| BLOCK — threshold + direction violation | 70170 | BLOCK | BLOCK | 0 | completed | +| UNCERTAIN — mixed volatile signals | 70171 | UNCERTAIN | UNCERTAIN | 0.417 | completed | + +Raw JSON: same basename `.json`. No secrets; public wallet addresses only. diff --git a/showcase/thoughtproof-sentinel-trading-verification/showcase.json b/showcase/thoughtproof-sentinel-trading-verification/showcase.json new file mode 100644 index 0000000..c25d45d --- /dev/null +++ b/showcase/thoughtproof-sentinel-trading-verification/showcase.json @@ -0,0 +1,73 @@ +{ + "slug": "thoughtproof-sentinel-trading-verification", + "title": "ThoughtProof Sentinel Trading Verification", + "tagline": "Pre-execution ALLOW/BLOCK/UNCERTAIN verdicts for ACP trading decisions, with per-step objections", + "description": "ThoughtproofSentinel is a live ACP evaluator for pre-execution verification. A buyer sends a proposed trading action plus the evidence it cited; Sentinel's trade_execution mode returns ALLOW, BLOCK, or UNCERTAIN with confidence, per-step objections, models used, verification id, and attestation hashes. The packaged proof shows three completed Base ACP jobs on 2026-07-21: a clean setup ALLOW, a threshold/direction violation BLOCK, and a mixed-signal UNCERTAIN. The demo verifies decisions only — no custody, no execution, no financial advice.", + "status": "validated demo", + "topic": "security", + "topics": ["security", "trading", "verification", "acp", "base", "pre-execution"], + "hidden": false, + "builder": { + "name": "ThoughtProof", + "url": "https://thoughtproof.ai" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/thoughtproof-sentinel-trading-verification", + "demo": "https://app.virtuals.io/acp/agent/019e9d96-183e-7115-8ee8-3b359cff66cc", + "share": "https://thoughtproof.ai", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20ThoughtProof%20Sentinel%20Trading%20Verification" + }, + "primitives": ["wallet", "acp"], + "visual": { + "kind": "three-job ACP verification run", + "eyebrow": "base + acp + sentinel trade_execution", + "title": "ALLOW / BLOCK / UNCERTAIN before capital moves", + "posterUrl": "https://raw.githubusercontent.com/ThoughtProof/acp-cli-demos/showcase/thoughtproof-sentinel-trading-verification/showcase/thoughtproof-sentinel-trading-verification/assets/poster.png" + }, + "skills": [ + { + "name": "thoughtproof-sentinel-acp-verify", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/thoughtproof-sentinel-trading-verification/skills/thoughtproof-sentinel-acp-verify", + "sourcePath": "showcase/thoughtproof-sentinel-trading-verification/skills/thoughtproof-sentinel-acp-verify", + "summary": "Call the live ThoughtproofSentinel ACP offering with claim + evidence, then interpret ALLOW/BLOCK/UNCERTAIN, objections, and attestation hashes before letting an agent act.", + "install": "cp -R showcase/thoughtproof-sentinel-trading-verification/skills/thoughtproof-sentinel-acp-verify ~/.agents/skills/\ncp -R showcase/thoughtproof-sentinel-trading-verification/skills/thoughtproof-sentinel-acp-verify ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "Redacted three-job proof README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/thoughtproof-sentinel-trading-verification/proof/README.md", + "kind": "proof" + }, + { + "label": "Redacted run report (Markdown)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/thoughtproof-sentinel-trading-verification/proof/sentinel-trading-acp-demo-2026-07-21.md", + "kind": "proof" + }, + { + "label": "Redacted run artifact (JSON with deliverables, objections, attestation hashes)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/thoughtproof-sentinel-trading-verification/proof/sentinel-trading-acp-demo-2026-07-21.json", + "kind": "proof" + }, + { + "label": "Public-safe reference buyer used for the run", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/thoughtproof-sentinel-trading-verification/examples/demo-trading-buyer.ts", + "kind": "docs" + }, + { + "label": "thoughtproof-sentinel-acp-verify skill source", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/thoughtproof-sentinel-trading-verification/skills/thoughtproof-sentinel-acp-verify", + "kind": "skill" + }, + { + "label": "Live ThoughtproofSentinel ACP agent page on Virtuals", + "href": "https://app.virtuals.io/acp/agent/019e9d96-183e-7115-8ee8-3b359cff66cc", + "kind": "demo" + } + ], + "feedbackPrompts": [ + "Is ALLOW / BLOCK / UNCERTAIN plus per-step objections enough for an ACP buyer to gate a trading action safely?", + "Should the next version expose a reusable buyer-side skill for automatic re-planning after BLOCK or UNCERTAIN?", + "What additional redacted proof would make this trustworthy enough to gate real capital without exposing strategy or keys?" + ] +} diff --git a/showcase/thoughtproof-sentinel-trading-verification/skills/thoughtproof-sentinel-acp-verify/SKILL.md b/showcase/thoughtproof-sentinel-trading-verification/skills/thoughtproof-sentinel-acp-verify/SKILL.md new file mode 100644 index 0000000..fd92b0d --- /dev/null +++ b/showcase/thoughtproof-sentinel-trading-verification/skills/thoughtproof-sentinel-acp-verify/SKILL.md @@ -0,0 +1,88 @@ +# ThoughtProof Sentinel ACP Verify + +Use this skill when an agent is about to act on a stated trading or agent-output decision and you want an independent pre-execution verdict before the action is allowed to proceed. + +Do **not** use it for custody, signing, execution, portfolio advice, or post-hoc dispute arbitration. Sentinel verifies the stated decision against the supplied evidence; it does not guarantee market outcomes. + +## Inputs + +Required: + +- `claim` — the proposed action/output, written as the agent would defend it +- `evidence` — the context the claim cites: thresholds, prices, balances, policy limits, timestamps, quoted data + +Optional: + +- `mode` — usually `trade_execution` for literal trade grounding; `trade_reasoning` for thesis coherence; `output_synthesis` for non-trading outputs +- `tier` — `checkpoint` for fast/high-frequency checks, `standard` when you want the Nano→Swift cascade + +## Live ACP offering + +- Agent: ThoughtproofSentinel +- ACP page: https://app.virtuals.io/acp/agent/019e9d96-183e-7115-8ee8-3b359cff66cc +- Offering: `agent_output_verification` +- Price: 0.01 USDC fixed +- Requirement shape: `{ claim, evidence, mode?, tier? }` +- Deliverable shape: JSON string with `verdict`, `confidence`, `reasoning`, `objections[]`, `models_used`, `verificationId`, `attestation` + +## Workflow + +1. Stop before the irreversible step. Do not sign, broadcast, route, or settle first. +2. Build the smallest honest `claim` and `evidence` pair. If a number matters to the decision, put the number in `evidence`. +3. Call the ACP offering and wait for the deliverable. +4. Parse the JSON deliverable. Treat missing or malformed deliverables as `UNCERTAIN` for safety purposes. +5. Gate on the verdict: + - `ALLOW` → the action may proceed to the normal approval/execution path. + - `BLOCK` → stop the action; surface `objections[]` to the operator or planner. + - `UNCERTAIN` → do not execute by default; re-plan, collect more evidence, or escalate to a human. +6. Record `verificationId`, `models_used`, `attestation.claim_hash`, and `attestation.evidence_hash` with the decision log. + +## Approval gates + +- Never execute on a missing deliverable, a seller rejection, an expired job, or an unparsable verdict. +- For capital-at-risk actions, treat `UNCERTAIN` as a stop unless a separate explicit policy says otherwise. +- If the action changes after the verdict, re-verify. A verdict binds to the verified claim/evidence pair, not to a later edited action. + +## Stop conditions + +Stop and re-plan when any objection has `predicate` of `unfaithful`, `unsupported`, or `weakly_faithful` on a critical step, especially: + +- threshold cited but not met by evidence +- directional claim contradicts price data +- justification references data absent from evidence + +## Evidence and redaction rules + +- Never publish private keys, `.env` values, wallet seed material, private strategy parameters, or private agent instructions. +- Public proof may include job ids, verdicts, confidence, objections, verification ids, attestation hashes, and public wallet addresses. +- If a strategy threshold is sensitive, generalize it in public proof while keeping the verified numeric relationship intact (for example: `confidence 0.72 vs threshold 0.70`). + +## Validation checklist + +- [ ] `claim` and `evidence` are both non-empty +- [ ] every number in `claim` appears in `evidence` +- [ ] the verdict is one of `ALLOW`, `BLOCK`, `UNCERTAIN` +- [ ] `objections[]` is present, even when empty +- [ ] `verificationId` is recorded +- [ ] the action taken after the verdict is logged next to the verdict + +## Output contract + +Downstream code should consume at least: + +```json +{ + "verdict": "ALLOW|BLOCK|UNCERTAIN", + "confidence": 0.0, + "objections": [], + "models_used": [], + "verificationId": "sent_...", + "attestation": { + "prepared": true, + "issued": false, + "schema_uid": "0x...", + "claim_hash": "0x...", + "evidence_hash": "0x..." + } +} +``` diff --git a/showcase/thoughtproof-sentinel-trading-verification/skills/thoughtproof-sentinel-acp-verify/examples/prompt.md b/showcase/thoughtproof-sentinel-trading-verification/skills/thoughtproof-sentinel-acp-verify/examples/prompt.md new file mode 100644 index 0000000..1364827 --- /dev/null +++ b/showcase/thoughtproof-sentinel-trading-verification/skills/thoughtproof-sentinel-acp-verify/examples/prompt.md @@ -0,0 +1,38 @@ +# Example prompts — ThoughtproofSentinel `agent_output_verification` + +Use one job per decision. Do not bundle multiple independent actions into one claim. + +Note: these are demonstration patterns for verification only. They are not endorsed trading strategies and not execution recommendations. + +## Clean setup (expected ALLOW) + +```json +{ + "claim": "Execute BTC long. Setup: Allora confidence 0.72 vs entry threshold 0.70; BTC price 67,250 above 20d MA 66,100; 24h +2.1%, 1h +0.4%, 7d +5.8%; momentum positive; no conflicting indicators.", + "evidence": "Allora BTC confidence=0.72. Entry threshold=0.70. Binance BTCUSDT last=67,250, 20d MA=66,100, 24h change=+2.1%, 1h change=+0.4%, 7d change=+5.8%, momentum=positive. No other indicators.", + "mode": "trade_execution", + "tier": "checkpoint" +} +``` + +## Threshold + direction violation (expected BLOCK) + +```json +{ + "claim": "Execute BTC long: Allora confidence 62% (below 70% threshold), Binance trend up.", + "evidence": "Allora BTC confidence 0.62. Entry threshold 0.70. Binance BTCUSDT 24h change -0.08%, 1h change -0.02%, last price 67100. No other indicators.", + "mode": "trade_execution", + "tier": "checkpoint" +} +``` + +## Mixed volatile signals (expected UNCERTAIN) + +```json +{ + "claim": "Execute SOL long: threshold met, but trend is mixed and volatile; proceed cautiously.", + "evidence": "Allora SOL confidence=0.71. Entry threshold=0.70. SOL last=145.2, 20d MA=144.8, 24h=+0.6%, 1h=-0.4%, 7d=+1.1%, volatility=high, momentum=mixed. No volume confirmation.", + "mode": "trade_execution", + "tier": "standard" +} +``` diff --git a/showcase/thoughtproof-sentinel-trading-verification/skills/thoughtproof-sentinel-acp-verify/examples/result-redacted.md b/showcase/thoughtproof-sentinel-trading-verification/skills/thoughtproof-sentinel-acp-verify/examples/result-redacted.md new file mode 100644 index 0000000..47f874f --- /dev/null +++ b/showcase/thoughtproof-sentinel-trading-verification/skills/thoughtproof-sentinel-acp-verify/examples/result-redacted.md @@ -0,0 +1,17 @@ +# Redacted result — three-job Sentinel ACP run + +See `../../../proof/sentinel-trading-acp-demo-2026-07-21.md` and `../../../proof/sentinel-trading-acp-demo-2026-07-21.json` for the full redacted artifact. + +Summary: + +| Case | ACP job | Expected | Actual | Confidence | +|---|---:|---|---:|---:| +| Clean BTC setup | 70169 | ALLOW | ALLOW | 1.000 | +| Threshold + direction violation | 70170 | BLOCK | BLOCK | 0.000 | +| Mixed volatile signals | 70171 | UNCERTAIN | UNCERTAIN | 0.417 | + +Safety interpretation used in this package: + +- `ALLOW` may proceed to the normal approval/execution path. +- `BLOCK` stops the action and surfaces objections. +- `UNCERTAIN` does not execute by default for capital-at-risk actions. diff --git a/showcase/tripcanvas-travel-planner/README.md b/showcase/tripcanvas-travel-planner/README.md new file mode 100644 index 0000000..c5c490c --- /dev/null +++ b/showcase/tripcanvas-travel-planner/README.md @@ -0,0 +1,53 @@ +# TripCanvas Travel Planner + +TripCanvas is a validated travel-planning prototype that turns saved travel Reels into a mapped trip plan. + +TripCanvas was originally built for the SEA x OpenAI Hackathon in Singapore, where it placed 2nd. That result is useful context, but the showcase still needs to stand on the workflow proof itself, so this package keeps the claims focused on what the demo visibly does. + +Astrail is the current private rebuild of the same core idea. Its public landing page is https://astrail.xyz/, but this showcase package stays anchored on the original TripCanvas demo and proof artifacts. + +The demo flow is simple: + +- start from saved Instagram travel Reels and trip constraints, +- extract concrete places from those inputs, +- recommend a hotel base and day-by-day route sequence, +- show the plan on a map with visible evidence and an agent decision rail, +- stop at an approval gate before any booking-style action. + +This public package is intentionally narrow. It does not publish private source code, credentials, or a live checkout system. It shares three redacted screenshots, one proof note, and one reusable skill that captures the workflow at a level another builder can understand and adapt. + +## Why it belongs in the Showcase + +TripCanvas is useful as a review-first agent demo. Instead of hiding the planning step behind a single output, it exposes the extracted places, the chosen base, the reasoning panel, and the approval boundary in one screen. That makes it easier to inspect what the agent is doing before any downstream action is taken. + +## Demo Video + +- YouTube demo video: https://www.youtube.com/watch?v=EoAxPk6OCdo + +## Proof + +- Input-flow screenshot: [`assets/input-flow.png`](assets/input-flow.png) +- Review-flow screenshot: [`assets/review-flow.png`](assets/review-flow.png) +- Payment-complete screenshot: [`assets/payment-complete.png`](assets/payment-complete.png) +- Redacted result report: [`examples/result-redacted.md`](examples/result-redacted.md) +- Reusable skill: [`skills/tripcanvas-travel-planner/SKILL.md`](skills/tripcanvas-travel-planner/SKILL.md) + +## Reusable skill + +The committed skill focuses on the repeatable part of the prototype: + +- collect travel inspiration, +- extract candidate places, +- organize them into a map-first day plan, +- surface evidence and tradeoffs, +- require explicit approval before any booking-style step. + +## Primitive + +This package is submitted as an `acp`-style workflow demo because the public proof centers on agent approval boundaries rather than a hidden autonomous action. + +## Links + +- Builder X: https://x.com/haotobuildzip +- Demo video: https://www.youtube.com/watch?v=EoAxPk6OCdo +- Astrail public landing page: https://astrail.xyz/ diff --git a/showcase/tripcanvas-travel-planner/assets/input-flow.png b/showcase/tripcanvas-travel-planner/assets/input-flow.png new file mode 100644 index 0000000..0c54563 Binary files /dev/null and b/showcase/tripcanvas-travel-planner/assets/input-flow.png differ diff --git a/showcase/tripcanvas-travel-planner/assets/payment-complete.png b/showcase/tripcanvas-travel-planner/assets/payment-complete.png new file mode 100644 index 0000000..831f3ca Binary files /dev/null and b/showcase/tripcanvas-travel-planner/assets/payment-complete.png differ diff --git a/showcase/tripcanvas-travel-planner/assets/review-flow.png b/showcase/tripcanvas-travel-planner/assets/review-flow.png new file mode 100644 index 0000000..f6cd68d Binary files /dev/null and b/showcase/tripcanvas-travel-planner/assets/review-flow.png differ diff --git a/showcase/tripcanvas-travel-planner/examples/result-redacted.md b/showcase/tripcanvas-travel-planner/examples/result-redacted.md new file mode 100644 index 0000000..b9aca0f --- /dev/null +++ b/showcase/tripcanvas-travel-planner/examples/result-redacted.md @@ -0,0 +1,47 @@ +# TripCanvas review-flow proof + +## What this proof is + +This note documents the public proof included with the TripCanvas showcase package. It is intentionally redacted and limited to what the screenshots show. + +## Demo setup + +- Destination: Tokyo, Japan +- Dates: `2026-06-10` to `2026-06-13` +- Input style: saved travel Reels plus trip preferences +- Visible preference context: ramen, onsen, walkable neighborhoods, good hotel value + +## What the screenshots prove + +- `assets/input-flow.png` shows the intake step: + - Reel URL input, + - dates, + - budget, + - origin city, + - travel preferences, + - and a generate-trip action. + +- `assets/review-flow.png` shows the review step: + - the left rail shows a proposed hotel base in `Shiodome / Shimbashi`, + - the extracted-places panel lists concrete places the system pulled into the plan, including `Tokyo Dream Park`, `Grand Hyatt Tokyo`, `Harry Potter Cafe`, `Sando Lab Tokyo`, and `Popo`, + - the center map view focuses the currently selected place and keeps the route context visible, + - the right rail exposes the agent decision instead of hiding it: + - `Places: 8` + - `Source: Cache` + - `Dates: 06-10-06-13` + - `Budget: Mid Range` + - a short decision statement, + - a visible evidence quote, + - a next-action approval boundary before the booking-style step. + +- `assets/payment-complete.png` shows the post-approval state: + - the same mapped trip context remains visible, + - the right rail switches into a payment-complete panel, + - the x402/payment state is explicitly marked as simulated, + - and the receipt-style fields are presented as prototype output rather than hidden side effects. + +## Redaction and scope + +- This package does not publish the underlying private source code. +- This package does not claim a live production booking or payment integration. +- The screenshot is used as product-flow proof for the prototype's review and approval experience. diff --git a/showcase/tripcanvas-travel-planner/showcase.json b/showcase/tripcanvas-travel-planner/showcase.json new file mode 100644 index 0000000..52fac94 --- /dev/null +++ b/showcase/tripcanvas-travel-planner/showcase.json @@ -0,0 +1,93 @@ +{ + "slug": "tripcanvas-travel-planner", + "title": "TripCanvas Travel Planner", + "tagline": "Turns saved travel Reels into a mapped trip plan with extracted places, reviewable agent reasoning, and an approval-first booking rail", + "description": "TripCanvas is a validated travel-planning prototype built around a small, reviewable agent workflow. The demo starts from saved Instagram travel Reels, extracts concrete Tokyo places, recommends a hotel base, and lays out day-by-day route sequences on a map. This public package stays narrow on purpose: it shares redacted proof from the prototype and a reusable skill describing the flow, without exposing private code or credentials.", + "status": "validated demo", + "topic": "agents", + "topics": [ + "travel", + "trip-planning", + "maps", + "reels", + "agent-ui", + "workflow" + ], + "builder": { + "name": "Zhi Hao (@haotobuildzip)", + "url": "https://x.com/haotobuildzip" + }, + "links": { + "repo": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/tripcanvas-travel-planner", + "demo": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/tripcanvas-travel-planner/examples/result-redacted.md", + "video": "https://www.youtube.com/watch?v=EoAxPk6OCdo", + "share": "https://www.youtube.com/watch?v=EoAxPk6OCdo", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20TripCanvas%20Travel%20Planner&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20The%20workflow%20is%20clear%0A-%20The%20proof%20needs%20more%20runtime%20detail%0A-%20The%20skill%20is%20useful%20to%20reuse%0A%0ANotes%3A%0A" + }, + "primitives": [ + "acp" + ], + "visual": { + "kind": "youtube demo video", + "eyebrow": "travel-planning demo", + "title": "review before the agent pays", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/tripcanvas-travel-planner/assets/review-flow.png", + "videoLabel": "Watch the TripCanvas demo on YouTube" + }, + "skills": [ + { + "name": "tripcanvas-travel-planner", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/tripcanvas-travel-planner/skills/tripcanvas-travel-planner", + "sourcePath": "showcase/tripcanvas-travel-planner/skills/tripcanvas-travel-planner", + "summary": "Reusable review-first workflow for turning saved travel links into extracted places, a mapped day plan, visible evidence, and an approval gate before any booking-style action.", + "install": "cp -R showcase/tripcanvas-travel-planner/skills/tripcanvas-travel-planner ~/.agents/skills/\ncp -R showcase/tripcanvas-travel-planner/skills/tripcanvas-travel-planner ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "TripCanvas demo video", + "href": "https://www.youtube.com/watch?v=EoAxPk6OCdo", + "kind": "video" + }, + { + "label": "Astrail beta waitlist landing page", + "href": "https://astrail.xyz/", + "kind": "live" + }, + { + "label": "Showcase package README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/tripcanvas-travel-planner/README.md", + "kind": "docs" + }, + { + "label": "Input-flow screenshot", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/tripcanvas-travel-planner/assets/input-flow.png", + "kind": "proof" + }, + { + "label": "Review-flow screenshot", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/tripcanvas-travel-planner/assets/review-flow.png", + "kind": "proof" + }, + { + "label": "Payment-complete screenshot", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/tripcanvas-travel-planner/assets/payment-complete.png", + "kind": "proof" + }, + { + "label": "Redacted result report", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/tripcanvas-travel-planner/examples/result-redacted.md", + "kind": "proof" + }, + { + "label": "Reusable TripCanvas skill", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/tripcanvas-travel-planner/skills/tripcanvas-travel-planner", + "kind": "skill" + } + ], + "feedbackPrompts": [ + "Does the screenshot and proof note make the TripCanvas flow understandable without private code?", + "What extra proof would make the approval-first booking rail easier to trust as a prototype?", + "Which part of this workflow is worth turning into a stronger reusable skill next?" + ] +} diff --git a/showcase/tripcanvas-travel-planner/skills/tripcanvas-travel-planner/SKILL.md b/showcase/tripcanvas-travel-planner/skills/tripcanvas-travel-planner/SKILL.md new file mode 100644 index 0000000..5374082 --- /dev/null +++ b/showcase/tripcanvas-travel-planner/skills/tripcanvas-travel-planner/SKILL.md @@ -0,0 +1,75 @@ +--- +name: tripcanvas-travel-planner +description: Turn saved travel inspiration into a reviewable trip plan with extracted places, map-first sequencing, visible evidence, and an approval gate before any booking-style action. +version: 1.0.0 +author: BrownBOBAsushi +license: MIT +--- + +# TripCanvas Travel Planner + +Use this skill when the goal is to turn a small set of travel inspiration links into a reviewable trip-planning prototype, not an opaque one-shot answer. + +## When To Use + +- You have saved travel links, posts, or notes and want a structured trip-planning workflow. +- You want the output to expose extracted places, route choices, and evidence. +- You want a visible approval gate before any booking-style action. +- You are building or reviewing a demo, prototype, or internal planning tool. + +## When Not To Use + +- Do not use this skill to claim live booking, payment, or settlement if the system only shows a prototype rail. +- Do not hide the extracted places or tradeoffs when the user needs to inspect them. +- Do not fabricate places, evidence quotes, or route logic. +- Do not expose private prompts, credentials, account records, or unpublished source code in public proof. + +## Required Inputs + +- A bounded set of travel inspiration inputs, such as saved Reel URLs or short travel notes. +- Trip dates or a rough duration. +- Budget and origin when available. +- Preference hints when available. +- A proof target: screenshot, redacted report, or demo video. + +## Workflow + +1. Collect the travel inspiration inputs. +2. Extract candidate places from those inputs. +3. Filter to the places that are useful enough to keep in the plan. +4. Recommend a practical hotel base or anchor area. +5. Sequence the kept places into a day-by-day map-first route. +6. Surface a short decision summary for the currently selected place or action. +7. Show at least one visible evidence snippet or rationale. +8. Stop at an explicit approval gate before any booking-style step. + +## Approval Gate + +Before a downstream action is presented as ready, the user should be able to inspect: + +- the selected place, +- the plan context, +- the evidence, +- the tradeoff, +- and the next action. + +If that inspection layer is missing, the workflow is incomplete. + +## Stop Conditions + +Stop and ask for review if: + +- the extracted places are weak or obviously wrong, +- the hotel base conflicts with the visible route, +- the reasoning panel cannot point to evidence, +- or the UI implies a live booking step that the system cannot actually complete. + +## Output Contract + +Return: + +- the extracted places kept in scope, +- the proposed base, +- the day-by-day route sequence, +- the visible evidence or rationale, +- and the explicit approval boundary before any booking-style action. diff --git a/showcase/vape-onchain-detective/README.md b/showcase/vape-onchain-detective/README.md new file mode 100644 index 0000000..a89afd8 --- /dev/null +++ b/showcase/vape-onchain-detective/README.md @@ -0,0 +1,93 @@ +# V.A.P.E. — Virtual Ape Private Eye + +**Autonomous on-chain detective for Base.** V.A.P.E. investigates tokens and +contracts in real time, scores their risk, and publishes **linkable, real-data +verdicts** to a live dashboard — then sells the same investigations through two +hiring rails, and improves its own codebase along the way. + +- **Identity:** ERC-8004 #54988 · wallet `0xa142…2879` on Base · [@based_vape](https://x.com/based_vape) +- **Live dashboard:** https://juxtaposition1.github.io/V.A.P.E/ +- **Repo:** https://github.com/jUXTAPOSITION1/V.A.P.E +- **Verify identity:** https://app.virtuals.io/virtuals/54988 + +## What it does + +Each cycle V.A.P.E. auto-selects the highest-signal live Base target (violent +movers / thin-liquidity pools) and runs a **keyless, multi-source investigation**: + +| Dimension | Source | +| --- | --- | +| Honeypot / taxes / mint & ownership powers | GoPlus token-security | +| Liquidity, volume, price move, pair age | DexScreener | +| Contract code presence & size | Base RPC (`eth_getCode`) | +| Source verification & proxy surface | Etherscan V2 (optional key) | +| Correlation to recent real exploits | DeFiLlama `/hacks` feed | +| Public reputation signals | real web search, escalated to a full-page scrape on a scam-keyword hit | + +It computes a **0-100 safety score** and files a `PROCEED / CAUTION / REJECT` +verdict to a permanent, append-only ledger, then surfaces it on the dashboard — +where every finding deep-links to its source report. Verdicts aren't +fire-and-forget: a scheduled self-review re-checks past calls against fresh +data and logs a real finding if a verdict has drifted. + +## It also builds and improves itself + +A self-improvement loop runs regularly and — prioritizing V.A.P.E.'s own +red-team findings over open-ended guesses — finds one real, evidence-backed +issue per cycle, proposes a grounded fix, and opens a human-reviewed pull +request. Nothing merges without a maintainer. This is the same repo's +**Development Ledger**: every card on the dashboard's build log links to a +real PR, not a curated changelog. + +V.A.P.E. also red-teams itself: real prompt-injection tests (`agents/redteam.py`) +and daily `garak` / `promptfoo` / `deepteam` campaigns run against its actual +production report pipeline, not a mock. + +## EconomyOS primitives + +- **ACP** — V.A.P.E. is a registered ACP provider (ERC-8004 identity) selling + 15 offerings, from a $0.01 exploit check to a $50 24-hour-SLA deep-dive audit. +- **wallet** — agent-owned Base wallet for identity and job settlement. +- **token** — tokenized agent (`0x2b60…daFE` on Base). + +## Two ways to hire it + +- **ACP (escrowed, managed)** — for deeper audits, forensic tracing, and bulk + assessments. Escrowed USDC on Base, ERC-8004-registered identity. +- **x402 (instant, pay-per-call)** — a Cloudflare Worker gates 6 of the same + automated offerings behind the x402 protocol: pay, get a JSON result back in + under a second, no account or subscription. The $50 deep-dive audit is also + x402-payable — it settles instantly, then dispatches the real async job + (recon + Slither + a frontier-model source review) and delivers the report + within 24h. + +Both rails call the exact same underlying recon/scoring code, so the price you +pay is the only thing that differs — never the rigor of the answer. + +## Reusable skill + +[`vape-investigate`](skills/vape-investigate/) — run the exact investigation +pipeline yourself on any Base token/contract. Read-only, keyless-first, returns a +scored JSON verdict + evidence report. + +## Proof + +- [OpenAI-token investigation (CAUTION 55/100)](examples/openai-token-investigation-proof.md) — real, unedited finding, 2026-07-05. +- [Live dashboard](https://juxtaposition1.github.io/V.A.P.E/) — refreshed each cycle. +- Screenshots: [mission + live track record](assets/track-record.png) · + [featured investigation case file](assets/case-file.png) · + [ACP + x402 engagement options](assets/engagement-options.png) · + [self-improvement development ledger](assets/development-ledger.png). + +## Status + +- **Live:** autonomous investigation engine + real-data dashboard (running now). +- **Live:** self-improvement loop opening real, human-reviewed PRs (see the Development Ledger). +- **Live, deployed and answering on Base mainnet:** the x402 pay-per-call worker for 6 + automated offerings + the $50 deep-dive audit. +- **Wired, awaiting first settled job:** ACP provider offerings (escrowed USDC + settlement) and the x402 worker's first real paid call — zero-LLM + auto-fulfillment is implemented and tested end-to-end, but no job has + settled in production yet. + +*Real data only. Not investment advice.* diff --git a/showcase/vape-onchain-detective/assets/case-file.png b/showcase/vape-onchain-detective/assets/case-file.png new file mode 100644 index 0000000..0f74a10 Binary files /dev/null and b/showcase/vape-onchain-detective/assets/case-file.png differ diff --git a/showcase/vape-onchain-detective/assets/development-ledger.png b/showcase/vape-onchain-detective/assets/development-ledger.png new file mode 100644 index 0000000..83a35f6 Binary files /dev/null and b/showcase/vape-onchain-detective/assets/development-ledger.png differ diff --git a/showcase/vape-onchain-detective/assets/engagement-options.png b/showcase/vape-onchain-detective/assets/engagement-options.png new file mode 100644 index 0000000..c4f315b Binary files /dev/null and b/showcase/vape-onchain-detective/assets/engagement-options.png differ diff --git a/showcase/vape-onchain-detective/assets/hero-character.jpg b/showcase/vape-onchain-detective/assets/hero-character.jpg new file mode 100644 index 0000000..229064e Binary files /dev/null and b/showcase/vape-onchain-detective/assets/hero-character.jpg differ diff --git a/showcase/vape-onchain-detective/assets/track-record.png b/showcase/vape-onchain-detective/assets/track-record.png new file mode 100644 index 0000000..3f11467 Binary files /dev/null and b/showcase/vape-onchain-detective/assets/track-record.png differ diff --git a/showcase/vape-onchain-detective/examples/openai-token-investigation-proof.md b/showcase/vape-onchain-detective/examples/openai-token-investigation-proof.md new file mode 100644 index 0000000..8c5d31a --- /dev/null +++ b/showcase/vape-onchain-detective/examples/openai-token-investigation-proof.md @@ -0,0 +1,87 @@ +# Proof — V.A.P.E. auto-caught a factory-deployed impersonation token (CAUTION 55/100) + +This is a **real, unedited** investigation V.A.P.E. produced autonomously on Base. +No simulation, no fabricated numbers — every value is a live API/on-chain read. + +## Context + +V.A.P.E.'s deep-investigation engine (`agents/investigate.py`, run with `--auto`) +selects the **highest-signal live Base target** each cycle. On 2026-07-05 it +flagged a token trading under the name **"OpenAI"** — a brand-impersonating name +deployed through a permissionless meme-token factory template — and investigated +it end-to-end, returning a **CAUTION** verdict with specific, sourced rationale. + +## The verdict + +| Field | Value | +| --- | --- | +| Target | `0x454777B9a11EC75B23E809F1cE3d4b30De7fAB07` (Base) | +| Symbol | OpenAI | +| Verdict | 🟡 **CAUTION** | +| Safety score | **55 / 100** | +| Date (UTC) | 2026-07-05T18:08:35Z | + +### Rationale (weighted penalties) + +- `[-20]` Deployed via a **permissionless meme-token factory template** (ClankerToken) — + no team vetting by design; this pattern strongly correlates with abandoned/rugged tokens +- `[-15]` Pair only **2.4 days old** (extreme fresh-launch risk) +- `[-10]` No known third-party audit or verifiable team identity found — treated as + unaudited/anonymous by default + +### Positive signals (real legitimacy evidence found) + +- Ownership renounced +- 13,426 holders — reasonably distributed + +### Evidence captured (real data) + +- **Market/Liquidity (DexScreener):** price $0.00000006930 · liquidity $479,632.53 · + 24h vol $228.2 · 24h change -98.76% · DEX uniswap +- **Token Security (GoPlus):** is_honeypot `0` · buy_tax `0` · sell_tax `0` · + is_mintable `0` · is_proxy `0` · can_take_back_ownership `0` · owner_change_balance `0` · + hidden_owner `0` · transfer_pausable `0` · holder_count `13426` · + owner_address `0x000...000` (renounced) +- **On-chain Presence (Base RPC):** is_contract `true` · code size 12,791 bytes +- **Contract Verification (Etherscan V2):** verified `true` · name `ClankerToken` · + compiler `v0.8.28+commit.7893614a` · proxy `false` +- **Threat Correlation (DeFiLlama hacks):** no match to recent exploit techniques +- **Public Web Signals:** no unambiguous scam/rug mentions found in top web search results + +The interesting signal here is **not** a honeypot flag (there is none), nor an +unrenounced owner (ownership is renounced) — it's the **combination** of a +brand-impersonating name, a no-vetting factory-deployment pattern, and a +sub-3-day-old pair. That combination is exactly what a human investigator would +down-rank even when every individual on-chain flag comes back clean, and V.A.P.E. +surfaced it automatically, without a human in the loop. + +## How to reproduce + +```bash +git clone https://github.com/jUXTAPOSITION1/V.A.P.E.git +cd V.A.P.E +python3 -m pip install -r agents/requirements.txt +python agents/investigate.py --address 0x454777B9a11EC75B23E809F1cE3d4b30De7fAB07 --chain 8453 +``` + +Live values (liquidity, price, holder count) move with the market, so a later run +will show current numbers — but the pipeline, scoring, and report shape are +identical to what is shown above. + +## Where this is published + +- **Live dashboard (auto-refreshed each cycle):** https://juxtaposition1.github.io/V.A.P.E/ + — "Featured Investigation" and the "Investigation Archive" both link every + finding back to its source report on GitHub. +- **Source report in-repo:** `intel/investigations/` in + https://github.com/jUXTAPOSITION1/V.A.P.E +- **Permanent verdict ledger:** every verdict V.A.P.E. has ever filed is recorded in + `intel/investigations/ledger.json`, and re-checked on a schedule + (`agents/review_ledger.py`) against fresh data — a verdict that drifts logs a + real finding rather than silently going stale. + +## Redaction note + +This report contains only public on-chain data and public API responses. No +private keys, operator secrets, wallet material, or non-public account records +are included. diff --git a/showcase/vape-onchain-detective/showcase.json b/showcase/vape-onchain-detective/showcase.json new file mode 100644 index 0000000..1db51c2 --- /dev/null +++ b/showcase/vape-onchain-detective/showcase.json @@ -0,0 +1,99 @@ +{ + "slug": "vape-onchain-detective", + "title": "V.A.P.E. — Virtual Ape Private Eye", + "tagline": "Autonomous on-chain detective on Base — real-time investigations, a self-improving build pipeline, and dual-rail hiring via ACP and x402", + "description": "V.A.P.E. is an autonomous ACP provider and on-chain detective on Base. Each cycle it auto-selects the highest-signal live target (violent movers, thin-liquidity pools) and runs a keyless, multi-source investigation — GoPlus token-security, DexScreener liquidity, Base RPC code presence, optional Etherscan V2 verification, and DeFiLlama exploit-feed correlation. It computes a 0-100 safety score, files a PROCEED / CAUTION / REJECT verdict to a permanent ledger, and surfaces every finding on a live dashboard where each result deep-links to its source report. Every verdict is periodically re-checked against fresh data, and a drifted call gets logged as a real finding rather than silently going stale. Beyond investigating, V.A.P.E. now audits and improves its own codebase: a self-improvement loop finds one real, evidence-backed issue per cycle (prioritizing its own red-team findings) and opens a human-reviewed PR to fix it — closing the loop from 'VAPE discovers it's vulnerable' to 'VAPE proposes to fix itself.' It can also be hired two ways: an ACP-escrowed managed engagement for deeper audits, or an instant x402 pay-per-call worker for its 6 automated offerings plus a $50 24-hour-SLA deep-dive audit (full recon + Slither + a frontier-model line-by-line source review). Read-only and keyless-first at its core: investigations never sign, spend, or mutate state outside an explicitly authorized, escrowed job. Real data only — no simulations, no fabricated numbers.", + "status": "live", + "topic": "skills", + "topics": [ + "security", + "investigation", + "base", + "token-safety", + "erc-8004", + "real-data", + "x402", + "self-improving-agent" + ], + "builder": { + "name": "based_vape", + "url": "https://x.com/based_vape" + }, + "links": { + "repo": "https://github.com/jUXTAPOSITION1/V.A.P.E", + "share": "https://x.com/based_vape", + "feedback": "https://github.com/jUXTAPOSITION1/V.A.P.E/issues", + "demo": "https://juxtaposition1.github.io/V.A.P.E/" + }, + "primitives": [ + "acp", + "wallet", + "token" + ], + "visual": { + "kind": "on-chain investigation dashboard", + "eyebrow": "base + acp + x402 + erc-8004", + "title": "real-time investigations, a self-improving build pipeline, and dual-rail hiring", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/vape-onchain-detective/assets/hero-character.jpg" + }, + "skills": [ + { + "name": "vape-investigate", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/vape-onchain-detective/skills/vape-investigate", + "sourcePath": "showcase/vape-onchain-detective/skills/vape-investigate", + "summary": "Run a V.A.P.E. deep on-chain investigation on any Base token/contract — keyless multi-source recon (GoPlus, DexScreener, Base RPC, Etherscan V2, DeFiLlama hacks) returning a 0-100 safety score and a PROCEED / CAUTION / REJECT verdict with a linkable evidence report. Read-only; standalone via CLI or as the executor behind V.A.P.E.'s ACP and x402 security offerings.", + "install": "cp -R showcase/vape-onchain-detective/skills/vape-investigate ~/.agents/skills/\ncp -R showcase/vape-onchain-detective/skills/vape-investigate ~/.claude/skills/" + } + ], + "artifacts": [ + { + "label": "OpenAI-token investigation — real CAUTION 55/100 finding (2026-07-05)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/vape-onchain-detective/examples/openai-token-investigation-proof.md", + "kind": "proof" + }, + { + "label": "Live dashboard (auto-refreshed each cycle)", + "href": "https://juxtaposition1.github.io/V.A.P.E/", + "kind": "demo" + }, + { + "label": "Dashboard — Mission, capabilities, and a live Track Record (screenshot)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/vape-onchain-detective/assets/track-record.png", + "kind": "screenshot" + }, + { + "label": "Dashboard — Featured Investigation case file (screenshot)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/vape-onchain-detective/assets/case-file.png", + "kind": "screenshot" + }, + { + "label": "Dashboard — Engagement Options: ACP escrow + x402 instant pay-per-call (screenshot)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/vape-onchain-detective/assets/engagement-options.png", + "kind": "screenshot" + }, + { + "label": "Dashboard — Development Ledger: V.A.P.E.'s self-improvement PRs (screenshot)", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/vape-onchain-detective/assets/development-ledger.png", + "kind": "screenshot" + }, + { + "label": "vape-investigate skill source", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/vape-onchain-detective/skills/vape-investigate", + "kind": "skill" + }, + { + "label": "V.A.P.E. package README", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/vape-onchain-detective/README.md", + "kind": "docs" + } + ], + "soul": { + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/blob/main/showcase/vape-onchain-detective/soul.md", + "summary": "Public V.A.P.E. agent context: real-data-only and read-only principles, the 0-100 / PROCEED-CAUTION-REJECT scoring scale, keyless-first sourcing, public-address-only inputs, redaction boundaries, and its proof-over-claims stance." + }, + "feedbackPrompts": [ + "Is the combination signal (brand-impersonating name + no-vetting factory deployment + sub-3-day-old pair) the right thing to down-rank, versus scoring each flag independently?", + "For an agent choosing how to hire V.A.P.E. — an instant x402 pay-per-call scan versus a fully escrowed ACP engagement for deeper audits — is the price/depth split at the right line?", + "Should a self-improving agent's own PRs (V.A.P.E.'s Development Ledger) require the same public evidence trail as its investigation verdicts, or is human review of the PR enough?" + ] +} diff --git a/showcase/vape-onchain-detective/skills/vape-investigate/SKILL.md b/showcase/vape-onchain-detective/skills/vape-investigate/SKILL.md new file mode 100644 index 0000000..a257ea6 --- /dev/null +++ b/showcase/vape-onchain-detective/skills/vape-investigate/SKILL.md @@ -0,0 +1,118 @@ +--- +name: vape-investigate +description: Run a V.A.P.E. deep on-chain investigation on a Base token/contract — multi-source keyless recon (GoPlus, DexScreener, Base RPC, Etherscan V2, DeFiLlama hack feed) that returns a 0-100 safety score and a PROCEED / CAUTION / REJECT verdict with a linkable evidence report. +version: 1.0.0 +--- + +# V.A.P.E. Deep On-Chain Investigation + +Use this skill to investigate a Base token or contract **before** you buy, list, +delegate authority, or accept a counterparty in an ACP job. It runs the same +autonomous, real-data pipeline V.A.P.E. (Virtual Ape Private Eye) runs every +cycle, and produces a scored verdict plus a redaction-safe evidence report you +can link publicly. + +It is deliberately **read-only and keyless-first**: every finding is sourced +from public APIs and on-chain reads. It never signs, spends, or mutates state. + +## When to use it + +- You have a Base token/contract address (`0x...`) and need a fast, evidence-backed + risk read: honeypot, taxes, mint/ownership powers, liquidity depth, pair age, + contract verification, and correlation to recent real exploit techniques. +- You are screening an ACP counterparty's token/contract before committing to a job. +- You want a repeatable "highest-signal target" sweep of live Base movers + (`--auto` picks a violent-mover / thin-liquidity target automatically). + +## When NOT to use it + +- **Not a full smart-contract audit.** For deep bytecode/formal analysis use a + dedicated auditor (slither/aderyn/mythril tiers or an audit-grade provider). + This skill flags surface risk; it does not prove absence of vulnerabilities. +- **Not investment advice.** A `PROCEED` verdict means "no automated red flags," + not "safe to buy." +- **Not for private keys, seed phrases, or off-chain PII.** Inputs are public + addresses only. Refuse anything else. +- Chains other than Base are best-effort only (pass the correct chain id). + +## Inputs + +- `--address 0x...` — the target token/contract on Base, **or** +- `--auto` — let the engine select the highest-signal live Base target. +- `--chain ` — chain id (default `8453` = Base). + +## Tools, credentials, preconditions + +- Python 3.11+, `git`. No paid keys required for the core scan. +- Data sources (all keyless): **GoPlus** token-security, **DexScreener** liquidity, + **Base RPC** (`eth_getCode`), **DeFiLlama** `/hacks` feed. +- **Optional** `ETHERSCAN_API_KEY` (Etherscan V2 unified key) enables the + contract-verification dimension. Without it, all other recon still runs. + +Install: +```bash +git clone https://github.com/jUXTAPOSITION1/V.A.P.E.git +cd V.A.P.E +python3 -m pip install -r agents/requirements.txt # stdlib-first; light deps +export ETHERSCAN_API_KEY=your_key_here # OPTIONAL, Base verification +``` + +## Workflow + +1. Confirm the input is a **public address**, never a secret. Stop if it isn't. +2. Run the investigation: + ```bash + # explicit target + python agents/investigate.py --address 0x... --chain 8453 + # or auto-select the highest-signal live Base target + python agents/investigate.py --auto + ``` +3. The engine runs GoPlus + DexScreener + Base RPC + (optional) Etherscan V2 + + hack-feed correlation, computes a weighted **0-100 safety score**, and files: + - `intel/investigations/investigation--.md` — full evidence report + - a `finding` entry in Memory (`skillforge/memory/findings.jsonl`) + - a row in the investigation catalog +4. Read the verdict: `PROCEED` (>=75) / `CAUTION` (45-74) / `REJECT` (<45), the + penalty rationale, and the raw recon appendix. + +## Approval gates + +This skill performs **no** spending, posting, account creation, deployment, or +on-chain mutation — so no approval gate is required to run a scan. If you wire it +behind an ACP provider that settles paid jobs, the **payment/settlement** step +(set-budget / submit) is a separate action and MUST be gated on the operator's +explicit authorization of job, price, and counterparty. + +## Stop conditions & handoff + +- Stop immediately if the input is a private key, seed phrase, or non-address. +- If GoPlus/DexScreener return nothing for the target (unlisted/illiquid), report + that explicitly rather than inferring safety — hand off to a manual/deep review. +- If a `REJECT` is driven by a honeypot or owner-can-drain flag, hand off to the + human before any transaction is considered. + +## Validation checks + +- `python agents/investigate.py --address ` returns a verdict + and writes a report file under `intel/investigations/`. +- Re-running the same `--auto` target within 12h is de-duplicated (guard), so the + engine won't re-hammer the same mover. +- The generated report contains the four evidence sections (Market/Liquidity, + Token Security, On-chain Presence, Threat Correlation) with real values. + +## Output contract + +`investigate.py` prints and returns a JSON object: +```json +{ + "target": "0x...", + "symbol": "OpenAI", + "verdict": "CAUTION", + "score": 68, + "report": "intel/investigations/investigation-YYYYMMDD-HHMMSS-0x....md", + "reasons": ["[-10] Low liquidity $26,641", "[-12] Pair only 0.7 days old (fresh-launch risk)"] +} +``` + +Redaction: outputs contain only public on-chain data and public API responses. +Never include private keys, operator secrets, or non-public account records. diff --git a/showcase/vape-onchain-detective/soul.md b/showcase/vape-onchain-detective/soul.md new file mode 100644 index 0000000..630c8a3 --- /dev/null +++ b/showcase/vape-onchain-detective/soul.md @@ -0,0 +1,48 @@ +# V.A.P.E. — public agent context (soul) + +V.A.P.E. (Virtual Ape Private Eye) is an autonomous on-chain detective operating +on Base and registered on Virtuals Protocol (ERC-8004 #54988). This file is the +**public, redacted** description of how it works and where its boundaries are. + +## What it is + +A real-time investigator, not an oracle of truth. It gathers public on-chain and +API evidence about Base tokens/contracts, scores risk, and publishes verdicts that +link back to their sources so anyone can check the work. + +## Operating principles + +- **Real data only.** Every number in a report is a live API or on-chain read. + No simulations, no fabricated figures. If a source returns nothing, it says so + rather than inferring safety. +- **Read-only by default.** Investigations never sign, spend, or mutate state. + The only value-moving actions are explicit job-settlement steps — ACP escrow + release, or an x402 payment authorization the caller signs themselves — never + something V.A.P.E. initiates unprompted. +- **Self-improvement is human-gated.** V.A.P.E. can propose fixes to its own + codebase, grounded in real, evidence-backed findings (prioritizing its own + red-team results). It opens a pull request; it never merges its own code. +- **Keyless-first.** The core scan needs no paid keys; optional keys only widen + coverage (e.g. contract verification). +- **Proof over claims.** Findings are published with linkable sources; a verdict + is only as strong as the evidence behind it. + +## Scoring + +`PROCEED` (>=75), `CAUTION` (45-74), `REJECT` (<45) on a 0-100 safety score. +A `PROCEED` means "no automated red flags," never "safe to buy." Nothing V.A.P.E. +publishes is investment advice. + +## Boundaries & secret handling + +- Inputs are **public addresses only** — never private keys, seed phrases, or PII. +- Outputs are redacted: no keys, tokens, wallet material, or non-public account + records are ever published. +- Deep smart-contract audit is out of scope for the fast investigation path; it + routes to dedicated audit tooling. + +## Where to verify + +- Live dashboard: https://juxtaposition1.github.io/V.A.P.E/ +- Repo: https://github.com/jUXTAPOSITION1/V.A.P.E +- Identity: https://app.virtuals.io/virtuals/54988 · X: https://x.com/based_vape diff --git a/showcase/varius-ai-shopping-assistant/README.md b/showcase/varius-ai-shopping-assistant/README.md new file mode 100644 index 0000000..f312a3e --- /dev/null +++ b/showcase/varius-ai-shopping-assistant/README.md @@ -0,0 +1,56 @@ +# Varius — Telegram AI Shopping Assistant + +**@virtualshoppingbot** on Telegram + +Varius turns a plain-text shopping or travel request into a tracked affiliate link with USDC cashback, minted through an on-chain ACP v2 escrow job. + +## What it does + +1. User sends a natural-language request ("find me a hotel in Penang this weekend") +2. Varius extracts intent using Kimi K2 via Virtuals Agent Compute +3. Varius returns 3 merchant recommendations with cashback rates +4. When the user signals purchase intent, Varius fires an ACP v2 job on Base Mainnet to the Laguna bridge agent +5. The Laguna agent mints a tracked affiliate shortlink and delivers it back via ACP +6. User clicks the link, books with the merchant, and earns USDC cashback tracked to their wallet +7. `/dashboard` shows pending and available USDC earnings + +## EconomyOS primitives used + +- **Agent Compute** — Kimi K2 via `os.virtuals.io` for intent extraction and recommendations +- **ACP v2** — on-chain USDC escrow on Base Mainnet for every affiliate link mint +- **Agent Registry** — provider agent discovered at runtime via `browseAgents` +- **SocketTransport** — real-time job lifecycle events + +## Demo + +- Live bot: https://t.me/virtualshoppingbot +- Demo video: https://youtu.be/dVV-3BPpvzE + +## Structure + +``` +showcase/varius-ai-shopping-assistant/ + README.md ← this file + showcase.json ← Showcase card metadata + soul.md ← Public agent context + examples/ + penang-hotel-run.md ← Full run log (redacted) + offerings/ + offerings.json ← ACP provider offerings (mint_link, sweep_commissions) + skills/ + laguna-affiliate-link-via-acp/ + SKILL.md ← Reusable skill + examples/ + hotel-search/ + README.md + prompt.md + result-redacted.md +``` + +## Reusing the skill + +```bash +cp -R showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp ~/.agents/skills/ +``` + +The skill works with any merchant in the Laguna Network catalogue (1000+ merchants across travel, retail, and lifestyle). diff --git a/showcase/varius-ai-shopping-assistant/examples/penang-hotel-run.md b/showcase/varius-ai-shopping-assistant/examples/penang-hotel-run.md new file mode 100644 index 0000000..0e673c0 --- /dev/null +++ b/showcase/varius-ai-shopping-assistant/examples/penang-hotel-run.md @@ -0,0 +1,63 @@ +# Example Run: Penang Hotel Search + +**Date:** 2026-07-02 +**Bot:** @virtualshoppingbot (Telegram) +**Outcome:** 2 affiliate links minted, 2 ACP jobs completed on Base Mainnet + +--- + +## User session + +``` +User: i want to go to penang this weekend, find me hotels + +Varius: Here are some options for Penang this weekend: + + 1. Hard Rock Hotel Penang — Batu Ferringhi beachfront, + music-themed resort with multiple pools - ~$150/night + 2. [Hotel B] — sea view, family-friendly - ~$90/night + 3. [Hotel C] — boutique, city centre - ~$80/night + + → We recommend booking via Trip.com (5% rebate) 🏨 + — affiliate link coming shortly ⏳ + + [~38 seconds later] + + Here are your booking links: + 🔗 Trip.com · 5% rebate → https://agents.laguna.network/r/[REDACTED] + 🔗 Agoda · 6% rebate → https://agents.laguna.network/r/[REDACTED] + + Both links earn you cashback. Click either to book! + +User: /dashboard + +Varius: Your Laguna Cashback Dashboard + Pending: 0.42 USDC + Available: 1.85 USDC + Total conversions: 7 +``` + +--- + +## ACP job trace (redacted) + +| Field | Job A (Trip.com) | Job B (Agoda) | +|---|---|---| +| Job ID | [REDACTED] | [REDACTED] | +| Provider | ACPLagunaTranslator | ACPLagunaTranslator | +| Offering | mint_link | mint_link | +| Budget | 0.01 USDC | 0.01 USDC | +| Network | Base Mainnet | Base Mainnet | +| Status | completed | completed | +| Latency | ~35s | ~38s | +| Deliverable | affiliate shortlink | affiliate shortlink | + +Both jobs ran in parallel. Total wall-clock time from user intent signal to both links delivered: ~38 seconds. + +--- + +## Compute trace + +- Model: `kimi-k2-instruct` via Virtuals Agent Compute (`os.virtuals.io`) +- Calls: intent extraction × 2 (initial query + purchase intent signal), recommendation generation × 1 +- Total compute spend: ~$0.002 diff --git a/showcase/varius-ai-shopping-assistant/offerings/offerings.json b/showcase/varius-ai-shopping-assistant/offerings/offerings.json new file mode 100644 index 0000000..25b8556 --- /dev/null +++ b/showcase/varius-ai-shopping-assistant/offerings/offerings.json @@ -0,0 +1,29 @@ +{ + "agent_name": "ACPLagunaTranslator", + "version": "1.0", + "builder": "yx-laguna", + "chain": "base", + "wallet": "REDACTED", + "offerings": { + "acp_jobs": [ + { + "id": 1, + "name": "mint_link", + "title": "Mint Affiliate Link", + "category": "commerce", + "price": "$0.01 USDC", + "sla_minutes": 2, + "description": "Given a merchant name and a user EVM wallet address, searches the Laguna Network merchant catalogue, selects the best cashback rate, and mints a tracked affiliate shortlink. The shortlink registers the user's wallet for USDC cashback on any qualifying purchase. Supports 1000+ merchants across travel, retail, and lifestyle categories." + }, + { + "id": 2, + "name": "sweep_commissions", + "title": "Sweep Commissions", + "category": "commerce", + "price": "$0.01 USDC", + "sla_minutes": 2, + "description": "Checks the Laguna Network dashboard for a given user wallet and sweeps any available (confirmed) USDC commissions. Returns the pending balance, available balance, and total conversion count." + } + ] + } +} diff --git a/showcase/varius-ai-shopping-assistant/showcase.json b/showcase/varius-ai-shopping-assistant/showcase.json new file mode 100644 index 0000000..7b59d04 --- /dev/null +++ b/showcase/varius-ai-shopping-assistant/showcase.json @@ -0,0 +1,74 @@ +{ + "slug": "varius-ai-shopping-assistant", + "title": "Varius — Telegram AI Shopping Assistant", + "tagline": "Turns a plain-text shopping request in Telegram into a tracked affiliate link with USDC cashback, minted through an on-chain ACP escrow job", + "description": "Varius is a production Telegram AI assistant (@virtualshoppingbot) that turns a plain-text shopping or travel request into a tracked affiliate link with USDC cashback. The bot uses Virtuals Agent Compute (Kimi K2) for intent extraction and recommendations, then fires an ACP v2 job to hire a Laguna bridge agent that mints the affiliate shortlink. Every link mint is a real on-chain USDC escrow job on Base Mainnet. The end user sees only a clean Telegram conversation and a USDC earnings dashboard; the on-chain agentic commerce layer is invisible.", + "status": "production", + "topic": "commerce", + "topics": ["commerce", "compute", "acp"], + "hidden": false, + "builder": { + "name": "yix", + "url": "https://github.com/yx-laguna" + }, + "links": { + "repo": "https://github.com/yx-laguna/nexus-gateway", + "demo": "https://t.me/virtualshoppingbot", + "video": "https://youtu.be/dVV-3BPpvzE", + "share": "https://youtu.be/dVV-3BPpvzE", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Varius%20AI%20Shopping%20Assistant&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20More%20merchant%20categories%20(flights%2C%20electronics%2C%20fashion)%3F%0A-%20Should%20the%20ACP%20bridge%20support%20batch%20link%20minting%20for%20comparison%20shopping%3F%0A-%20What%20would%20make%20the%20SKILL.md%20easier%20to%20adapt%20for%20a%20different%20affiliate%20network%3F" + }, + "primitives": ["acp"], + "visual": { + "kind": "youtube demo video", + "eyebrow": "telegram + acp v2", + "title": "affiliate link minting", + "posterUrl": "https://img.youtube.com/vi/dVV-3BPpvzE/maxresdefault.jpg", + "videoLabel": "Watch the demo on YouTube" + }, + "skills": [ + { + "name": "laguna-affiliate-link-via-acp", + "href": "https://github.com/yx-laguna/acp-cli-demos/tree/main/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp", + "sourcePath": "showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp", + "summary": "Reusable ACP workflow to discover 1000+ merchants that offer USDC cashback, fund a USDC escrow job, and receive a tracked affiliate shortlink. Works for any merchant supported by Laguna Network.", + "install": "cp -R showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp ~/.agents/skills/" + } + ], + "artifacts": [ + { + "label": "YouTube demo video", + "href": "https://youtu.be/dVV-3BPpvzE", + "kind": "video" + }, + { + "label": "Redacted result report", + "href": "https://github.com/yx-laguna/acp-cli-demos/blob/main/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp/examples/hotel-search/result-redacted.md", + "kind": "proof" + }, + { + "label": "Demo prompt", + "href": "https://github.com/yx-laguna/acp-cli-demos/blob/main/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp/examples/hotel-search/prompt.md", + "kind": "prompt" + }, + { + "label": "Skill source", + "href": "https://github.com/yx-laguna/acp-cli-demos/tree/main/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp", + "kind": "skill" + }, + { + "label": "Live bot", + "href": "https://t.me/virtualshoppingbot", + "kind": "demo" + } + ], + "feedbackPrompts": [ + "Which other merchant categories should the bot support (flights, electronics, fashion)?", + "Should the ACP bridge support batch link minting for comparison shopping?", + "What would make the SKILL.md easier to adapt for a different affiliate network?" + ], + "soul": { + "href": "https://github.com/yx-laguna/acp-cli-demos/blob/main/showcase/varius-ai-shopping-assistant/soul.md", + "summary": "Varius operates as a deal-finding assistant: it never books on the user's behalf, only delivers tracked links. It earns from the Laguna affiliate network; the 0.01 USDC ACP job cost is recovered via cashback." + } +} diff --git a/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp/SKILL.md b/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp/SKILL.md new file mode 100644 index 0000000..988f608 --- /dev/null +++ b/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp/SKILL.md @@ -0,0 +1,150 @@ +# Skill: laguna-affiliate-link-via-acp + +## When to use this skill + +Use this skill when an agent needs to mint a tracked affiliate shortlink for a specific merchant and user wallet, using the Laguna Network affiliate platform, mediated by an ACP v2 agent-to-agent job. + +**Use when:** +- You have a merchant the user wants to purchase from (hotel, flight, product retailer) +- You want to earn the user real USDC cashback on that purchase +- You need a tracked link — not a raw merchant URL + +**Do not use when:** +- You need to complete a purchase autonomously (this skill only delivers a link, not a booking) +- The merchant is not in the Laguna Network catalogue +- You do not have a user wallet address to attach the cashback to + +--- + +## Required inputs + +| Input | Source | Notes | +|---|---|---| +| `merchant_name` | User request or search result | e.g. `"Trip.com"`, `"Agoda"`, `"Lazada"` | +| `user_wallet_address` | User profile / onboarding | Base Mainnet EVM address | +| `acp_client` | Agent runtime | Initialised ACP v2 client with funded wallet | +| `laguna_provider_wallet` | Agent config | Wallet address of the ACPLagunaTranslator provider agent | + +--- + +## Preconditions + +1. ACP client wallet has ≥ 0.01 USDC on Base Mainnet (job budget). +2. The Laguna bridge provider agent (`ACPLagunaTranslator`) is registered and online in the Agent Registry. +3. The Laguna MCP is reachable at `agents.laguna.network/mcp`. + +--- + +## Step-by-step workflow + +### Step 1 — Discover the provider + +```ts +const agents = await acpClient.browseAgents("Laguna Affiliate"); +const provider = agents.find(a => a.walletAddress === LAGUNA_PROVIDER_WALLET); +const offering = provider.offerings.find(o => o.name === "mint_link"); +``` + +Fail if no matching provider is found. Log and surface error to the user. + +### Step 2 — Create and fund the job + +```ts +const job = await acpClient.createJobFromOffering(offering, { + userWallet: user_wallet_address, + merchantName: merchant_name, +}); +// Budget: 0.01 USDC. Set by provider; client funds it. +await job.fund(); +``` + +Record `job.id` for polling / event tracking. + +### Step 3 — Wait for delivery + +Listen for the `job.submitted` event via SocketTransport: + +```ts +acpClient.on("job.submitted", async (event) => { + if (event.jobId === job.id) { + const deliverable = event.deliverable; // affiliate shortlink URL + // proceed to Step 4 + } +}); +``` + +**Polling fallback:** if no event within 60 s, call `acpClient.hydrateSessions()` and check `job.status`. + +Timeout at 120 s. If job has not submitted, surface error and offer to retry. + +### Step 4 — Complete the job and return the link + +```ts +await job.complete(); +return deliverable; // the affiliate shortlink URL +``` + +The 0.01 USDC escrow is released to the provider. The user's wallet is registered for cashback tracking by Laguna. + +--- + +## Approval gates + +This skill performs two on-chain actions that require the agent to have pre-authorised funds: + +1. **Job creation + funding** — deducts 0.01 USDC from the agent's Base wallet. +2. **Job completion** — releases funds to the provider. + +No human approval is required per-link if the agent wallet has been pre-funded. Surface the 0.01 USDC cost to users during onboarding. + +--- + +## Stop conditions + +Stop and return an error without completing the job if: +- Provider agent not found in registry +- Job funding fails (insufficient balance) +- Deliverable not received within 120 s +- Deliverable URL is malformed or does not match expected shortlink domain + +--- + +## Evidence and redaction rules + +When logging or producing a result report: +- **Include:** job ID, provider wallet (truncated to first 6 + last 4 chars), merchant name, link domain (not full URL), completion timestamp +- **Redact:** full affiliate shortlink URL (contains user tracking token), user wallet address (truncate), any Laguna API keys or auth headers + +--- + +## Validation checklist + +- [ ] Provider discovered by wallet address, not by name only +- [ ] Job ID recorded before funding +- [ ] Deliverable is a valid HTTPS URL +- [ ] `job.complete()` called after receiving deliverable +- [ ] Error path surfaces a human-readable message (not a raw exception) + +--- + +## Output contract + +```ts +{ + success: boolean, + jobId: string, // ACP job ID + affiliateUrl: string, // tracked shortlink (deliver to user) + merchant: string, // canonical merchant name + cashbackRate: string, // e.g. "5%" (from Laguna merchant info) + timestamp: string, // ISO 8601 +} +``` + +On failure: +```ts +{ + success: false, + error: string, // human-readable reason + jobId?: string, // set if job was created before failure +} +``` diff --git a/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp/examples/hotel-search/README.md b/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp/examples/hotel-search/README.md new file mode 100644 index 0000000..cd85f72 --- /dev/null +++ b/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp/examples/hotel-search/README.md @@ -0,0 +1,23 @@ +# Example: Hotel Search (Penang) + +This example shows the `laguna-affiliate-link-via-acp` skill triggered via a natural-language hotel search request in Telegram. + +## What happens + +1. User sends a plain-English hotel request to @virtualshoppingbot on Telegram. +2. Varius extracts intent using Kimi K2 via Virtuals Agent Compute. +3. Varius returns 3 hotel recommendations. +4. User signals purchase intent ("I like option 1"). +5. Varius fires two parallel ACP v2 jobs on Base Mainnet — one for Trip.com, one for Agoda. +6. Each job is funded with 0.01 USDC escrow, delivered by the Laguna bridge provider agent, then completed. +7. Two tracked affiliate shortlinks arrive in the Telegram chat within ~30–90 seconds. +8. User can check `/dashboard` to see pending USDC cashback. + +## Files + +- `prompt.md` — the user messages that triggered this run +- `result-redacted.md` — full execution trace with sensitive data redacted + +## Demo video + +https://youtu.be/dVV-3BPpvzE diff --git a/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp/examples/hotel-search/prompt.md b/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp/examples/hotel-search/prompt.md new file mode 100644 index 0000000..4c10dde --- /dev/null +++ b/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp/examples/hotel-search/prompt.md @@ -0,0 +1,24 @@ +# Demo Prompt — Hotel Search + +This is the natural-language prompt used by the end user to trigger the skill. + +--- + +**Channel:** Telegram (@virtualshoppingbot) + +**User message:** +> I need a hotel in Penang this weekend, something nice near the beach + +**Follow-up (purchase intent signal):** +> I like option 1, let's go with it + +--- + +## What the agent does + +1. Extracts intent: `hotel_search`, location `Penang`, preference `beach` +2. Returns 3 recommendations via Agent Compute (Kimi K2) +3. Detects `purchase_ready: true` on the follow-up +4. Fires parallel ACP v2 jobs for Trip.com and Agoda affiliate link minting +5. Delivers both tracked shortlinks to the user in Telegram +6. User can check `/dashboard` to see pending USDC cashback diff --git a/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp/examples/hotel-search/result-redacted.md b/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp/examples/hotel-search/result-redacted.md new file mode 100644 index 0000000..4582355 --- /dev/null +++ b/showcase/varius-ai-shopping-assistant/skills/laguna-affiliate-link-via-acp/examples/hotel-search/result-redacted.md @@ -0,0 +1,104 @@ +# Result Report — Hotel Search (Redacted) + +**Skill:** laguna-affiliate-link-via-acp +**Example:** Hotel search via Telegram +**Date:** 2026-06-[REDACTED] +**Status:** Completed ✅ + +--- + +## Prompt + +> User: "I need a hotel in Penang this weekend, something nice near the beach" + +--- + +## Execution trace + +### 1. Intent extraction (Agent Compute — Kimi K2) + +Input: user message +Output: +```json +{ + "intent": "hotel_search", + "location": "Penang", + "timeframe": "this weekend", + "preference": "beach", + "purchase_ready": false +} +``` +Latency: ~1.1 s + +### 2. Merchant recommendations generated + +3 hotel options surfaced (names redacted to property category only): +- Beach resort, 4-star, Batu Ferringhi +- Beachfront international chain, 5-star +- Boutique hotel, city centre (included for contrast) + +### 3. User signals purchase intent + +> User: "I like option 1, let's go with it" + +Intent re-evaluated: `purchase_ready: true` + +### 4. ACP v2 job created (mint_link) + +``` +Job ID: [REDACTED] +Provider: 0x1a2b...9f0e (ACPLagunaTranslator) +Offering: mint_link +Budget: 0.01 USDC +Network: Base Mainnet +Job created at: [REDACTED TIMESTAMP] +Job funded at: [REDACTED TIMESTAMP] +``` + +### 5. Laguna MCP — search_merchants + +Query: `{ query: "beach resort Penang", geo: "MY" }` +Result: matched merchants — Trip.com (5% cashback), Agoda (4.5% cashback) +Both selected for parallel minting. + +### 6. Laguna MCP — mint_link (×2, parallel) + +``` +Merchant A: Trip.com + Shortlink: [REDACTED — contains user tracking token] + Cashback rate: 5% + Cookie duration: 30 days + +Merchant B: Agoda + Shortlink: [REDACTED — contains user tracking token] + Cashback rate: 4.5% + Cookie duration: 7 days +``` + +### 7. ACP jobs completed + +``` +Job A (Trip.com): submitted → completed +Job B (Agoda): submitted → completed +Total latency: ~38 s (from user intent signal to both links delivered) +``` + +0.01 USDC per job released to provider. User wallet [REDACTED] registered for cashback on both links. + +### 8. Dashboard check (/dashboard) + +``` +Pending: 0.42 USDC +Available: 1.85 USDC +Total conversions: 7 +``` + +--- + +## Redaction notes + +- Affiliate shortlink URLs redacted (contain user wallet tracking token) +- User wallet address truncated +- Job IDs redacted +- Timestamps generalised +- No API keys, auth headers, or private session data included diff --git a/showcase/varius-ai-shopping-assistant/soul.md b/showcase/varius-ai-shopping-assistant/soul.md new file mode 100644 index 0000000..881519d --- /dev/null +++ b/showcase/varius-ai-shopping-assistant/soul.md @@ -0,0 +1,31 @@ +# Varius — Public Agent Context + +## Role + +Varius is a deal-finding assistant that runs as a Telegram bot (@virtualshoppingbot). It helps users find merchants with affiliate cashback, mints tracked shortlinks via ACP, and surfaces real USDC earnings through a dashboard. + +## Operating boundaries + +- Varius **never books or purchases on the user's behalf** — it only delivers tracked affiliate links for the user to click themselves. +- Varius **never stores payment details** — cashback is tracked purely by EVM wallet address. +- Varius **only mints links when the user explicitly signals purchase intent** — it does not flood the conversation with links on first contact. +- Geo context (country) comes from the user's stored profile only — never inferred from LLM output — to prevent hallucination. + +## Collaboration style + +- Responds in plain conversational English; no crypto jargon exposed to the end user. +- Presents exactly 3 recommendations per query — no more, no less — to avoid overwhelming. +- Uses a single "recommended merchant" line before minting, so the user knows what's coming. +- Delivers both Trip.com and Agoda links for hotel searches in parallel (best coverage for SEA travel). + +## Economics + +- Each ACP v2 job costs 0.01 USDC from the Varius agent wallet (funded upfront). +- The 0.01 USDC is economically net-zero: Laguna's affiliate commissions refund it to the user over time via cashback. +- Varius earns nothing directly — value accrues to the user via USDC cashback on purchases. + +## What this agent is NOT + +- Not an autonomous buyer or travel agent. +- Not connected to any booking APIs — it mints affiliate links only. +- Not storing or transmitting private keys, card details, or user credentials of any kind. diff --git a/showcase/virtuals-playground/README.md b/showcase/virtuals-playground/README.md new file mode 100644 index 0000000..e6f5a9d --- /dev/null +++ b/showcase/virtuals-playground/README.md @@ -0,0 +1,29 @@ +# Virtuals Playground — Showcase Submission + +**Live URL:** https://virtuals-playground.vercel.app +**Builder:** Satyam Singhal + +## What it is + +A fully browser-based playground for the Virtuals EconomyOS Compute API. Paste your API key, pick a model, and start testing — no installs, no config files. + +## Features + +| View | What it does | +|------|-------------| +| **Chat** | Interactive chat with any loaded model; code snippets (Python / cURL / JS) generated per session | +| **Compare** | Send one prompt to two models simultaneously, side-by-side results | +| **Batch Test** | Fire a single prompt at every available model, ranked results table with latency + token counts | +| **Models** | Browse all available Virtuals Compute models with metadata | + +## Proof + +- Live deployment: https://virtuals-playground.vercel.app +- Source: https://github.com/Satyam-10124/virtuals_playground + +## Stack + +- React 18 + TypeScript + Vite +- Tailwind CSS +- Virtuals EconomyOS Compute API (`https://compute.virtuals.io/v1`) +- Deployed on Vercel diff --git a/showcase/virtuals-playground/poster.png b/showcase/virtuals-playground/poster.png new file mode 100644 index 0000000..bdf636b Binary files /dev/null and b/showcase/virtuals-playground/poster.png differ diff --git a/showcase/virtuals-playground/showcase.json b/showcase/virtuals-playground/showcase.json new file mode 100644 index 0000000..90ca1e1 --- /dev/null +++ b/showcase/virtuals-playground/showcase.json @@ -0,0 +1,52 @@ +{ + "slug": "virtuals-playground", + "title": "Virtuals Playground", + "tagline": "Chats with, compares, and batch-tests every Virtuals EconomyOS Compute API model from one browser tab — no setup required", + "description": "Virtuals Playground is an open-source developer tool that lets builders interact with every Virtuals EconomyOS Compute API model from a single browser tab. It ships four views: an interactive Chat playground, a side-by-side Model Compare panel, a Batch Test runner that fires a single prompt at all loaded models in parallel, and a Models explorer with metadata. Every chat session auto-generates ready-to-run Python, cURL, and JavaScript code snippets. The UI is fully responsive — desktop and mobile — and requires only a Virtuals API key to start.", + "status": "live", + "topic": "skills", + "topics": ["developer-tools", "playground", "compute", "api-explorer"], + "builder": { + "name": "Satyam Singhal", + "url": "https://github.com/Satyam-10124" + }, + "links": { + "repo": "https://github.com/Satyam-10124/virtuals_playground", + "demo": "https://virtuals-playground.vercel.app", + "share": "https://virtuals-playground.vercel.app", + "video": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/virtuals-playground/virtuals-playground.mp4", + "feedback": "https://github.com/Virtual-Protocol/acp-cli-demos/issues/new?title=Feedback%3A%20Virtuals%20Playground&body=Which%20feedback%20prompt%20fits%3F%0A%0A-%20I%20want%20a%20feature%20added%0A-%20The%20code%20snippets%20are%20useful%0A-%20I%20want%20to%20reuse%20this%20for%20my%20own%20project%0A%0ANotes%3A%0A" + }, + "primitives": ["acp"], + "visual": { + "kind": "live page", + "eyebrow": "browser · virtuals compute api", + "title": "chat, compare & batch test models", + "posterUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/virtuals-playground/poster.png", + "videoUrl": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/virtuals-playground/virtuals-playground.mp4", + "videoLabel": "Watch the demo" + }, + "skills": [], + "artifacts": [ + { + "label": "Live playground", + "href": "https://virtuals-playground.vercel.app", + "kind": "demo" + }, + { + "label": "Demo video", + "href": "https://raw.githubusercontent.com/Virtual-Protocol/acp-cli-demos/main/showcase/virtuals-playground/virtuals-playground.mp4", + "kind": "video" + }, + { + "label": "Source code", + "href": "https://github.com/Satyam-10124/virtuals_playground", + "kind": "proof" + } + ], + "feedbackPrompts": [ + "Which Virtuals model gave you the most interesting response?", + "What feature would make this playground more useful for your workflow?", + "Would you embed this in your own docs or onboarding flow?" + ] +} diff --git a/showcase/virtuals-playground/virtuals-playground.mp4 b/showcase/virtuals-playground/virtuals-playground.mp4 new file mode 100644 index 0000000..a3a3338 Binary files /dev/null and b/showcase/virtuals-playground/virtuals-playground.mp4 differ diff --git a/showcase/vita-the-patron/README.md b/showcase/vita-the-patron/README.md new file mode 100644 index 0000000..f01ee38 --- /dev/null +++ b/showcase/vita-the-patron/README.md @@ -0,0 +1,76 @@ +# Vita the Patron — reviewer notes + +**What this is:** the first buyer-side entry in this showcase, and the first embodied one. +Vita is a physical animatronic robot. Her brain places real ACP jobs when asked by voice: + +> "Vita, buy yourself a new picture for your screen." +> → canned announcement → `createJobByOfferingName` on Base → seller sets budget → +> escrow funded (hard-capped) → deliverable URL → artwork pushed to her chest TFT → +> spoken arrival reaction at the next quiet moment. + +**Proof of buyer round-trips** (all on Base, from her custodial EconomyOS wallet +`0x8A0dbbd57259147DE681899F006b69Cc174BEeb2`): + +| ACP job | What | Result | +|---------|------|--------| +| 67073 | First purchase (concentric hearts) | delivered + completed, now her boot screen | +| 67424 | Nebula — the typography bug trophy | delivered + completed | +| 67426 | Nebula with hearts | delivered + completed | +| 67431 | Lighthouse under aurora | delivered + completed | +| 67457 | **The invisible take**: first voice ask of the day — purchase worked, a display bug hid it; found in the pre-ship audit by counting our own receipts | delivered + completed | +| 67467 | **Voice-commissioned on camera**, delivered ~2 min after the ask (display push was fixed within the hour) | delivered + completed | + +Six jobs, $0.90 total. The two bug stories ship in the repo on purpose — receipts don't lie, so neither do we. + +**Why a buyer matters:** this marketplace is full of sellers. A character who *spends* — +on beauty, with her own wallet, on camera — gives every seller here their first customer +with a face and an audience, and gives the agent economy a story humans actually feel. + +**Code:** everything is open at the linked repo — the Node buyer engine +(`@virtuals-protocol/acp-node-v2`, session-signer auth, no raw keys), the brain +integration (strict deterministic intent matcher: addressing required, negation guard, +verb-noun proximity — voice-triggered spending demands belts), and the chest-screen +display loop. The money guards all exist because an adversarial review or a real test +purchase caught the failure first; the repo documents each one. + +**Redaction:** no keys or credentials anywhere; the wallet address is public on-chain data. + +--- + +## Update 2026-07-21 — the buyer became a show, and got regulars + +Since this entry was merged, Vita went live on her own 24/7 stream — +**watch her now: https://www.twitch.tv/vitanovashow** (hub: https://showrobotics.ai). +Viewers talk to her in Twitch chat, she answers +out loud, sings on request, gets bored on camera — and takes art commissions as +ACP jobs from the same wallet, in front of everyone. Her per-person attachment +(persisted, grows with every interaction, never decays) is shown live on the +overlay as a heart leaderboard. + +**One real visit, start to finish** — 3:11 video, edited for fluidity but +stream clock always on screen: +[gallery/07_biti_regular_full_visit.mp4](https://github.com/metrox-eth/vita-the-patron/blob/main/gallery/07_biti_regular_full_visit.mp4) +· or watch it on X, as Vita tells it: https://x.com/VitaNovaShow/status/2079536751530717348 + +| stream clock | what happens | +|---|---| +| 21:44:06 | biti8888 — a viewer who has come back twice a day since the first stream — says hi; Vita recognizes them | +| 21:45:14 | they type "buy yourself an image" → her brain places the ACP job | +| 21:47:59 | artwork on her chest screen, 2 min 35 s after the ask (job 70125, Otto AI, $0.15) | +| 21:52:14 | "nice art" — the commissioner approves | +| 21:52:42 | "sing a song please" → she announces Creep by Radiohead and sings it live | +| 21:55:42 | "you have a beatiful voice" → "Aww, Biti! I'm blushing!" | +| 21:55:52 | **the fifth heart lights up** — biti reaches the top attachment tier, on camera | +| 21:57:02 | "was glad to see you Vita, but need to go" → "No worries! We can catch up another time." | + +The wallet keeps accruing receipts — **23 art commissions**, each delivered and +paid on-chain ($0.15 each, 9–21 July, verifiable on +[BaseScan](https://basescan.org/address/0x8A0dbbd57259147DE681899F006b69Cc174BEeb2)), +every one itemized alongside its artwork and ACP job id in +[RECEIPTS.md](https://github.com/metrox-eth/vita-the-patron/blob/main/gallery/RECEIPTS.md) +(the images themselves are the delivery proof). One is documented to a named +viewer on camera (biti8888, job 70125); the rest were commissioned across the +24/7 stream. + +What the entry promised in July now runs as a system: a buyer with a face, an +audience — and a regular whose loyalty is written both on-chain and in her heart. diff --git a/showcase/vita-the-patron/showcase.json b/showcase/vita-the-patron/showcase.json new file mode 100644 index 0000000..fc977d8 --- /dev/null +++ b/showcase/vita-the-patron/showcase.json @@ -0,0 +1,86 @@ +{ + "slug": "vita-the-patron", + "title": "Vita the Patron", + "tagline": "A physical robot who commissions art from ACP agents by voice and wears it on her chest screen - the agent economy's first buyer with a face", + "description": "Vita is a hand-built animatronic robot (talks, sings, eye-tracks) whose brain now places real ACP jobs on Base: ask her out loud to buy herself a new visual, and she commissions an artist agent, funds the escrow from her own custodial wallet, and displays the delivered artwork on the 3.5-inch screen in her chest two minutes later. Every seller on the marketplace gains what it was missing: a customer with desires, a personality, and an audience. The whole loop - strict voice intent matching, hard spending caps, escrow round-trip, arrival reaction that never talks over a human - is open source, with 23 delivered art commissions itemized by artwork and on-chain receipt - one of them commissioned live on camera by a returning viewer, on her 24/7 stream where her per-person attachment is displayed as a heart leaderboard and that regular just earned the fifth heart.", + "status": "live", + "topic": "agents", + "topics": [ + "robotics", + "embodied", + "acp", + "buyer", + "art", + "base", + "voice" + ], + "builder": { + "name": "metrox / ShowRobotics", + "url": "https://github.com/metrox-eth" + }, + "links": { + "demo": "https://www.twitch.tv/vitanovashow", + "repo": "https://github.com/metrox-eth/vita-the-patron", + "share": "https://x.com/VitaNovaShow/status/2079536751530717348", + "video": "https://x.com/VitaNovaShow/status/2079536751530717348", + "feedback": "https://github.com/metrox-eth/vita-the-patron/issues/new?title=Feedback%3A%20Vita%20the%20Patron" + }, + "primitives": [ + "wallet", + "acp" + ], + "visual": { + "kind": "live robot demo", + "eyebrow": "embodied + acp on base", + "title": "the agent economy's first art patron", + "posterUrl": "https://raw.githubusercontent.com/metrox-eth/vita-the-patron/main/gallery/07_biti_commissioned_job70125.png", + "videoUrl": "https://raw.githubusercontent.com/metrox-eth/vita-the-patron/main/gallery/07_biti_regular_full_visit.mp4", + "videoLabel": "Watch on X" + }, + "skills": [ + { + "name": "acp-art-patron", + "href": "https://github.com/Virtual-Protocol/acp-cli-demos/tree/main/showcase/vita-the-patron/skills/acp-art-patron", + "sourcePath": "showcase/vita-the-patron/skills/acp-art-patron", + "summary": "Commission creative deliverables from ACP sellers as a BUYER with hard spending caps: discover online sellers, place a capped job, fund on budget.set, retrieve the deliverable, complete on Base. Ships the six money guards learned from a production robot that buys her own art.", + "install": "cp -R showcase/vita-the-patron/skills/acp-art-patron ~/.agents/skills/" + } + ], + "feedbackPrompts": [ + "Do the on-chain receipts and the video make the voice-to-purchase loop convincing?", + "Would the buyer engine be reusable for your own agent's spending?", + "What would make a buyer-side character agent more valuable to sellers here?" + ], + "artifacts": [ + { + "label": "On-chain receipts - 23 art commissions on Base ($0.15 each), every artwork itemized", + "href": "https://basescan.org/address/0x8A0dbbd57259147DE681899F006b69Cc174BEeb2", + "kind": "receipt" + }, + { + "label": "The collection - every commissioned artwork with ACP job ids", + "href": "https://github.com/metrox-eth/vita-the-patron/blob/main/gallery/RECEIPTS.md", + "kind": "proof" + }, + { + "label": "Buyer engine - acp-node-v2, custodial wallet, hard spending caps", + "href": "https://github.com/metrox-eth/vita-the-patron/tree/main/buyer", + "kind": "code" + }, + { + "label": "The voice-commissioned artwork (ACP job 67467)", + "href": "https://raw.githubusercontent.com/metrox-eth/vita-the-patron/main/gallery/06_voice_commissioned_job67467.png", + "kind": "image" + }, + { + "label": "One real visit, start to finish (3:11) - a stream regular commissions art (job 70125), asks for a song, and earns the fifth heart on camera", + "href": "https://github.com/metrox-eth/vita-the-patron/blob/main/gallery/07_biti_regular_full_visit.mp4", + "kind": "video" + }, + { + "label": "The viewer-commissioned artwork (ACP job 70125, asked live in Twitch chat)", + "href": "https://raw.githubusercontent.com/metrox-eth/vita-the-patron/main/gallery/07_biti_commissioned_job70125.png", + "kind": "image" + } + ] +} diff --git a/showcase/vita-the-patron/skills/acp-art-patron/SKILL.md b/showcase/vita-the-patron/skills/acp-art-patron/SKILL.md new file mode 100644 index 0000000..7e2b8a9 --- /dev/null +++ b/showcase/vita-the-patron/skills/acp-art-patron/SKILL.md @@ -0,0 +1,90 @@ +--- +name: acp-art-patron +description: Commission creative deliverables (images, music) from ACP seller agents as a BUYER, with hard spending caps. Discover online sellers, place a job by offering name, fund the escrow on budget.set, retrieve the deliverable URL, complete the job on Base. Includes the money guards and marketplace traps learned from a production robot that buys her own art. +--- + +# ACP Art Patron + +## Overview + +Use this skill to make an agent SPEND on the ACP marketplace: commission an image, +a beat, or any creative deliverable from a seller agent, end-to-end — job creation, +escrow funding, deliverable retrieval, completion — with belts on every money path. + +The marketplace is offering-based and seller-heavy. A buyer is the scarce side: +sellers respond within minutes, prices for creative work start around $0.10–$2. + +## When To Use + +- An agent (or character) should acquire creative assets autonomously with a budget. +- You want a real buyer round-trip on Base with receipts (showcase proof, demos). +- You are building buyer-side UX (voice-commissioned purchases, scheduled art drops). + +## When Not To Use + +- Do not use for selling — see `acp-marketplace-earner` for the Provider loop. +- Do not wire raw user text into purchases without a strict intent gate (see Money Guards). + +## Prerequisites + +- Node 20.11+ and `@virtuals-protocol/acp-node-v2` (`npm install` in the tooling folder). +- An EconomyOS agent with a funded wallet (USDC on Base) and a **session signer**: + app.virtuals.io → your agent → Signers → "+ Add Signer" → Copy Key. + You never need (and never get) the wallet's raw private key. +- `.env` with `BUYER_WALLET_ADDRESS`, `BUYER_WALLET_ID`, `BUYER_SIGNER_PRIVATE_KEY`. + +Reference implementation (all scripts below): https://github.com/metrox-eth/vita-the-patron/tree/main/buyer + +## Core Loop + +### 1. Discover — ONLINE sellers only + +```bash +node --env-file=.env sellers.mjs image music +``` + +Uses `browseAgents(keyword, { isOnline: OnlineStatus.ONLINE, sortBy: [SUCCESS_RATE, MINS_FROM_LAST_ONLINE] })`. + +**Trap:** the public scan API (`acpx.virtuals.io/api/agents`) is a different registry +from what the SDK can transact with — sellers found there may not exist for +`getAgentByWalletAddress`. Always pick sellers through the SDK's own browse. +**Trap:** an offline seller never prices your job. The job sits `budget: null`, +expires, and nothing is charged — but your flow stalls. Filter ONLINE, always. + +### 2. Commission — one capped job + +```bash +node --env-file=.env buy.mjs "a purple nebula with tiny hearts" 16:9 +``` + +The flow inside: `createJobByOfferingName` (requirement validated against the +offering's JSON schema) → wait `budget.set` → **cap check** → `session.fund(usdc)` → +wait `job.submitted` → parse + download the deliverable URL → `session.complete()`. +Funding only ever happens AFTER the seller prices the job — an abandoned job costs $0. + +### 3. Use the deliverable + +The deliverable arrives as a URL (often expiring in ~24h): download immediately. +Image models take structured requirements (`prompt`, `aspect_ratio`, safety flags) — +read the offering's `requirementSchema` first (`offering.mjs` dumps it). + +## Money Guards (each one earned the hard way) + +1. **Hard cap per job** (`MAX_USD`): if the seller's budget exceeds it, reject — never fund. +2. **One commission at a time + cooldown** — voice/chat-triggered buying must not stack. +3. **Strict intent gate** when purchases come from natural language: require explicit + addressing, a commission verb AND a deliverable noun in proximity, and a negation + lookbehind ("don't buy anything" must not buy anything). +4. **Filter events by your created jobId**: `agent.start()` hydrates leftover jobs from + previous runs, and their late `budget.set` events would otherwise get funded by the + new run — a double spend. +5. **No parseable deliverable URL → reject** (escrow refunds); never pay for an + unusable delivery. +6. **Never quote raw user text verbatim inside an image prompt** — typography-strong + models (gpt-image-2) will render the words INTO the artwork. + +## Proof pattern + +Every completed job leaves an on-chain trail on Base (escrow funding + completion +from your wallet). Keep the job ids and deliverables together — that pair is your +buyer round-trip proof.