fix: align SDK clip + voice contracts with the live gateway - #67
fix: align SDK clip + voice contracts with the live gateway#67yakimoto wants to merge 4 commits into
Conversation
Verified against api.wave.online today: clips.create() sent a rejected
{ source: { type, id, start_time, end_time } } object; the gateway accepts
{ source: "<recording-id>", in: "5s", out: "10s" }. voice.synthesize() hit
/v1/voice/synthesize and expected a JSON job object; the gateway serves
POST /v1/voice returning audio/mpeg bytes directly.
- clips.ts: CreateClipRequest.source is now a recording-id string, plus
in/out relative time-string fields.
- clips-types.ts: ClipSource updated to the verified string+in/out shape.
- voice.ts: synthesize() now POSTs /v1/voice and returns ArrayBuffer bytes.
- voice-types.ts: SynthesizeRequest.voice_id now optional.
No auth/secret changes. Type-check clean for touched files (pre-existing
telemetry.ts require error is unrelated).
Bugbot couldn't run - usage limit reachedBugbot 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_bfc10709-cb64-4dc8-9c0a-ba48cb11bc50) |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 2 minutes Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
Comment |
ApprovabilityVerdict: Needs human review Breaking API changes to You can customize Macroscope's approvability policy. Learn more. |
PR Summary by QodoAlign clips + voice SDK contracts with live gateway responses
AI Description
Diagram
High-Level Assessment
Files changed (5)
|
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
There was a problem hiding this comment.
Risk: medium. Left a non-blocking comment — Cursor Bugbot and Cursor Security Agent did not complete successfully (skipped / usage limit), so approval is deferred for human review. No reviewers assigned because no non-author assignable reviewers were available.
Sent by Cursor Approval Agent: Pull Request Router and Approver
There was a problem hiding this comment.
🔍 Synthesis job helpers become unreachable now that synthesize returns raw bytes
synthesize() no longer returns a SynthesisResult with an id, so getSynthesis() (src/voice.ts:173), listSyntheses() (src/voice.ts:184) and waitForSynthesis() (src/voice.ts:233) have no in-SDK way to obtain a synthesis id, and they still target /v1/voice/synthesize/... — the very path the PR says the gateway does not implement for synthesis. synthesizeStream() (src/voice.ts:199) likewise still POSTs to /v1/voice/synthesize/stream with a bare fetch (no retries/WaveError). Worth confirming whether these endpoints exist on the live gateway; if not, they should be removed or re-pointed in the same contract-alignment pass.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Removing or deprecating the synthesis-job surface (getSynthesis, listSyntheses, waitForSynthesis, synthesizeStream) is a breaking scope expansion beyond this contract fix, and whether those endpoints exist on the live gateway cannot be verified without API credentials. Best handled as a follow-up by the author.
| export interface ClipSource { | ||
| type: 'stream' | 'recording' | 'upload'; | ||
| /** Recording id the clip is cut from */ | ||
| id: string; | ||
| start_time: number; | ||
| end_time: number; | ||
| /** Start offset as a time string, e.g. `"5s"` or `"2m"` */ | ||
| in: string; | ||
| /** End offset as a time string, e.g. `"10s"` or `"1m30s"` */ | ||
| out: string; | ||
| } |
There was a problem hiding this comment.
🔍 Response type reuses the request-side clip source shape
ClipSource was redefined as { id, in, out }, but it is only used on the response object Clip.source (src/clips-types.ts:43) — CreateClipRequest now uses a plain string plus top-level in/out. The PR verified the request shape against the live gateway; there is no evidence in the diff that the gateway returns { id, in, out } for clip.source. If the response still returns numeric start_time/end_time, consumers reading clip.source.in will get undefined at runtime while the types claim otherwise.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
The response-side shape of clip.source cannot be verified from this environment (no API credentials to probe the live gateway). The type follows the PR author's verified contract; if the gateway returns a different shape for clip.source, that needs the author's live verification to resolve.
Code Review by Qodo
1.
|
- Add responseType: 'arraybuffer' support to WaveClient request path so binary endpoints keep retries, rate-limit handling, timeouts, custom headers, and WaveError-typed failures - voice.synthesize() now forwards the full SynthesizeRequest (audio options were silently dropped) via client.post instead of a bare fetch - Update ClipsAPI JSDoc example and README quick-start to the new clip/voice contracts Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Qodo Fixer🍒 Ready to be cherry-picked — ✅ Merged (0) · ☑ Fixed (1) 🔗 Fix PR: #68 This fix PR was closed automatically. Its branch is preserved so you can cherry pick the changes into the original PR. Prompt for coding agent Process — 1 fixed
|
| /** | ||
| * Clip source reference. | ||
| * | ||
| * Live contract (verified against api.wave.online): `source` is the recording | ||
| * id as a string, with `in`/`out` relative time strings (`"5s"`, `"2m"`). | ||
| * The older `{ type, id, start_time, end_time }` object shape is rejected by | ||
| * the gateway on create. | ||
| */ | ||
| export interface ClipSource { | ||
| type: 'stream' | 'recording' | 'upload'; | ||
| /** Recording id the clip is cut from */ | ||
| id: string; | ||
| start_time: number; | ||
| end_time: number; | ||
| /** Start offset as a time string, e.g. `"5s"` or `"2m"` */ | ||
| in: string; | ||
| /** End offset as a time string, e.g. `"10s"` or `"1m30s"` */ | ||
| out: string; | ||
| } |
There was a problem hiding this comment.
🟡 Clip results describe their source with fields the service does not return
The description of where a clip came from was rewritten to hold two text offsets (in/out at src/clips-types.ts:30-37) even though this description is only ever used for clip data returned by the service, so code reading it can get fields that never arrive.
Impact: Users inspecting a returned clip's source get misleading typing and may read fields that are absent at runtime.
Mechanism: request-shape comment applied to a response-only type
ClipSource is only referenced by Clip.source (src/clips-types.ts:43), i.e. the response object. The create request no longer uses it at all — CreateClipRequest now carries source: string plus top-level in/out (src/clips.ts:55-60). The new JSDoc on ClipSource explicitly documents the create contract ("The older { type, id, start_time, end_time } object shape is rejected by the gateway on create"), which does not describe what the gateway returns for a clip. Additionally ListClipsParams.source_type (src/clips.ts:86) still filters by 'stream' | 'recording' | 'upload', a discriminator that no longer exists anywhere on the source type. Either the response shape should be verified and typed separately from the create shape, or Clip.source should be aligned with what the live gateway actually returns.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
What the gateway returns for clip.source cannot be verified from this environment (no API credentials), so retyping the response or removing the ListClipsParams.source_type filter would be a speculative breaking change; this needs the author's live verification, as noted on the earlier identical finding.
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
| async synthesize(request: SynthesizeRequest): Promise<ArrayBuffer> { | ||
| return this.client.post<ArrayBuffer>(this.basePath, request, { | ||
| headers: { Accept: 'audio/mpeg' }, | ||
| responseType: 'arraybuffer', | ||
| }); |
There was a problem hiding this comment.
🟡 Agent-authored pull request changes library source code although the repo only permits docs/config-only agent changes
This pull request modifies SDK source code (src/voice.ts:161-165, plus src/client.ts, src/clips.ts, src/clips-types.ts, src/voice-types.ts), while the repository rules limit agent-authored pull requests to documentation and configuration changes only.
Impact: The change falls outside the scope the repository owner allows for agent-authored contributions, so it should not be merged as-is.
Repository rule in CONTRIBUTING.md restricting agent PR scope
CONTRIBUTING.md states under "Agent-authored pull requests": "This repo accepts PRs opened by an AI coding agent (e.g. a Cursor Cloud Agent session), scoped today to docs/config-only changes." The commits in this PR (fix: route voice.synthesize through WaveClient and refresh stale docs, fix: remove unused ClipSource import in clips.ts) change runtime behavior of VoiceAPI.synthesize, the WaveClient request pipeline, and the clips request/type contract, which exceeds docs/config scope.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
This is a repo-governance concern, not a code defect: the PR was opened by the repo owner (Jake), who is also the required human reviewer for agent PRs per CONTRIBUTING.md, and the only way to "fix" it would be closing the PR, which is the owner's decision.
| /** | ||
| * Clip source reference. | ||
| * | ||
| * Live contract (verified against api.wave.online): `source` is the recording | ||
| * id as a string, with `in`/`out` relative time strings (`"5s"`, `"2m"`). | ||
| * The older `{ type, id, start_time, end_time }` object shape is rejected by | ||
| * the gateway on create. | ||
| */ |
There was a problem hiding this comment.
🔍 Committed dist/ artifacts still declare the old synthesize/clip contracts
dist/ is tracked in git (dist/voice.d.ts:213 still declares synthesize(...): Promise<SynthesisResult>; dist/clips.d.ts:25-39 still has the old ClipSource). Consumers reading the checked-in build output — or any tooling that resolves the package's types entry without a rebuild — will see the pre-change contract. Confirm the release pipeline rebuilds dist/ before publish, or refresh the committed artifacts.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Verified release.yml rebuilds dist/ (npm run build) and packs the fresh output before publishing, so published artifacts never carry the stale contracts; refreshing committed dist/ would add large generated churn and require a full local build outside this PR's scope.


The published SDK was out of sync with the live gateway. Verified today by probing api.wave.online:
clips.create()sent a rejected{ source: { type, id, start_time, end_time } }object. The gateway accepts{ source: "<recording-id>", in: "5s", out: "10s" }.voice.synthesize()POSTed/v1/voice/synthesizeand expected a JSON job object. The gateway servesPOST /v1/voicereturning rawaudio/mpegbytes directly (verified — got a real 51KB MP3).Changed:
CreateClipRequest(source string + in/out time strings),ClipSource,voice.synthesize()(returns ArrayBuffer),SynthesizeRequest.voice_idoptional. CHANGELOG updated.No auth/secret changes. Type-check clean for touched files.
Note
Medium Risk
Breaking public API changes: clip create payload and ClipSource types, plus voice.synthesize() return type and endpoint—callers expecting the old shapes or SynthesisResult need updates, though the change corrects behavior against the live gateway.
Overview
Aligns clips and voice client shapes with the live gateway at
api.wave.onlineso published SDK calls succeed instead of being rejected or hitting the wrong route.Clips:
CreateClipRequestno longer nests asourceobject withtype,start_time, andend_time. Create payloads now use a recording id string plus top-levelin/outtime strings (e.g."5s","2m").ClipSourceon clip resources is updated toid,in, andoutinstead of the old object fields.Voice:
synthesize()POSTs/v1/voice(not/v1/voice/synthesize) with{ text, voice_id? }, requestsaudio/mpeg, and returnsPromise<ArrayBuffer>instead of a JSONSynthesisResult.SynthesizeRequest.voice_idis optional. Job helpers likegetSynthesisare unchanged in this diff.CHANGELOG [Unreleased] documents the contract fix.
Reviewed by Cursor Bugbot for commit 92ebacd. Configure here.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is enabled.Note
Align SDK clip and voice contracts with the live gateway
clips.create()now expectssourceas a recording-id string with top-levelinandouttime strings, replacing the older discriminatedClipSourceobject shape.voice.synthesize()now POSTs to/v1/voice(was/v1/voice/synthesize), setsAccept: audio/mpeg, and returnsPromise<ArrayBuffer>of raw audio bytes instead of a JSON job object.RequestOptionsgains aresponseTypefield ('json' | 'arraybuffer') wired throughexecuteWithRetry()to support binary response parsing.CreateClipRequestshapes and handleArrayBufferinstead of a synthesis job object.Macroscope summarized 00f8ab7.