Skip to content

feat(tools): fleet, dispatch and payment toolkits (#72) - #58

Open
yakimoto wants to merge 1 commit into
mainfrom
feat/fleet-tools-72
Open

feat(tools): fleet, dispatch and payment toolkits (#72)#58
yakimoto wants to merge 1 commit into
mainfrom
feat/fleet-tools-72

Conversation

@yakimoto

@yakimoto yakimoto commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Part of wave-av/wave-context#48. Delivers the adk half of wave-av/wave-context#72 (the mcp-server half is wave-av/mcp-server#63).

The ADK shipped 10 tools covering streams and live production — and nothing for the fleet products that are actually in production. This adds three toolkits, taking the registry to 17 tools.

Toolkit Tools Calls
FleetToolkit wave_speak, wave_transcribe, wave_caption POST /v1/{voice,transcribe,captions} via the gateway
DispatchToolkit wave_route, wave_list_routing_profiles Dispatch POST /, GET /profilesits own host
PaymentsToolkit wave_find_paid_services, wave_payment_schemes GET /v1/mpp/services, GET /v1/{x402,mpp}/facilitator/supportedpublic

Every path is grounded in the spoke's own router, not the API spec

wave-av/api-spec declares /voice/generate, /voice/voices, /voice/clone, /transcribe/{id}, /captions/{jobId}/download, /phone/*, /podcast/*none of which exist. Each spoke owns its whole /v1 namespace with an exact-match router and 404s anything else; the gateway forwards /v1/<product> verbatim with no rewriting. Building from the spec would have shipped tools that 404. Filed as wave-av/api-spec#33; the paths here come from git show origin/main:src/api.ts in each spoke.

Worth stating plainly: phone and podcast are not buildable. wave-phone-edge and wave-podcast-edge have no api.ts at all — their src/ is favicon.ts, landing.ts, tokens.css.ts, worker.ts. They are landing shims, and neither prefix appears in the gateway routing table. No tool is claimed for either.

Three decisions worth your attention

1. wave_speak gives code the bytes and a serialiser a receipt. POST /v1/voice returns audio/mpeg. Returning the bytes is right for an SDK — discarding a response body is lossy and the caller can't recover it. But agent frameworks routinely JSON.stringify a tool result straight into a model's context, where a megabyte of serialised byte array is useless to something that cannot listen. So VoiceResult holds the real Uint8Array on .audio, and its toJSON() emits { contentType, byteLength, usage } instead. No caller has to choose.

This is the concrete answer to the open question I raised on mcp-server#63 — in MCP the tool result is text so a receipt is all that's possible, but in a TypeScript SDK we can have both.

2. PaymentsToolkit takes no API key — not an optional one, none at all. Those gateway routes are public by design, because an agent has to discover what it can buy before it holds a WAVE key. The surest way to never send a credential to an endpoint that doesn't authenticate it is to have nothing to send. Only the read half is exposed; the facilitator's verify and settle are the money-moving side and are deliberately not wrapped.

3. A separate base URL and a separate fetch path, twice. api.wave.online fronts the product spokes and the payment rails; dispatch.wave.online fronts Dispatch, which is not behind the gateway. Reusing AgentToolkit.call() would also have forced Content-Type: application/json onto content-type-sensitive spokes and — see below — swallowed every error.

Two things this fixes on the way past

AgentTool and the MCP-shape mapper move to src/tools/shared.ts so all four toolkits emit identical MCP definitions. AgentToolkit re-exports AgentTool from its original path, so import { type AgentTool } from '@wave-av/adk' resolves exactly as before — the public API is unchanged, and the smoke check asserts it.

The new toolkits throw a typed WaveToolError carrying the status and the response body verbatim. AgentToolkit.call() never checks response.ok, so it returns error bodies as though they were successful results — that's pre-existing and out of scope here, but it's why wave_find_paid_services surfacing a real 503 (below) is the intended behaviour rather than a swallowed empty list. WaveToolError is also the export the README has been advertising from @wave-av/adk/tools without it existing.

Verification

npm run type-check   # exit 0
npm run lint         # exit 0 (eslint --max-warnings 0)
npm run build        # ESM + CJS build success

Smoke check against the built output (dist/index.js, so it proves what a consumer gets) — 33/33:

=== registry ===
  ✓ AgentToolkit (pre-existing): 10 tools    ✓ FleetToolkit: 3 tools
  ✓ DispatchToolkit: 2 tools                 ✓ PaymentsToolkit: 2 tools
  total tools: 17
=== MCP shape is identical across all four toolkits ===
  ✓ AgentToolkit MCP defs unchanged in shape — wave_create_stream
=== no credential can reach the public payment rails ===
  ✓ PaymentsToolkit holds no apiKey — {"baseUrl":"https://api.wave.online"}
=== VoiceResult returns bytes to code, a receipt to a serialiser ===
  ✓ result.audio holds the real bytes        ✓ JSON.stringify omits the bytes
=== usage headers: absent must be null, never 0 ===
  ✓ usageMinutes null when absent (not 0)    ✓ a genuine 0 remaining survives as 0
=== zod validation actually fires ===
  ✓ wave_transcribe rejects a malformed url before any network call
  ✓ wave_payment_schemes rejects an out-of-enum rail (no path injection)
SMOKE PASS

Live proof — the two public tools need no key, so they ran against production:

=== wave_payment_schemes rail=x402 ===
{"supportedNetworks":[{"networkId":"eip155:8453","version":"x402@1","asset":"0x833589…2913"},
                      {"networkId":"eip155:84532","version":"x402@1","asset":"0x036cbd…cf7e"}],
 "schemes":["exact"],"facilitatorUrl":"https://gateway.wave.online/v1/x402/facilitator"}

=== wave_find_paid_services q='video transcription' ===
WaveToolError 503: {"error":"discovery_unavailable","reason":"vectorize_unbound"}

That 503 is a real production gap, not a defect in this PR — MPP service discovery is dark because its Vectorize index isn't bound (wave-av/wave-gateway#730). The toolkit raising it as a typed error rather than swallowing it is the point.

Caveats — read these

  • The five authenticated tools are not proven end-to-end. That needs a live WAVE_API_KEY, and every product call is metered, so proving them bills real money. It should be done deliberately against a test org, not incidentally in a PR.
  • This repo's tests do not run. src/__tests__/*.test.ts import vitest, but vitest is absent from package-lock.json, there is no test script, and tsconfig.json excludes the test directory. So the gate here is type-check + lint + build + the out-of-repo smoke run above. I'm filing that separately rather than smuggling a test framework into this PR — and it is why I removed the words "and are covered by tests" from the Status prose in .wave/repo.json, which was not true.
  • dist/ is tracked but intentionally not updated here. It is committed at release time (prepublishOnly rebuilds it), not per feature.
  • The framework adapters still wrap only AgentToolkit, so the new tools aren't reachable from Mastra/LangGraph/LiveKit yet. Filing that as a follow-up.
  • CI is hard-down org-wide on Actions billing (wave-av/wave-rig#174), so checks cannot run on this PR.

.wave/repo.json

Updated so the generated README covers the new surface: three new capabilities, six new resolver-verified claims, a corrected tool count (10 → 17), and a "Working with audio" section. The README prose changes ride in this PR deliberately — merging it is the review.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Note

Medium Risk
Adds live production API clients (voice, payments discovery, Dispatch routing) with new error and auth semantics; framework adapters still only wrap AgentToolkit, so new tools aren’t reachable via Mastra/LangGraph/LiveKit yet.

Overview
Expands the ADK from 10 stream/production tools to 17 by adding FleetToolkit, DispatchToolkit, and PaymentsToolkit, with shared plumbing in src/tools/shared.ts and root exports from src/index.ts.

FleetToolkit adds wave_speak, wave_transcribe, and wave_caption against gateway paths grounded in spoke routers (POST /v1/voice, /v1/transcribe, /v1/captions). VoiceResult keeps real audio on .audio while toJSON() emits a usage receipt so serializers don’t dump megabytes into model context. STT tools pass a public url query param; calls use assertOk and WaveToolError instead of AgentToolkit.call()’s unchecked JSON parsing.

DispatchToolkit targets dispatch.wave.online (not api.wave.online) for wave_route and wave_list_routing_profiles. PaymentsToolkit exposes read-only x402/MPP discovery with no API key and no Authorization header; verify/settle are intentionally omitted.

AgentToolkit now delegates AgentTool, validated, and toMCPToolDefs to shared without changing the public AgentTool import path. .wave/repo.json documents the new capabilities, claims, tool count, and “Working with audio” guidance.

Reviewed by Cursor Bugbot for commit f31bbad. Configure here.


Summary by cubic

Add FleetToolkit, DispatchToolkit, and PaymentsToolkit to expose voice, transcription, captions, model routing, and payment discovery; the tool registry grows from 10 to 17 with paths grounded in live spokes and Dispatch (not the API spec).

  • New Features

    • FleetToolkit (gateway): wave_speak (POST /v1/voice), wave_transcribe (POST /v1/transcribe), wave_caption (POST /v1/captions). VoiceResult returns real audio bytes; toJSON() emits a small receipt. Usage/rate-limit headers surfaced via readUsage.
    • DispatchToolkit (own host): wave_route (POST /), wave_list_routing_profiles (GET /profiles). Defaults to https://dispatch.wave.online.
    • PaymentsToolkit (public, no key): wave_find_paid_services (GET /v1/mpp/services), wave_payment_schemes (GET /v1/{x402,mpp}/facilitator/supported). Sends no Authorization header by design.
    • All three toolkits throw WaveToolError on non-2xx (status/body preserved, isRateLimited for 429).
  • Refactors

    • Shared plumbing in src/tools/shared.ts: AgentTool, MCP mapper (toMCPToolDefs), WaveToolError, readUsage, validated. AgentToolkit re-exports types; MCP tool defs are identical across toolkits.
    • Public exports added in @wave-av/adk: new toolkits, types, and helpers.
    • Docs updated in .wave/repo.json to cover 17 tools and audio handling.

Written for commit f31bbad. Summary will update on new commits.

Review in cubic

Adds the shipped WAVE surface the ADK was missing: the voice/transcribe/captions product spokes (FleetToolkit), Dispatch model routing (DispatchToolkit), and the read half of the x402/MPP agent-payment rails (PaymentsToolkit). Registry goes 10 -> 17 tools.

Every path comes from each spoke router at origin/main, not api-spec/openapi.yaml, which over-declares endpoints that 404 in production (wave-av/api-spec#33).

AgentTool and the MCP-shape mapper move to src/tools/shared.ts so all four toolkits emit identical MCP definitions; AgentToolkit re-exports AgentTool from its original path, so the public API is unchanged.
@changeset-bot

changeset-bot Bot commented Jul 26, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: f31bbad

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_8f5f1c1f-9cbb-40fd-a286-7717a40ff437)

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 458117d5-0eac-457d-984a-efaca08d4918

📥 Commits

Reviewing files that changed from the base of the PR and between eb79119 and f31bbad.

📒 Files selected for processing (8)
  • .wave/repo.json
  • src/index.ts
  • src/tools/AgentToolkit.ts
  • src/tools/DispatchToolkit.ts
  • src/tools/FleetToolkit.ts
  • src/tools/PaymentsToolkit.ts
  • src/tools/index.ts
  • src/tools/shared.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/fleet-tools-72
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/fleet-tools-72

Comment @coderabbitai help to get the list of available commands.

@yakimoto

Copy link
Copy Markdown
Contributor Author

Flagging a problem with this PR's base that I found after opening it.

This repo is not the one that publishes @wave-av/adk. It sits at 1.0.2; npm serves 1.0.14, built from wave-av/sdkssdk-typescript/packages/adk. Full evidence and the decision needed are in wave-av/sdks#42.

Two consequences for this PR:

  1. Merging it would not ship these toolkits to anyone. The work is sound — 33/33 smoke checks, live-proved payment tools, type-check/lint/build clean — but it lands in a fork.
  2. One of its stated caveats is wrong because the fork is wrong. I wrote that dist/ here contains only index.js/index.cjs and that the subpath exports are unbuilt. That is true of this repo and false of the shipped package, which builds all six subpath entries. That claim comes from this repo's .wave/repo.json, which describes the fork rather than the product.

If sdks becomes the home (the likelier call), this needs re-basing onto sdk-typescript/packages/adk — mechanically straightforward, since the four new files are self-contained and the only edits to existing files are the AgentTool/toMCPTools extraction in AgentToolkit.ts and the two barrel exports. The .wave/repo.json half would need reworking against whatever SSOT sdks uses.

Not re-basing it unilaterally — that decision belongs with wave-av/sdks#42. Holding here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant