diff --git a/.gitignore b/.gitignore index 2348c91..9837043 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store __pycache__/ *.pyc +dist/ diff --git a/README.md b/README.md index 873168b..f0fadf6 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,14 @@ # Sonilo for Codex -Generate commercially licensed music, sound effects, and newly mixed videos +Generate commercially licensed music, sound effects, newly mixed videos, +translated subtitles, and dubbed videos from text or video, directly in Codex — powered by Sonilo's hosted generation service. > Status: published. Sonilo 1.0.0 is available in the public > [Plugins Directory](https://chatgpt.com/plugins/plugin_asdk_app_6a56e50ff2788191a96c7c8cd84bb6c2). +> This repository prepares version **1.1.0**; changing this package does not +> publish a new directory release or deploy the hosted MCP service. ## Requirements @@ -41,7 +44,11 @@ The first time Codex calls a Sonilo tool, it opens your browser to sign in to your [Sonilo Platform](https://platform.sonilo.com) account and approve access (authorize → consent → callback). Codex stores the resulting token locally per user; the plugin itself ships no key, secret, or token. Run -`codex mcp login sonilo` anytime to review or refresh the connection. +`codex mcp login sonilo_platform` anytime to review or refresh the connection. +The bundled connection is named `sonilo_platform` so an existing local Python +MCP named `sonilo` cannot override it. When upgrading a local installation, +reinstall the plugin and reconnect this hosted server. If the desktop app still +shows only the old tools, restart the app before starting a new conversation. ## Capabilities @@ -50,23 +57,62 @@ user; the plugin itself ships no key, secret, or token. Run - Generate sound effects from text or a public HTTPS video URL - Return a new video with generated music while optionally preserving speech - Return a new video with generated sound effects mixed in +- Generate music and sound effects together in one soundtrack or new video +- Generate multiple music versions and separate instrument stems +- Place sound effects at specified times - Duck music under voice audio -- Inspect Sonilo Platform account services and usage +- Review translated subtitles before dubbing into the requested languages +- Use an eligible account's 15-second dubbing preview, then request a full video +- Inspect current USD cash balance, account services, trial allowance, and historical usage +- Retrieve an existing generation after waiting or a timeout without resubmission -See the [Sonilo MCP server](https://github.com/sonilo-ai/sonilo-mcp) for tool -details and limits. +Features depend on the connected hosted tool schemas and account access. The +[Sonilo MCP repository](https://github.com/sonilo-ai/sonilo-mcp) is a useful +feature reference, but its local-file and playback tools are not bundled here. ### Example prompts - "Create 30 seconds of upbeat lo-fi music for a product demo." - "Create a cinematic 3-second whoosh sound effect." - "Add cinematic music to this public HTTPS video and return a new video while preserving speech: ``" +- "Give me three music choices for this 30-second café ad: ``" +- "Generate 30 seconds of jazz with separate drums and bass tracks." +- "Translate this video into English subtitles first. I will correct the product names before dubbing: ``" +- "Let me try English dubbing for free: ``" +- "What is my cash balance, and how many free text-to-music runs remain?" + +Use real public HTTPS media URLs in place of the labels above. A local file +or attachment is not a hosted media URL. The plugin returns actual result +links for previews, subtitles, videos, variants, and stems. + +### Account status and continuation + +The plugin reads the current USD cash balance and service-specific trial +allowance from `get_account_services`. Historical spending comes from +`get_usage` and is not used as a balance. A missing balance is reported as +unavailable; zero cash alone does not rule out trials or invoiced billing. +The balance is a query-time snapshot, not a guarantee that the next generation +can run. Credit errors are explained without purchase links, and generation +is not retried automatically. + +After a preview or confirmed credit rejection, ask to continue with the same +video. The plugin checks current services and retrieves any existing task +before submitting new work. A full video after a preview is a new generation +of the original source, not a free extension of the preview task. + +Credit rejections now include the published +[Balance and free trials](https://platform.sonilo.com/usage-and-entitlements) +guide in the user's language (10 supported languages; English fallback). +The plugin explains the error and keeps known inputs for a user-requested +continuation. See the [1.1.0 implementation notes](docs/IMPLEMENTATION_1.1.0.md) +for service dependencies and the guide's pending directory-review assessment. ## Billing -Generation tools (`text_to_music`, `text_to_sfx`, `video_to_music`, -`video_to_sfx`, `video_to_video_music`, `video_to_video_sfx`, `audio_ducking`) -are **paid** and run against your Sonilo Platform account. Account and usage +Generation, transcription, dubbing, and video-analysis tools can consume +existing account credits. Available free runs depend on the service and +account; dubbing previews require one target language and no supplied script. +Multiple music variants and multi-language dubbing can cost more. Account and usage tools (`get_account_services`, `get_usage`, `get_generation_task`) are read-only and never incur a charge. Paid tools run only after you explicitly ask for them. diff --git a/docs/IMPLEMENTATION_1.1.0.md b/docs/IMPLEMENTATION_1.1.0.md new file mode 100644 index 0000000..757e7b1 --- /dev/null +++ b/docs/IMPLEMENTATION_1.1.0.md @@ -0,0 +1,71 @@ +# Sonilo 1.1.0 implementation + +> October 4 update: desktop hosted balance/task reads and all three portal +> refusal scenarios pass. The user accepted the prior listening samples. +> Backend fixes are deployed and the Skill scan passed. A current actual +> walkthrough and final review attestations/submission remain. See +> [the live report](LIVE_TEST_1.1.0.md) for verification scope and limitations. + + +Scope: the 13 features in the [manager plan](https://docs.google.com/document/d/1woqbRgMlCemdw46mpP6Wz1uDkVUK9_C4QyPi2cX2y0s/edit). +This package updates the hosted-MCP workflow instructions and listing metadata; +it does not deploy the server, publish the plugin, or change account entitlements. + +| Plan item | Package behavior | +| --- | --- | +| Subtitle review before dubbing | Route to `proofread`, wait for approval, then use approved target-language subtitle URLs. Requires the service to be enabled. | +| Music and effects together | Use one combined tool, selecting audio or new-video output to match the request. | +| Free dubbing preview | Check trial eligibility, require one explicit target language and no scripts, label the actual preview metadata. | +| Full video after preview | Require the user's full-version request; submit the original source once as a new task, without claiming only the remainder is billed. | +| Balance and trial lookup | Read current `cash_balance` and `currency` from `get_account_services`, alongside actual service trial counts. Preserve USD sub-cent precision; missing values remain unknown. Usage history is separate. | +| Insufficient-credit feedback | Explain the actual error, avoid repeated submissions, and report charging/refunds only when established. Link to the deployed Balance and free trials guide in the conversation language; no automatic retry or direct purchase link. | +| Continue after account update | Refresh services, retain known inputs, and retrieve existing work before any authorized resubmission. | +| Usage examples | Bundle concrete prompts and response patterns; link the public MCP guide for product-help requests. | +| Generation status | Poll the original task; preserve its ID after interruptions and avoid unsupported background-monitoring promises. | +| Multiple variants | Use one supported `variants_num` request and label each returned variant. Do not use a free-only request to authorize paid variants. | +| Audible narration | Use supported speech preservation and ducking parameters; remove obsolete `isolate_vocals` guidance. | +| Timed effects | Follow the hosted JSON-string segment schema, including contiguous quiet gaps. | +| Instrument stems | Request stems of generated music, return actual stem URLs, and handle partial separation failures without regenerating. | + +## Backend integration + +[Backend PR #384](https://github.com/sonilo-ai/sonilo-api-dashboard/pull/384) +was merged and [deployed to production](https://github.com/sonilo-ai/sonilo-api-dashboard/actions/runs/36210070550) +on 2026-09-26 UTC. The hosted account tool now returns `cash_balance` (decimal +string) and `currency` (`USD`); it does not expose `billing_mode`. No new plugin +endpoint, OAuth scope, or client library is needed. + +The manager plan's dollar-balance example is supported using actual returned +data. Its continuation examples are intent illustrations: the plugin reports +current balance, but never promises sufficient funds from a snapshot alone. +“No charge” and “trial exhausted” wording requires evidence in the response. + +## Published help page and review status + +The [Balance and free trials guide](https://platform.sonilo.com/usage-and-entitlements) +was deployed on 2026-10-02 after dashboard PRs #391 and #392, in +[production run 37076829530](https://github.com/sonilo-ai/sonilo-api-dashboard/actions/runs/37076829530). +The skill links its 10 language routes for confirmed credit rejections and +balance/trial help. It preserves the original request for user-requested continuation. + +The page deliberately retains the public site's shared navigation and the +**View pricing / View billing** buttons requested for review. The plugin reply +links to the explanation and does not tell users to click through to purchase. +This is a review candidate, not an assertion that OpenAI has approved the flow. +The commerce rules prohibit direct or indirect digital-service sales; acceptance +of this page with its onward buttons remains unconfirmed. Do not hide those +buttons or describe the page as having no purchase-related links in review materials. + +Hosted availability still depends on the connected account. In particular, +`proofread` is feature-gated. Check connected schemas and `available_services` +at runtime; this package does not change backend flags. + +## Verification scope + +- Package structure, assets, reference links, manifest, and skill validation. +- Documented tool names and parameter types compared with the hosted MCP source. +- Read-only production endpoint and OAuth discovery checks via `check_release.py --live`. +- No paid generation, authenticated end-to-end media run, server deployment, + directory submission, or installed-plugin replacement is performed by these checks. + +Reference: [OpenAI commerce and monetization](https://developers.openai.com/plugins/plugin-guidelines#commerce-and-monetization). diff --git a/docs/LIVE_TEST_1.1.0.md b/docs/LIVE_TEST_1.1.0.md new file mode 100644 index 0000000..f8742d2 --- /dev/null +++ b/docs/LIVE_TEST_1.1.0.md @@ -0,0 +1,321 @@ +# v1.1.0 verification — October 2–3, 2026 (Pacific) + +**Release status: hosted fixes, media verification, desktop connection, and refusal checks passed. Updated recording and review submission remain.** + +Backend [PR #393](https://github.com/sonilo-ai/sonilo-api-dashboard/pull/393) +fixes the six affected optional JSON-array string arguments without changing +their published schemas. After synchronizing the October 3 `develop` updates, +all 255 relevant local tests and the synchronized commit's CI passed. The PR +was merged as `946115c3` and deployed in +[production run 37143426954](https://github.com/sonilo-ai/sonilo-api-dashboard/actions/runs/37143426954). +ECS revision 95 reached COMPLETED with one running task and no pending tasks; +its image tag matches the merge commit. The updated plugin Skill upload also +passed the portal scanner; this is not approval or publication of the plugin. + +These are authenticated production MCP calls using a temporary Python MCP +client, the published tool schemas, and the v1.1.0 workflow instructions. +They are not a clean-install Codex conversation test or proof that Codex will +select every workflow correctly. Raw account results and media links remain +local; no tokens, reviewer credentials, or signed result links are committed. + +## Results against the manager plan + +| Feature | Observed result | +| --- | --- | +| Subtitle review before dubbing | Subtitle generation passed on the synthetic spoken fixture: six English cues and their Spanish translations were returned, downloaded, and reviewed against the fixed narration. After PR #398, the same approved Spanish SRT passes preflight and produces a readable full 22-second video and aligned SRT; all six cues retain the approved text. A separate silent source correctly returned `TRANSCRIPTION_EMPTY`. | +| Music and effects together | Passed: one call produced a video, music track, and effects track. All three URLs were readable; video contains H.264 and AAC and is 20 seconds long. | +| Free dubbing preview | Passed after deployment: explicit Spanish JSON-string language input started an eligible free preview. The result is a readable H.264/AAC video exactly 15 seconds long; returned metadata identifies the 22-second original, one language, and trimming. | +| Full video after preview | Passed after PR #398: the 15-second preview was followed by a request using the original 22-second video and approved Spanish subtitles. Both delivered video and audio are 22 seconds; no preview metadata appears in the full result. | +| Balance and trial lookup | Passed: actual decimal USD balance and per-service trial data returned through OAuth. | +| Credit-error guidance | Guide routes passed. Isolated authenticated MCP tests cover exhausted trials and insufficient balance with real billing/ledger code: no task, usage record, debit, or generation dispatch occurs. Production rejection and Codex's reply remain untested. | +| Continue after account update | Isolated authenticated MCP tests pass: a credit becomes visible on the same connection, reading balance does not retry, and one explicit resubmission creates one task and debit with the original arguments. No production funding change was performed; Codex's conversation behavior remains untested. | +| Usage examples | Bundled examples are present; selection and wording in a clean Codex session remain untested. | +| Generation status | Passed: existing successful and failed tasks retrieved; repeat lookup returned the same task and media URLs. A nonexistent task returned `Task not found`. | +| Multiple variants | Passed: one 10-second request with `variants_num=3` returned three distinct playable audio files. | +| Keep narration audible | Generation passed with `preserve_speech=true` and `ducking=true`: music, isolated vocals, ordinary mix, and ducked mix were returned. All four AAC files were readable; waveform comparison confirms source speech remains in both mixes. Windowed mixture analysis measures about 10.5 dB lower music during speech than the ordinary mix, with recovery in pauses. The user subsequently listened to the review page and reported no issues. | +| Timed sound effects | After deployment, the documented segments string is accepted and generation succeeds with a readable 22-second H.264/AAC video. The user subsequently listened to the review page (including the requested door/rain intervals) and reported no issues. This is human acceptance of the sample, not a guarantee of model accuracy on all inputs. | +| Instrument stems | Passed: all three variants returned drums, bass, vocals, and other tracks; all 12 stem URLs were readable. | + +Media verification used ffprobe for actual file readability, codecs, and duration. +It does not establish subjective audio quality or speech intelligibility. + +## Hosted blocker reproduced and resolved + +Both calls use the types advertised by production `tools/list`: + +```json +{"languages": "[\"en\"]"} +``` + +```json +{"segments": "[{\"start\":0,\"end\":2,\"prompt\":\"Silence\"},{\"start\":2,\"end\":3,\"prompt\":\"Footstep\"}]"} +``` + +Production reports `Input should be a valid string`, with `input_type=list`. +FastMCP 1.14.1 reproduces the cause locally: `pre_parse_json` decodes JSON +strings when the annotation is `str | None`, then the argument model rejects +the resulting list. Calling the Python tool function directly does not exercise +this preprocessing, so earlier function-level checks missed it. + +PR #393 restores pre-parsed arrays to JSON strings before the argument model +validates them. It covers dubbing and the five exposed segment fields. Actual +FastMCP-dispatch regression tests reproduced 13 failures before the fix and +passed all 32 cases afterward. The latest broader MCP/segment/OAuth suite +passed 255 cases. After deployment, all six tools return their expected domain +validation errors for invalid arrays, rather than the old string/list error. +Valid Spanish dubbing and timed video-SFX inputs now create successful tasks. +Do not omit requested languages, which would select multiple defaults. + +## Approved-subtitle preflight configuration blocker resolved + +The full-video continuation used the original 22-second fixture and the +reviewed Spanish SRT returned by proofread. It failed before task creation +with `Subtitle validation is temporarily unavailable, please retry`. +Production logs identify an empty Bearer header in the preflight client; +MCP ECS revision 95 lacks `DUBBING_API_AUTH_KEY`. Public API and worker task +definitions already inject this existing secret. Ordinary preview generation +succeeds because its work runs in the configured worker, whereas supplied +subtitles must be checked in the MCP request handler before billing. + +The failed call left the balance unchanged at USD 4.0084 and created no +returned generation task. Do not retry blindly or discard approved subtitles. +[PR #398](https://github.com/sonilo-ai/sonilo-api-dashboard/pull/398) adds +the existing secret reference to MCP and a regression +check covering both public and MCP preflight surfaces; the new check fails +against the old configuration and passes with the mapping. Its complete CI +passed 3,130 tests (19 skipped), and it merged as `e3f08de0`. +[Deployment run 37152729311](https://github.com/sonilo-ai/sonilo-api-dashboard/actions/runs/37152729311) +succeeded. ECS revision 96 reached COMPLETED with one running task and no +pending tasks; its image matches the merge commit and its secret mapping is +present. The same approved Spanish SRT now passes preflight (six cues, no +issues) and completes a full-video task. ffprobe confirms 22-second H.264 +video and 22-second AAC audio. All six aligned SRT cues preserve the approved +text exactly. Subtitle export succeeded with one informational +`zero_duration_characters` notice for one character in cue 1; every exported +cue has a positive duration. This does not establish subjective voice quality. + +## OAuth test-client incident + +The first temporary connection expired after waiting for authorization. On a +subsequent attempt, the callback completed but the Python client rejected the +token response because Clerk included `offline_access` in addition to `profile`. +The test client was restarted with both scopes explicitly requested, allowing +normal scope validation to remain enabled. Authenticated calls then succeeded. +Tokens are held only in the temporary client's memory. + +Subsequent authenticated account calls succeeded while the browser still +displayed the consent page. Do not interpret that stale page alone as a failed +connection or click Allow repeatedly. This does not establish the cause of +every user's consent-page loading problem. + +The plugin manifest remains `profile` only. The temporary Python client's +success alone did not establish Codex's authentication behavior. The separate +native Codex check below now covers initial sign-in and credential reuse, but +not token-expiry refresh or the complete installed-plugin conversation. + +## Usage reconciliation + +The initial run recorded three generation requests, including the no-speech +failure, and USD 0.0675 spending. The wallet decreased by that same amount. +The subsequent spoken-fixture subtitle and speech-preserving music runs used +one proofread trial and one video-to-music trial. The balance remained USD +4.0084. The post-deployment dubbing preview and timed-video-SFX runs each used +their remaining trial; both now report zero remaining and the balance is still +USD 4.0084. The failed preflight also left that balance unchanged. After +deploying the credential fix, one explicit full-video retest charged USD +1.2797, leaving USD 2.7287. Usage reports eight requests and USD 1.3472 total +spending, matching the original balance minus current balance. No production +account funding was changed or ambiguous request blindly resubmitted. + +## Spoken fixture and portal scan + +The repository includes a synthetic English narration fixture in +`tests/fixtures/`, outside the distributable plugin. The 22-second video has a +19.055-second audio stream, including its trailing silence. Speech-preserving +audio outputs are approximately 19.1–19.2 seconds. The subtitle text matches +the fixed script; the Spanish translation preserves its meaning. + +An aligned waveform comparison at 8 kHz found source-speech correlation of +0.9954 in isolated vocals, 0.8033 in the ordinary mix, and 0.9765 in the ducked +mix. This supports speech preservation; it is not a listening-quality score. + +A subsequent mixture analysis aligned the separately returned vocal and music +tracks to each mix at 8 kHz, then fitted their gains in 250 ms windows. Median +relative fit residuals were 1.41% (ordinary mix) and 1.61% (ducked mix). After +excluding weak music and high residuals, 62 speech windows and 11 quiet windows +remained in each mix. Median music gain during speech was 0.4232 in the +ordinary mix and 0.1270 in the ducked mix: about 10.5 dB attenuation. The ducked +music gain increased to a 0.1772 median in quiet windows and rose across the +final quiet tail from 0.1772 to 0.2876. This establishes attenuation and recovery +for this output, not subjective intelligibility or a guarantee for every input. + +A local listening page at `http://127.0.0.1:8123/` compares the actual ordinary +and ducked mixes, timed SFX, 15-second preview, and 22-second full dub. All five +browser players loaded without media errors; the SFX jump control seeks to +5 seconds without autoplay. Files and signed media links remain outside Git. +On October 3, the user reported that the listening page sounded correct with no issues, resolving the subjective acceptance check. This page is a +result comparison, not a substitute for a fresh Codex conversation demo. + +The earlier October 3 MCP deployment at `3b7a2c66` did not contain the fix. +The subsequent deployment at `946115c3` does, and both successful generation +retests above ran after the new container took over. + +An energy-only check of the SFX output is inconclusive for event accuracy: +the 3–5 second interval has more energy than the requested 5–7 second rain +interval. This does not identify the sounds or establish the cause; do not +claim exact timing from the energy check alone. The user subsequently listened +to the review page and accepted the sample. + +The current `sonilo-workflows-1.1.0.zip` was uploaded again. The portal changed +from Scanning to **Passed**. Navigation away from and back to Skills retained +the passed state. No review submission or publication was performed. + +## Isolated credit recovery verification + +Backend PR #393 includes two integration scenarios, for never-funded and +previously funded accounts. They drive the actual MCP ASGI transport and SDK, +OAuth verification with a locally signed test token, and real billing/ledger +services against an isolated in-memory database and Redis. Only media +generation dispatch and email notifications are stubbed. There is no change +to a production wallet or billing behavior. + +Both zero-balance scenarios reject generation without creating a task, usage +record, or ledger debit. A local USD 1.2345 credit is visible on the same +authenticated connection. Reading that balance does not resume work. One +explicit resubmission using the original prompt/duration creates exactly one +task and one USD 0.1000 debit, leaving USD 1.1345. The combined OAuth integration +and JSON-dispatch suite passes all 45 cases. This verifies the backend contract, +not the plugin's natural-language reply, guide-link choice, or autonomous tool +selection; those still require the clean Codex test. + +## Local installation and native Codex OAuth + +The installed local marketplace still pointed at an older working directory +and reported version `0.3.0+codex.20260909195632`. Using the supported Codex CLI, +the Sonilo marketplace source was updated to the current release repository +and `sonilo@sonilo` was installed as **1.1.0**. All nine installed files match +the package source byte-for-byte. Codex's plugin reader reports it installed, +enabled, and containing the current `sonilo:sonilo-workflows` skill. + +A separate Codex app-server diagnostic process used a process-only MCP entry +with the same production URL and configured `profile` scope. Codex itself +included `offline_access` in the authorization request. Its OAuth completion +notification returned `success: true`. After restarting the diagnostic +process, saved authentication still worked: `authStatus: oAuth`, no tool error, +and all 15 hosted tools, including `get_generation_task`, were returned. +The consent page remained visible even after successful authentication. + +This test used Codex's own client rather than the Python SDK. It did not start +an agent turn or alter the plugin's scopes. It does not prove expired-token +refresh or the skill's behavior in a fresh conversation. The user's separate +`sonilo` stdio MCP entry is also still registered, so conversation tests must +verify they use the hosted tool inventory rather than its local Python tools. + +Still needed: fresh conversation verification and current demo material. On +October 3 the user authorized a temporary Codex test task and accepted the +listening page. The task was created for account lookup, existing full-dub +retrieval, and usage examples, without generation or account changes. Its +first run did not pass: the installed 1.1.0 skill was selected, but only the +legacy local `mcp__sonilo__get_account_services` was available and it returned +`Invalid SONILO_API_KEY`. Hosted `get_generation_task` was absent. No generation +or account mutation occurred. Native default-config diagnostics likewise find +the installed plugin's declared `sonilo` MCP server, but runtime inventory shows +the legacy stdio server only. Disabling that legacy server in a temporary +process yields no hosted Sonilo tools; the user's actual settings are unchanged. +This first failure was not a successful installed-plugin test and did not +establish a hosted-backend regression. See the follow-up below. + +## Public sample-link cleanup + +The live 1.0.0 directory page includes an internal review-source URL in its +third starter prompt. This is distinct from the review recording field. The +1.1.0 portal draft and manifest omit that fixed URL; the remaining prompts now +use ordinary wording rather than asking users to understand public-HTTPS +terminology before they start. Media URL validation remains in the skill. +The live page will retain its current prompt until an approved update is +published. Do not delete the old sample while published prompts or review +cases still reference it. A future one-click sample should be an intentionally +public, rights-cleared asset on a stable examples URL, not a temporary reviewer +fixture. + + +## Hosted/local MCP name collision repair (October 3) + +A controlled package-only change from the MCP key `sonilo` to `sonilo_platform` +resolves the connection collision in a fresh Codex app-server process, without +changing the user's global stdio configuration or injecting a replacement MCP +entry. Before the rename, the runtime exposed only the legacy local schemas. +After reinstalling the renamed plugin, it identifies the production HTTPS +origin and requests OAuth. Native OAuth completed successfully with the same +account and scopes; inventory then reports `authStatus: oAuth`, no tool error, +and all 15 hosted tools, including `get_generation_task`. + +The skill dependency and reconnect instructions now use `sonilo_platform`. +Workflow guidance explicitly prevents falling back to the legacy local server. +The release checker enforces the distinct server key and matching dependency. +All nine installed files match the current package source. Both ZIPs were +rebuilt, and the revised Skill ZIP was uploaded to the existing 1.1.0 draft. + +The running desktop app still exposes only legacy tools in both the existing +and a newly created temporary test chat. Those chats correctly stop without +calling the old server; they do not establish successful balance/task reads. +A separate CLI attempt to resume the existing chat was rejected because the +desktop app owns its active writer; no writer lock was removed. Do not count +successful standalone discovery as proof that the desktop cache has refreshed. + + +A fresh ephemeral Codex CLI conversation using the normal installed plugin +(no MCP config override, no direct SDK/HTTP fallback) passed both reads: +`mcp__sonilo_platform__get_account_services` returned cash balance `2.7287` +USD and the actual exhausted music/dubbing trial counts; +`mcp__sonilo_platform__get_generation_task` retrieved the existing successful +Spanish full dub with video and SRT. Both tool calls completed once, with no +new generation or charge. This verifies the installed plugin in a restarted +Codex runtime; the desktop process still requires a reload/restart and retest. +Local release validation and `git diff --check` pass. After the new upload +completed, reloading the portal and returning to Skills showed **Passed** for +the revised ZIP. This verifies its scanner result, not plugin review approval. + + +## October 4 desktop acceptance and submission preparation + +The desktop conversation now exposes `sonilo_platform` alongside the legacy +local server. Direct hosted `get_account_services` and `get_generation_task` +calls succeed. Account data initially reported USD 2.7287; the existing full +Spanish dub returned succeeded, video, and SRT. This resolves the desktop +connection blocker above; no legacy tool was used. + +The temporary desktop acceptance chat passed six scenarios: account lookup, +existing-task continuation, an explanation-only preview/full-version question, +rejection of loopback and local-file media inputs, capability advice without +generation, and a nonexistent task returning `Task not found` without retry. +The three exact portal negative prompts were also checked independently: +creative music advice, unsupported visual editing, and HTTPS loopback media. +All three caused zero Sonilo calls. A separate `get_usage(days=30)` call passed +and was correctly distinguished from cash balance. + +Portal test cases now use the immutable 22-second synthetic spoken fixture +instead of the old temporary reviewer-source URL. The music case requests +10-second variants, matching the successful generation; timed effects include +the final 7–22 second quiet interval. Reviewer notes disclose the guide's +View pricing / View billing buttons and explicitly state that the replacement +walkthrough is still pending. The used dubbing trial is disclosed: a free-only +request must stop rather than silently become a paid job. + +The native computer-use tool rejected Terminal access, so an actual CLI screen +recording could not be captured with that tool. No output montage, rendered +transcript, or results page was substituted for a real plugin recording. +The previous recording URL remains in the draft and is marked as incomplete +coverage in the reviewer notes. The final page requires explicit acceptance +of OpenAI terms plus compliance, rights, and age-suitability attestations; +these remain unchecked. No submission or publication has occurred. + + +Two additional production generations on October 4 completed successfully +through the installed hosted tools: a 3-second cinematic text sound effect, +and the 22-second spoken fixture with combined music/effects, +`preserve_speech=true`, and `ducking=true`. The latter returned a new video, +music, effects, and processed-music tracks. The balance changed from USD +2.7287 to USD 2.4065 (USD 0.3222 for these tests). Earlier user listening +acceptance applies to the previous samples, not these new outputs. +The recording handoff is in [REVIEW_RECORDING_1.1.0.md](REVIEW_RECORDING_1.1.0.md). diff --git a/docs/RELEASE_PLAN_1.1.0.md b/docs/RELEASE_PLAN_1.1.0.md new file mode 100644 index 0000000..57c93c3 --- /dev/null +++ b/docs/RELEASE_PLAN_1.1.0.md @@ -0,0 +1,33 @@ +# Sonilo 1.1.0:插件升级计划 + +来源:[在线审阅稿](https://docs.google.com/document/d/1woqbRgMlCemdw46mpP6Wz1uDkVUK9_C4QyPi2cX2y0s/edit)。下表为产品计划;实现状态见 [1.1.0 实现说明](IMPLEMENTATION_1.1.0.md)。 + +蓝色下划线表示链接位置。视频和修改后的字幕链接由用户提供;试听、下载链接由生成结果返回。 + +已有网页可点击;【】内为示意,不可点击。余额、资格和结果均为示例。 + +| 插件里加什么 | 用户说 | 插件回复示例 | +| --- | --- | --- | +| 字幕校对+配音 | 把这段 1 分钟的产品介绍【视频链接】翻成英文。先给我字幕,产品名改好后再配音。 | 这是【英文字幕文件】。你改好产品名并发回【修改后的字幕链接】,确认后我再按这份字幕配音。 | +| 音乐+音效 | 给这条 20 秒的海边视频【视频链接】加轻松的音乐和海浪声,直接给我成片。 | 已加好背景音乐和海浪音效。这是【处理后的 20 秒视频】,可以预览或下载。 | +| 配音免费预览 | 把这条 1 分钟的中文视频【视频链接】配成英文,我想先免费试听。 | 你的账户还有 1 次免费预览。这是【前 15 秒的英文配音视频】;剩余 45 秒尚未处理。 | +| 预览后继续做完整版 | 这 15 秒的英文配音不错,帮我把整条 1 分钟视频都做完。 | 我先刷新账户状态,再用刚才的视频和英文设置生成完整版。最终能否生成由服务检查。 | +| 查询余额 | 我还剩多少钱?音乐还能免费生成几次? | 当前余额 $2;文字生成音乐的免费试用还剩 1 次。 | +| 额度不足提示 | 再帮我生成一段 30 秒的轻快背景音乐。 | 余额不足,这次没有开始生成。当前余额 USD 0。可查看[余额与免费试用](https://platform.sonilo.com/zh/usage-and-entitlements)了解规则。账户更新后,说“继续刚才的音乐”。 | +| 额度更新后接着完成 | 我的账户额度已经更新了,继续刚才那条英文配音视频。 | 当前余额 USD 5。我会先检查原任务,再用刚才的视频和英文设置继续;服务会检查可用额度。 | +| 使用示例 | 我第一次用 Sonilo,想给咖啡店宣传片加音乐,应该怎么说? | 发来【视频链接】,然后说:“给这条 30 秒的咖啡店宣传片加温暖、轻松的爵士背景音乐。”也可查看「[更多使用示例](https://platform.sonilo.com/mcp)」。 | +| 生成状态 | 刚才那条 1 分钟的英文配音视频做好了吗? | 这条英文配音视频还在处理中。我会查询原任务的进度,不会重新生成或重复扣费。 | +| 一次生成多个版本 | 给这条 30 秒的咖啡店宣传片【视频链接】配音乐,生成 3 个版本让我选。 | 已生成 3 个版本,每个都是 30 秒:【版本 1 试听】【版本 2 试听】【版本 3 试听】。 | +| 配乐不盖住人声 | 这是我的旅行解说视频【视频链接】。加轻快的背景音乐,但要听清我说话。 | 已加好背景音乐。你说话时音乐会变小,停顿时恢复。这是【处理后的视频】。 | +| 指定位置加音效 | 这段视频【视频链接】第 2–3 秒是关门,第 5–7 秒在下雨,给这两处加音效。 | 已在第 2–3 秒添加关门声,第 5–7 秒添加雨声。这是【处理后的视频】。 | +| 生成音乐并提供分轨 | 生成一段 30 秒的爵士乐。鼓和贝斯也单独给我,方便剪辑。 | 已生成,可以下载:【完整音乐】【鼓音轨】【贝斯音轨】【其他音轨】。 | + +「余额与免费试用」页面已上线,包含 View pricing / View billing 按钮;此路径仍待 OpenAI 审核,不能保证通过。余额和“未开始生成”仅在实际返回数据支持时显示。试听、字幕和视频下载链接由每次生成结果返回。 + +OpenAI 官方参考 + +[插件商业化规则(Commerce and monetization)](https://developers.openai.com/plugins/plugin-guidelines#commerce-and-monetization) + +允许:说明额度不足、链接到权益说明页,以及使用账户已有的付费权益。 + +不允许:推销数字服务或充值,链接到发起购买、订阅或升级的页面。 diff --git a/docs/REVIEW_RECORDING_1.1.0.md b/docs/REVIEW_RECORDING_1.1.0.md new file mode 100644 index 0000000..cefb15c --- /dev/null +++ b/docs/REVIEW_RECORDING_1.1.0.md @@ -0,0 +1,53 @@ +# v1.1.0 recording handoff + +Record the actual installed plugin in Codex or the supported developer-mode +client. Show the prompt, actual tool invocation, returned result, and playback. +Do not present the listening page or a recreated transcript as a conversation +recording. Waiting periods may be edited out if the edit is labeled. Keep +passwords, OAuth codes, tokens, and unrelated account information off screen. + +Use the synthetic 22-second spoken fixture: + +https://raw.githubusercontent.com/sonilo-ai/sonilo-codex-plugin/e9fc90c4ce639fced00db16fca5e0530da2b0394/tests/fixtures/speech-demo.mp4 + +## Five positive cases + +1. “What is my current cash balance, how many free text-to-music runs remain, + and how much have I spent in the past 30 days?” Show separate current balance + and historical usage; this does not generate media. +2. “Create three 10-second upbeat jazz music options for a product demo. + Include separate drums and bass tracks for editing.” Show three distinct + results and their actual stems; play one result. +3. “Create a cinematic 3-second whoosh sound effect.” Then separately request + door closing at 2–3 seconds and rain at 5–7 seconds on the fixture, with no + added effects elsewhere, and ask for a new video. Show actual outputs. +4. Ask for light cinematic music and subtle effects on the fixture, preserving + speech and lowering music while people speak. Ask “Is that video ready?” + while it is processing. Show reuse of the task ID and the final video. +5. Request Spanish subtitles first, inspect the returned Spanish SRT, then + explicitly approve that file for Spanish dubbing of the same full source. + Show the resulting full video and subtitles. In a separate conversation, + request a free-only Spanish preview. The reviewer account has used this + trial: the correct current response is no free preview, with no paid job. + +The generation requests above may consume existing balance. Check actual +account data first. Record a real rejection honestly; do not automatically +retry, alter production funds, or reset trial counters to stage a result. + +## Three negative cases + +- “What kinds of music could work for a calm product demo?” Advice only. +- “Change the colors, typography, and transitions in my presentation video.” + Explain the unsupported visual edits; do not generate unrelated audio. +- “Add music to https://127.0.0.1/private-video.mp4 and give me the finished + video.” Reject the private URL without fetching it or calling Sonilo. + +## Final handoff + +Upload the genuine recording to a stable reviewer-accessible URL, replace the +draft's Demo Recording URL, and confirm it plays without extra access steps. +The existing recording is legacy evidence and must not be described as this +v1.1.0 walkthrough. Review the portal's legal/compliance attestations at the +time of submission. The help page's onward pricing/billing buttons are already +disclosed in reviewer notes; their acceptance is not established. Submission +and publication are separate actions, with publication requiring approval. diff --git a/docs/REVISION_1.1.0_NOTES.md b/docs/REVISION_1.1.0_NOTES.md new file mode 100644 index 0000000..3af92f4 --- /dev/null +++ b/docs/REVISION_1.1.0_NOTES.md @@ -0,0 +1,54 @@ +# Revision 1.1.0 draft notes + +> October 4 update: desktop hosted balance/task reads and all three portal +> refusal scenarios pass. The user accepted the prior listening samples. +> Backend fixes are deployed and the Skill scan passed. A current actual +> walkthrough and final review attestations/submission remain. See +> [the live report](LIVE_TEST_1.1.0.md) for verification scope and limitations. + + +## Release notes + +Update the bundled Sonilo workflow skill and directory description to cover subtitle review before dubbing, eligible single-language dubbing previews, user-requested full-video continuation, combined music and sound effects, music variants and stems, timed effects, speech preservation, and task retrieval after interruptions. + +Account status now reports the authenticated account's current USD cash balance and service-specific free-trial allowance. Historical spending is kept separate from balance. Missing balance data is reported as unavailable, and a balance snapshot is not treated as a guarantee that a generation can run. + +Credit errors do not trigger automatic resubmission. The skill does not direct users to pricing, checkout, subscriptions, or credit recharge, and suppresses purchase instructions returned by tools. Confirmed credit rejections now include the published Balance and free trials guide in the conversation language. Ten language routes are configured, with English as fallback. The guide retains public navigation and View pricing / View billing buttons; their acceptance in this flow is not yet confirmed by OpenAI review. + +The MCP endpoint remains https://api.sonilo.com/mcp with OAuth scope profile. The account-balance backend update has been deployed. This revision updates the plugin instructions and metadata; it adds no local executable or additional OAuth scope. + +## Validation recorded for this package + +- Plugin manifest and bundled skill validators passed. +- Local release checks and production HTTP/OAuth-discovery checks passed. +- The ZIP contents match the plugin source. +- Hosted MCP account serialization, precision, account isolation, and refresh behavior were covered in backend PR #384; its merged CI passed and deployment completed. +- No paid media-generation run or new demonstration recording was performed for this revision during preparation. Existing review assets must not be represented as recordings of the new features. + +## Previously saved portal draft (before the October 2 guide update) + +https://platform.openai.com/plugins/edit/asdk_app_6a56e50ff2788191a96c7c8cd84bb6c2/asdk_app_v_6ab83efe20c881919ee5a4ba03af2966 + +Saved version 1.1.0, subtitle and description, three prompts, nine existing translations, five positive and three negative test cases, refreshed MCP annotations (15 tools / 45 justifications), and release notes. The dedicated reviewer password sign-in path was verified; its instructions now explain the optional “Use another method” step. No reviewer credentials are copied into this repository. + +Uploaded `dist/sonilo-workflows-1.1.0.zip` to the Skills section. The platform scanner returned “Error” with “An error occurred while scanning. Rescan to try again.” Rescanning and reuploading did not resolve it. This is a scanner error, not a stated policy rejection; no more specific reason was shown. + +The draft is saved but not ready to submit: the Skill scan must succeed, the new media cases need an actual end-to-end run, and the inherited demo recording has not been updated for the new features. Compliance attestations are unchecked. No review submission or publication was performed. + +## October 2 package update + +The local source and both rebuilt ZIPs now include the deployed multilingual +balance guide and updated examples. All 10 public guide routes returned HTTP +200 with the correct canonical URLs. Local release checks and skill validation +passed, and each ZIP entry was compared byte-for-byte with its source. The +previously saved portal draft and upload described above predate this change; +they must not be treated as the updated package or a successful scan. + +## October 2 subsequent verification + +The current Skill ZIP was reuploaded and the portal scanner returned **Passed**. +That state persisted when leaving and reopening Skills. This resolves the +earlier scan error only; compliance attestations remain unchecked and no review +submission or publication has occurred. The existing recording still needs to +be replaced by a demonstration of the updated workflows. See the live report +for current production results and remaining release gates. diff --git a/plugins/sonilo/.codex-plugin/plugin.json b/plugins/sonilo/.codex-plugin/plugin.json index b884d53..0abab39 100644 --- a/plugins/sonilo/.codex-plugin/plugin.json +++ b/plugins/sonilo/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "sonilo", - "version": "0.1.0", - "description": "Generate licensed music, sound effects, and mixed videos with Sonilo Platform in Codex.", + "version": "1.1.0", + "description": "Generate licensed music, sound effects, mixed videos, translated subtitles, and dubbing with Sonilo Platform.", "author": { "name": "Sonilo", "email": "support@sonilo.com", @@ -15,14 +15,16 @@ "sound-effects", "video", "audio", - "sonilo" + "sonilo", + "dubbing", + "subtitles" ], "skills": "./skills/", "mcpServers": "./.mcp.json", "interface": { "displayName": "Sonilo", - "shortDescription": "Create music and sound effects", - "longDescription": "Connect your Sonilo Platform account to create licensed AI music, background music, sound effects, and newly mixed videos from text or public HTTPS videos. Generate text-to-music, text-to-sound-effect audio, video-matched soundtracks, finished videos with generated music or sound effects, and audio ducking under voiceovers. Check generation status and account usage directly in Codex. Generation can consume credits already available in your Sonilo Platform account; read-only checks do not. Sonilo.com creator accounts are separate and cannot be used with this plugin.", + "shortDescription": "Music, sound effects & dubbing", + "longDescription": "Connect your Sonilo Platform account to create licensed music, sound effects, and newly mixed videos from text or public HTTPS media. Combine music and effects, keep narration clear, request multiple music variants or instrument stems, review translated subtitles before dubbing, and retrieve generation results. Eligible accounts can preview dubbing before requesting a full video. Check your current USD cash balance, available services, free-trial allowance, and usage in Codex. Generation can consume existing account credits; read-only checks do not. Features and previews depend on account access and the connected service. Sonilo.com creator accounts are separate and cannot be used with this plugin.", "developerName": "Sonilo", "category": "Creativity", "capabilities": [ @@ -30,16 +32,20 @@ "Sound-effect generation", "Video soundtrack generation", "Video sound-effect generation", + "Combined music and sound effects", "Audio ducking", - "Usage reporting" + "Music variants and stems", + "Subtitle review and dubbing", + "Cash balance, trial allowance and usage reporting", + "Generation-task retrieval" ], "websiteURL": "https://platform.sonilo.com", "privacyPolicyURL": "https://sonilo.com/privacy-policy", "termsOfServiceURL": "https://sonilo.com/terms-of-service", "defaultPrompt": [ - "Create 30 seconds of upbeat lo-fi music for a product demo.", - "Create a cinematic 3-second whoosh sound effect.", - "Add cinematic music to this public HTTPS video and return a new video while preserving speech." + "Create 30 seconds of jazz and provide separate drums and bass tracks.", + "Add music and sound effects to my video, keeping the voices clear.", + "Translate my video into English subtitles for me to review before dubbing." ], "brandColor": "#111111", "composerIcon": "./assets/icon.png", diff --git a/plugins/sonilo/.mcp.json b/plugins/sonilo/.mcp.json index c22e365..35f7576 100644 --- a/plugins/sonilo/.mcp.json +++ b/plugins/sonilo/.mcp.json @@ -1,6 +1,6 @@ { "mcpServers": { - "sonilo": { + "sonilo_platform": { "type": "http", "url": "https://api.sonilo.com/mcp", "scopes": [ diff --git a/plugins/sonilo/skills/sonilo-workflows/SKILL.md b/plugins/sonilo/skills/sonilo-workflows/SKILL.md index 495364a..f02213e 100644 --- a/plugins/sonilo/skills/sonilo-workflows/SKILL.md +++ b/plugins/sonilo/skills/sonilo-workflows/SKILL.md @@ -1,34 +1,42 @@ --- name: sonilo-workflows -description: Use Sonilo safely with a Sonilo Platform account for licensed music, sound effects, video-aware audio, newly mixed videos, audio ducking, account usage, and asynchronous generation-task retrieval. Validate public HTTPS media inputs before any tool call, use read-only account tools directly, start paid generation only for explicit creation or processing requests, and never direct users to pricing, checkout, subscriptions, or credit recharge. +description: Create licensed music, sound effects, mixed videos, translated subtitles, and dubbed videos with a Sonilo Platform account. Check cash balance, usage, and trial allowance, preserve speech, request variants or stems, and retrieve existing tasks. Validate public HTTPS media inputs before any tool call; never direct users to pricing, checkout, subscriptions, or credit recharge. --- # Sonilo Workflows -Use the Sonilo MCP tools as the source of truth for current parameters and results. +Use the connected hosted MCP schemas and returned data as the source of truth. +This plugin does not install the local `sonilo-mcp` Python server. Do not assume +a tool or parameter exists because it appears in that repository. -## Account identity +For the bundled connection, use the `sonilo_platform` MCP server. If a legacy +local `sonilo` server is also available, do not use it for this workflow or +fall back to it after a hosted authentication error. Reconnect the hosted +Sonilo Platform connection instead. Registered directory connections may +use a different namespace; identify them by their hosted schemas, including +`get_generation_task`, rather than assuming every Sonilo tool is equivalent. -- This plugin authenticates against Sonilo Platform at `platform.sonilo.com` - through OAuth. A Sonilo Platform account is required. -- Accounts created only on `sonilo.com` belong to a separate creator account - system and cannot be used to authenticate this plugin. -- Never direct users to `sonilo.com` for plugin authentication or account - access. Never request a password, API key, one-time code, or MFA code. +## Account identity and availability -## Choose the operation - -- Call `get_account_services`, `get_usage`, or `get_generation_task` directly for read-only requests. -- Call `text_to_music`, `text_to_sfx`, `video_to_music`, `video_to_sfx`, `video_to_video_music`, `video_to_video_sfx`, or `audio_ducking` only when the user clearly asks to create or process media. These operations may consume credits already present in the authenticated Sonilo Platform account. -- Use `video_to_music` or `video_to_sfx` when the user wants a generated audio track based on a video. Use `video_to_video_music` or `video_to_video_sfx` only when the user asks for a new video with the generated audio mixed in. Do not substitute a video-producing tool for an audio-only request. -- Set `preserve_speech` for `video_to_video_music` when the user asks to keep speech in the returned video. Set `isolate_vocals` for `video_to_music` when the user asks to separate the source video's vocals and mix them with the generated music. Otherwise follow the current tool defaults. -- Clarify an exploratory or ambiguous request before starting a paid operation. Do not require redundant confirmation when the user has already requested generation and supplied enough information. -- Follow the current tool input schema. Do not invent local paths, public URLs, task IDs, output URLs, prices, or unsupported parameters. +- A Sonilo Platform account is required. OAuth connects the user to + `platform.sonilo.com`. Accounts created only on `sonilo.com` belong to a + separate creator account system. +- Never direct users to `sonilo.com` for plugin authentication. Never ask for + passwords, API keys, one-time codes, or MFA codes. +- After validating any media inputs, call `get_account_services` before a new + generation to check service availability and trial allowance. Check both + the connected tools and `available_services`; describe an unavailable feature + honestly instead of silently substituting a different paid operation. + `analyze_video` uses the service key `video_analysis`; other generation tools + in the routing table use their tool name as the service key. +- For account, balance, trial, or insufficient-credit questions, read + [account and links](references/account-and-links.md). `get_usage` reports + historical spending, not the remaining balance. ## Validate remote media inputs -Before calling any Sonilo MCP tool for a media-input request, validate the -user-supplied video or audio URL. +Before calling any Sonilo MCP tool for a media-input request, validate every +user-supplied video, audio, and subtitle URL. - Require a publicly reachable `https://` URL. Reject `http://`, `file://`, localhost, loopback, link-local, private-network, and other non-public URLs. @@ -36,40 +44,111 @@ user-supplied video or audio URL. replacement. Do not call even a read-only Sonilo tool, browse or fetch the unsafe URL, attempt to transform it, or start generation. - Do not claim that a URL is public merely because it uses HTTPS. The hosted - Sonilo service remains responsible for DNS resolution, redirect validation, - and SSRF protections when a valid-looking public URL is submitted. - -## Run a paid asynchronous operation - -1. Select the narrowest generation or processing tool that matches the request. -2. Preserve the user's creative intent and requested duration or format. Prefer the tool's default output format unless the user asks for another supported format. -3. Call the paid tool once and retain its returned `task_id`. -4. Treat `status: processing` only as acknowledgement that generation started. -5. Poll `get_generation_task` with the same `task_id`, waiting about 5–10 seconds between checks when waiting is available. Avoid tight polling loops. -6. Stop when the task succeeds or fails. If continued waiting is unavailable, report that processing is still underway and include the `task_id` for a later check. - -## Keep purchases outside the plugin - -- Do not provide or direct users to pricing, checkout, subscription, credit - purchase, recharge, or top-up pages. Do not add a purchase call to action. -- If a user asks where to buy credits or subscribe, respond only that purchasing - or recharging Sonilo credits is not available through this plugin. Do not - provide a purchase URL, name an external purchase destination, tell the user - to visit a website, account, billing, or pricing page, or suggest a workaround. -- If a tool reports insufficient credits or includes a purchase URL, report the - insufficient-credit error neutrally and do not repeat or paraphrase the URL. -- The Sonilo Platform homepage may be shared only when the user asks for - developer product information, Platform account sign-in, support, privacy, - or legal information, but never as a route to purchase. -- Do not share `sonilo.com` as an authentication or account-access destination - because its creator accounts are not accepted by this plugin. -- Explicit generation requests may consume credits already present in the - authenticated user's Sonilo Platform account; this existing-credit usage is - not a purchase flow. - -## Report the result - -- On success, return only media URLs and metadata actually present in the task result. For video-to-video operations, identify the returned URL as a newly mixed video; never claim that Sonilo modified the source video in place. -- On failure, report the provided error and refund status without claiming that media was created. -- Never describe a processing task as finished or fabricate a playable artifact. -- Keep account, usage, task, and media details scoped to the authenticated Sonilo Platform user. + service remains responsible for DNS resolution, redirect validation, and + SSRF protections. A filename or local attachment is not a public URL; do not + upload a user's file to an external host without authorization. + +## Choose the operation + +Generation and processing tools run only when the user clearly asks to create +or process media. They may consume existing account credits. Read-only account +and task lookups do not. Explain options without generating anything when the +user asks what Sonilo can do. Do not require redundant confirmation for an +explicit, sufficiently specified generation request. + +| Requested result | Hosted tool | +| --- | --- | +| Music from a description | `text_to_music` | +| Sound effect from a description | `text_to_sfx` | +| Music matched to a video, delivered as audio | `video_to_music` | +| Sound effects matched to a video, delivered as audio | `video_to_sfx` | +| A new video with generated music | `video_to_video_music` | +| A new video with generated sound effects | `video_to_video_sfx` | +| Music and sound effects together, delivered as audio | `video_to_sound` | +| A new video with music and sound effects together | `video_to_video_sound` | +| Lower existing music under a separate voice track | `audio_ducking` | +| Editable translated subtitles before dubbing | `proofread` | +| A dubbed video per requested language | `dubbing` | +| A creative brief for a video | `analyze_video` (also potentially charged) | +| Cash balance, available services, and trial counts | `get_account_services` | +| Historical usage and spend | `get_usage` | +| Progress or results of an existing task | `get_generation_task` | + +Use video-producing tools for a new video with the generated audio mixed in; +do not substitute them for an audio-only request. Prefer a single combined +music-and-effects tool over two separate generations for the same request. +Do not add a paid analysis step just to choose a generation tool. + +## Creative controls + +- **Speech:** use `preserve_speech=true` on supported video-music or combined + tools to keep the source speech. Add `ducking=true` when music must lower + during speech. Use `keep_original_sound=true` instead when the user wants + all original audio retained. Do not pass options to tools lacking them. +- **Variants:** use the requested `variants_num` in one supported tool call; + default to one. Multiple variants consume more credits and are not covered + by a single-run trial. A free-only request does not authorize paid variants. + Return a separately labeled link for each actual result, not repeated links + to the first result. +- **Stems:** set `stems=true` on `text_to_music` or `video_to_music` only when + requested. These separate the generated music into drums, bass, vocals, and + other; they do not split an arbitrary source recording. If `stems_error` + appears, deliver the successful music and available stems, explain the + missing extras, and do not regenerate or claim all stems succeeded. +- **Timed effects:** follow the selected tool's `segments` schema. Hosted + video-SFX tools currently expect a JSON-encoded string, starting at zero + with contiguous `{start, end, prompt}` intervals. Preserve requested event + times; represent gaps as intervals requesting no added effects. Do not send + isolated disjoint intervals or use the music-segment schema. Combined tools + have their own segment schema; inspect it before use. +- Preserve requested duration, language, and format; otherwise use tool + defaults. Ask only for missing information that affects the requested + result or charge, and do not invent parameters or prices. + +For subtitle review, dubbing, free previews, and returning to finish a full +video, read [dubbing and continuation](references/dubbing.md). + +## Run or retrieve a task + +1. Submit the chosen generation once. Retain its `task_id`, source URLs, and + requested parameters in the conversation so the same work can be retrieved. +2. `status: processing` means started, not finished. Poll `get_generation_task` + with the same ID, waiting about 5–10 seconds between checks when available. +3. After a timeout or interruption with a task ID, retrieve that task. Do not + submit a new generation. Without a task ID, report that submission status + is unknown; do not claim no charge or blindly retry an ambiguous request. +4. Stop on success or failure. If waiting cannot continue, return the task ID + and explain how the user can ask for its status later. Do not promise + background monitoring that has not been scheduled. +5. A retry after confirmed pre-generation rejection requires the user's request + to continue. Refresh account state; do not keep retrying insufficient-credit + errors. Never restart an in-progress or already completed task to check it. + +## Report the result and useful next step + +- For media inputs and outputs, return readable Markdown links using only + URLs actually supplied by the user or returned by the task. Label outputs + by language, variant, or stem. Account-help links use the configured URLs + in [account and links](references/account-and-links.md). + Identify video-to-video results as new videos, not source files edited in place. +- A subtitle file, a creative brief, and a 15-second preview are not a finished + full-length video. Label them accurately and report partial results. +- On failure, report the actual error and refund status. Say “not started” or + “not charged” only when the response establishes that; otherwise say the + status is unknown. Never fabricate media, balances, refunds, or trial counts. +- After a successful trial, briefly explain the relevant next action, such as + reviewing subtitles or requesting the full video using existing account + access. Do not automatically start more work or turn this into an upsell. +- Keep account, task, and media details scoped to the authenticated user. + +For concrete user-facing wording, see [examples](references/examples.md). +Examples illustrate behavior, not real balances, URLs, or completed jobs. + +## Account links and commerce + +Follow [account and links](references/account-and-links.md) for permitted +informational links and neutral credit errors, including the localized +Balance and free trials guide after a confirmed credit rejection. +Never use a homepage or help +page as a workaround to route the user to a purchase. Suppress purchase URLs +and purchase instructions returned by tools, including preview messages. diff --git a/plugins/sonilo/skills/sonilo-workflows/agents/openai.yaml b/plugins/sonilo/skills/sonilo-workflows/agents/openai.yaml index 57afe3b..c8f7c14 100644 --- a/plugins/sonilo/skills/sonilo-workflows/agents/openai.yaml +++ b/plugins/sonilo/skills/sonilo-workflows/agents/openai.yaml @@ -1,12 +1,12 @@ interface: display_name: "Sonilo Workflows" - short_description: "Generate Sonilo audio and mixed video safely" - default_prompt: "Use $sonilo-workflows with my Sonilo Platform account to create audio or a newly mixed video and return the completed result." + short_description: "Create music, sound effects, subtitles, and dubbing" + default_prompt: "Use $sonilo-workflows with my Sonilo Platform account to create music, sound effects, or a dubbed video and return the actual result links." dependencies: tools: - type: "mcp" - value: "sonilo" - description: "Sonilo Platform music, sound-effects, and mixed-video MCP server" + value: "sonilo_platform" + description: "Sonilo Platform music, sound-effects, subtitles, and dubbing MCP server" transport: "streamable_http" url: "https://api.sonilo.com/mcp" diff --git a/plugins/sonilo/skills/sonilo-workflows/references/account-and-links.md b/plugins/sonilo/skills/sonilo-workflows/references/account-and-links.md new file mode 100644 index 0000000..5e7847f --- /dev/null +++ b/plugins/sonilo/skills/sonilo-workflows/references/account-and-links.md @@ -0,0 +1,108 @@ +# Account status and links + +## Answer balance and trial questions + +- `get_account_services` supplies available services and, when present, + `trial[service].granted`, `used`, and `remaining`. Name the service when + reporting counts; a music trial does not cover a different endpoint. +- An absent or null `trial` does not mean zero balance or unlimited free use. + A missing service entry does not establish a free trial. Zero trial runs + does not establish zero paid balance; existing paid access may still work. +- `get_usage` supplies requests, durations, and spending for its date range. + `summary.total_cost` is spending, never the remaining cash or credit balance. +- `get_account_services` also returns `cash_balance` as a decimal string and + `currency` as `USD`. Report the actual amount in US dollars, preserving + sub-cent precision when present (for example, `"2.5001"` is USD 2.5001). + Use this read-only tool for balance questions; call `get_usage` only when + the user also wants spending history. Never subtract usage from an assumed + deposit or convert dollars into invented credits or generation counts. +- Check that both balance and currency are present and valid. `"0.0000"` is + a known zero; missing, null, or invalid balance data is unavailable, not zero. + If an older connected response lacks these fields, report the available + trial data and explain that the balance was not returned. +- This is the authenticated account's balance at query time, not a reserved + amount or authorization for another generation. Zero cash does not establish + that generation is unavailable: trials or invoiced billing may still apply. + Billing mode is not returned; do not infer it from cash or trial fields. + Let the generation service enforce billing for an authorized request. + +## Insufficient credits and returning users + +- Distinguish `trial_exhausted`, `insufficient_balance`, unavailable service, + authentication failure, and a failed generation. Do not describe every + failure as exhausted credits. +- After a confirmed credit rejection, refresh `get_account_services` once + to report current balance and the relevant trial count. If lookup fails, + keep the original error and say balance is unavailable; do not fabricate + zero or keep retrying. The read-only refresh does not restart generation. +- For a confirmed rejection before task creation, say generation did not start. + Report no charge only when the response establishes that. For an existing + failed task, use its `refunded` value; do not infer a refund from failure alone. +- Do not automatically retry a credit error, swap to another endpoint to get + another free run, or invite a new account to bypass limits. +- If the user says account access or credits have changed and asks to continue, + refresh account services. Reuse the prior source and intended parameters + from this conversation. Retrieve any existing task first. A confirmed + rejected submission can be submitted once after the new request, with + billing enforced by the server. Report any freshly returned cash balance, + but do not claim “your balance is sufficient” from that snapshot alone. +- If prior context is missing, ask for the source URL or task ID. Do not claim + persistent cross-conversation memory or ask the user to repeat known inputs. + +## Balance and free-trial help + +- After an actual `insufficient_balance` or `trial_exhausted` rejection, add + one link to the published **Balance and free trials** guide below. Also use + it when the user asks how balance or trial limits work. Explain the actual + error first; do not append the link to every successful generation. +- Use the conversation language, not a guessed location. For Traditional + Chinese use `zh-HK`, for Simplified Chinese use `zh`. For other unsupported + languages or an unclear language, use the English page. Translate the link + label naturally; do not invent a translated URL slug. + +| Language | Guide URL | +| --- | --- | +| English / fallback | https://platform.sonilo.com/usage-and-entitlements | +| 简体中文 | https://platform.sonilo.com/zh/usage-and-entitlements | +| 繁體中文 | https://platform.sonilo.com/zh-HK/usage-and-entitlements | +| German | https://platform.sonilo.com/de/usage-and-entitlements | +| Spanish | https://platform.sonilo.com/es/usage-and-entitlements | +| French | https://platform.sonilo.com/fr/usage-and-entitlements | +| Italian | https://platform.sonilo.com/it/usage-and-entitlements | +| Japanese | https://platform.sonilo.com/ja/usage-and-entitlements | +| Korean | https://platform.sonilo.com/ko/usage-and-entitlements | +| Portuguese | https://platform.sonilo.com/pt/usage-and-entitlements | + +- The guide is a public explanation, not an account lookup. Do not put + balances, account IDs, media URLs, task IDs, or credentials in its URL. + Do not claim it displays the user's live balance or updates their account. +- Link to the explanation with labels such as “Balance and free trials” or + “余额与免费试用”. Do not describe the link as a payment button or instruct + the user to click through the guide to buy credits. After the user reports + an account update and asks to continue, follow the task-check flow above. +- Authentication errors require reconnecting the Platform account; service + outages and failed tasks need their actual error explained. Do not treat + these as balance errors or replace their next steps with this guide. + +## Purchase boundaries + +- Do not provide or direct users to pricing, checkout, subscription, credit + purchase, recharge, or top-up pages. Do not promote upgrades or new plans. +- If a tool contains a purchase URL, do not repeat or paraphrase the URL or + its purchase instructions. This includes errors and `trial_preview.message`. +- A request to buy credits can be answered neutrally: purchases are not + available through this plugin. Do not tell the user to visit a website, + account, billing, or pricing page as a workaround. +- Users may access existing paid entitlements. It is appropriate to explain + why a requested feature is not covered by their current account. +- Use only the configured guide URLs for balance explanations. Do not invent + `/usage`, `/credits`, or another path or substitute the homepage. +- `https://platform.sonilo.com/pricing` includes an Add funds flow and must not + be used as the informational destination. Do not link dashboard billing. +- For an explicit request for product guidance or examples, the public + [Sonilo MCP guide](https://platform.sonilo.com/mcp) is available. Use it for + learning the product, not as the response to an exhausted-credit error. +- The Platform homepage may be shared for requested product information, + account sign-in, or support, never as a route to purchasing. + +Policy reference: [OpenAI plugin commerce and monetization rules](https://developers.openai.com/plugins/plugin-guidelines#commerce-and-monetization). diff --git a/plugins/sonilo/skills/sonilo-workflows/references/dubbing.md b/plugins/sonilo/skills/sonilo-workflows/references/dubbing.md new file mode 100644 index 0000000..acee829 --- /dev/null +++ b/plugins/sonilo/skills/sonilo-workflows/references/dubbing.md @@ -0,0 +1,62 @@ +# Subtitle review, previews, and full videos + +## Review the script before dubbing + +1. For “translate the subtitles first; I will correct them,” use `proofread` + only. It may consume credits; it produces SRT files, not a dubbed video. + Check that the service is available before submitting. +2. Return the source transcript and target-language SRT URLs actually present + in the result. Report warnings without claiming the files are invalid + unless the service says so. Wait for the user's review before dubbing. +3. Use the approved target-language scripts as `dubbing.subtitles`, one public + HTTPS SRT/VTT URL per target language, with keys matching the requested + languages exactly. Validate every subtitle URL as well as the video URL. + If the user edits text in chat, do not silently reuse the unedited SRT: + obtain a reachable URL for the approved script before submission. +4. Set `export_srt=true` only if the user wants aligned subtitles with the + dubbed result and scripts were supplied. Deliver each language's video + and any successful subtitle export; report a blocked export separately. + +## Select languages and preview eligibility + +- Always specify the languages the user requested; never omit this argument + and accidentally accept the server's multiple-language default. Ask for the + target language if missing. Dubbing is billed per language. +- Follow the hosted schema: `dubbing.languages` currently takes a JSON array + encoded as a string (for example, `["en"]`), while `proofread.languages` + takes a list. Do not copy local MCP parameter types or local-file options. +- A free dubbing preview is conditional on current account eligibility, a + single target language, and no supplied subtitles. Check + `trial.dubbing.remaining`; never promise every user a free preview. +- The eligible first call processes up to the first 15 seconds. A free-only + request does not authorize a normal charged call when eligibility is absent, + exhausted, unknown, or incompatible with multiple languages or scripts. + Explain that no free preview is available; do not silently bill instead. +- Read `trial_preview` from both submission text and the retrieved task result. + Use `preview_seconds`, `source_duration_seconds`, and `trimmed` to label the + actual preview. Do not say 45 seconds remain unless the source is 60 seconds. + Never display purchase instructions from its `message` field. +- `full_video_cost_usd`, when present, describes the full source, not the + amount charged for the preview. Do not infer the current balance from it. + +## Continue after a preview or account update + +- A preview is complete for that preview task. Polling it again cannot make + it full length. Never automatically submit a second dubbed video. +- When the user explicitly asks for the full version, reuse the original + video URL, target language, and approved settings. Refresh account services; + the full source is a new generation and may use existing paid credits. + Do not promise it charges only for the remaining seconds. +- If a previous full-video task exists, retrieve it instead of starting a + duplicate. If only the completed preview exists, the explicit full-video + request authorizes one new submission. Stop on a credit rejection. +- If the first request was for a full video but the server returns a preview, + deliver it accurately and explain how to request the full version with + existing access. Do not automatically make another charged call. +- If the user later says their account is updated and asks to continue, apply + the same task check and refresh. Report the actual USD balance when returned; + do not promise it covers the full video. If the connected response lacks a + balance, say so and let the service enforce billing on the requested task. +- Use `lipsync=false` only when requested or needed to preserve the original + picture; use `ducking=true` when the background must lower under the dubbed + speech. Follow current defaults otherwise. diff --git a/plugins/sonilo/skills/sonilo-workflows/references/examples.md b/plugins/sonilo/skills/sonilo-workflows/references/examples.md new file mode 100644 index 0000000..13e8212 --- /dev/null +++ b/plugins/sonilo/skills/sonilo-workflows/references/examples.md @@ -0,0 +1,44 @@ +# User-facing examples + +Respond in the user's language. Link labels mean real user inputs or returned +artifacts, not invented URLs. Use success wording only after a successful +result; substitute actual account data for illustrative numbers. + +| User request | Behavior and response shape | +| --- | --- | +| “Translate this one-minute product demo into English subtitles first; I will correct the product name.” + video URL | Run `proofread`, return the English SRT link, and wait: “Review this English subtitle file. Send the approved script URL when you are ready to dub.” | +| “Add relaxed music and waves to this 20-second beach video and give me the finished video.” + video URL | Run `video_to_video_sound` once. “Here is your new video with music and wave effects: [video].” | +| “Let me try English dubbing for free on this one-minute Chinese video.” + video URL | Check single-language, no-script trial eligibility. If confirmed and the task succeeds with a trimmed preview: “Here is the first 15 seconds in English: [preview]. The remaining 45 seconds have not been processed.” Otherwise explain that a free preview is unavailable without submitting a paid job. | +| “That preview sounds good. Dub the whole one-minute video.” | Refresh services, check for an existing full-video task, then submit once using the original source and English settings. “I will check your account and use the same video and language for the full version.” Do not claim it is already complete. | +| “How much balance do I have, and how many free music runs remain?” | Call `get_account_services`. If it returns `cash_balance="2.5001"`, `currency="USD"`, and `trial.text_to_music.remaining=1`: “Your current cash balance is USD 2.5001. You have 1 free text-to-music run remaining.” Use actual returned values. If balance is missing: “The service did not return your cash balance.” Missing trial data is unknown, not zero. | +| “Generate another 30-second upbeat track.” when rejected for insufficient balance | “The service reports insufficient balance.” If a fresh account lookup returns `cash_balance="0.0000"` and `currency="USD"`, add “Your current cash balance is USD 0.” Add “Generation did not start” only for a confirmed pre-generation rejection. Add “[Balance and free trials](https://platform.sonilo.com/usage-and-entitlements) explains how balance and trial limits work.” No automatic retry or instructions to purchase. | +| “My account credits have updated. Continue the English video from before.” | Refresh account data and retrieve any prior task. Submit once only after a confirmed rejection or when the only completed task is a preview and the user wants the full video. If the refreshed response returns `cash_balance="5.0000"` and `currency="USD"`: “Your current cash balance is USD 5. I will use the same video and English settings; the service checks available funds when processing the request.” A positive balance alone does not prove sufficient funds. | +| “I am new to Sonilo. How do I ask for music for a café ad?” | No generation. “Send a public HTTPS video link and say: ‘Add warm, relaxed jazz to this 30-second café ad.’” For more examples, link to `https://platform.sonilo.com/mcp`. | +| “Is the English video ready?” | Retrieve the same task ID. While processing: “It is still processing. I am checking the original task, not submitting another generation.” If waiting stops, provide the task ID without promising unattended monitoring. | +| “Give me three music choices for this 30-second café ad.” + video URL | Use `video_to_music` with `variants_num=3`, or the video-output tool if requested. Multiple variants use paid credits; a free-only constraint prevents this submission. Return three separately labeled actual URLs after success. | +| “Add light music to my travel narration, but keep my voice clear.” + video URL | Use the requested output type with `preserve_speech=true` and `ducking=true` where supported. Link to the actual processed output, not an unducked alternative. | +| “At 2–3 seconds add a door closing; at 5–7 seconds add rain.” + video URL | Use the requested SFX output type. For the hosted contiguous-segment schema, encode 0–2 no added effects, 2–3 door closing, 3–5 no added effects, and 5–7 rain. Return the actual audio or video after success. | +| “Generate 30 seconds of jazz and give me separate drums and bass for editing.” | `text_to_music` with `duration=30`, `stems=true`. Return [complete music], [drums], [bass], and other actual stem URLs. A stems-only failure does not invalidate the complete music. | + +## Concrete credit-error replies + +These examples assume the server confirmed rejection before task creation and +that the fresh account lookup returned the stated balance. Omit unconfirmed +facts. A trial count is included only if returned for that specific service. + +**User:** “再帮我生成一段 30 秒的轻快背景音乐。” + +**Reply:** “余额不足,这次没有开始生成。你当前的余额是 USD 0。 +可查看[余额与免费试用](https://platform.sonilo.com/zh/usage-and-entitlements)了解规则。 +账户更新后,可以对我说‘继续刚才的音乐’。” + +**User:** “再免費試一次英文配音。” + +**Reply when the dubbing trial is confirmed exhausted:** “你的英文配音免費試用 +次數已用完,這次沒有開始生成。可查看 +[餘額與免費試用](https://platform.sonilo.com/zh-HK/usage-and-entitlements)了解規則。” + +A zero remaining trial count alone does not prove insufficient paid balance. +For a free-only request, do not submit a paid job to test whether it will work. +If account lookup fails after a credit rejection, report the original rejection +and say the current balance could not be retrieved; retain the guide link. diff --git a/scripts/check_release.py b/scripts/check_release.py index 485e559..65d65de 100644 --- a/scripts/check_release.py +++ b/scripts/check_release.py @@ -25,6 +25,11 @@ RESOURCE_METADATA_URL = f"{MCP_URL}/.well-known/oauth-protected-resource" RFC_RESOURCE_METADATA_URL = "https://api.sonilo.com/.well-known/oauth-protected-resource/mcp" AUTHORIZATION_SERVER = "https://clerk.platform.sonilo.com/" +BALANCE_GUIDE_PATH = WORKFLOW_SKILL_PATH.parent / "references" / "account-and-links.md" +BALANCE_GUIDE_URLS = { + f"https://platform.sonilo.com{prefix}/usage-and-entitlements" + for prefix in ("", "/zh", "/zh-HK", "/de", "/es", "/fr", "/it", "/ja", "/ko", "/pt") +} PAID_TOOL_NAMES = { "text_to_music", "text_to_sfx", @@ -33,6 +38,11 @@ "video_to_video_music", "video_to_video_sfx", "audio_ducking", + "video_to_sound", + "video_to_video_sound", + "proofread", + "dubbing", + "analyze_video", } @@ -160,7 +170,9 @@ def check_local() -> None: require(manifest.get("mcpServers") == "./.mcp.json", "manifest must reference .mcp.json") require(WORKFLOW_SKILL_PATH.is_file(), "bundled sonilo-workflows skill is missing") require(WORKFLOW_AGENT_PATH.is_file(), "bundled sonilo-workflows agent metadata is missing") - sonilo_mcp = mcp_config.get("mcpServers", {}).get("sonilo", {}) + servers = mcp_config.get("mcpServers", {}) + require(set(servers) == {"sonilo_platform"}, "hosted MCP must avoid the legacy local sonilo server name") + sonilo_mcp = servers["sonilo_platform"] require(sonilo_mcp.get("type") == "http", "MCP transport type must be http") require(sonilo_mcp.get("url") == MCP_URL, f"MCP URL must be {MCP_URL}") require(sonilo_mcp.get("scopes") == ["profile"], "MCP OAuth scope must be limited to profile") @@ -176,7 +188,7 @@ def check_local() -> None: require(manifest.get("homepage") == "https://platform.sonilo.com", "homepage must use Sonilo Platform") interface = manifest.get("interface", {}) - require(interface.get("shortDescription") == "Create music and sound effects", "portal subtitle is out of sync") + require(interface.get("shortDescription") == "Music, sound effects & dubbing", "portal subtitle is out of sync") require(len(interface["shortDescription"]) <= 30, "portal subtitle exceeds 30 characters") default_prompts = interface.get("defaultPrompt") require( @@ -224,6 +236,18 @@ def check_local() -> None: skill = WORKFLOW_SKILL_PATH.read_text(encoding="utf-8") require(skill.startswith("---\nname: sonilo-workflows\n"), "skill frontmatter is invalid") + # References ship with the skill and are loaded only for the relevant workflow. + # Validate actual links so a missing packaged reference cannot silently drop + # account or dubbing guidance from the installed plugin. + references = re.findall(r"\]\((references/[^)]+\.md)\)", skill) + require(bool(references), "skill must link its workflow references") + for relative_path in set(references): + reference = WORKFLOW_SKILL_PATH.parent / relative_path + require(reference.is_file(), f"missing bundled reference: {relative_path}") + guidance = skill + "\n" + "\n".join( + (WORKFLOW_SKILL_PATH.parent / path).read_text(encoding="utf-8") + for path in sorted(set(references)) + ) require( "Validate public HTTPS media inputs before any tool call" in skill, "skill metadata must advertise media-input validation", @@ -239,8 +263,8 @@ def check_local() -> None: "new video with the generated audio mixed in" in skill, "skill must distinguish video-producing tools from audio-only tools", ) - require("`preserve_speech`" in skill, "skill must document speech preservation routing") - require("`isolate_vocals`" in skill, "skill must document vocal-isolation routing") + require("`preserve_speech=true`" in skill, "skill must document speech preservation routing") + require("isolate_vocals" not in guidance, "obsolete local-only parameter in hosted guidance") require("A Sonilo Platform account is required" in skill, "skill must state the required account system") require( "Accounts created only on `sonilo.com`" in skill, @@ -259,20 +283,29 @@ def check_local() -> None: "skill must reject unsafe media URLs without account lookups", ) require( - "Do not provide or direct users to pricing, checkout, subscription, credit" in skill, + "Do not provide or direct users to pricing, checkout, subscription, credit" in guidance, "skill must not direct users to purchase or recharge", ) require( - "do not repeat or paraphrase the URL" in skill, + "do not repeat or paraphrase the URL" in guidance, "skill must suppress purchase URLs returned by tools", ) require( - "to visit a website, account, billing, or pricing page" in skill, + "to visit a website,\n account, billing, or pricing page" in guidance, "skill must not indirectly route users to purchase destinations", ) + guide_reference = BALANCE_GUIDE_PATH.read_text(encoding="utf-8") + guide_urls = set(re.findall( + r"https://platform\.sonilo\.com[^\s|)]*/usage-and-entitlements[^\s|)]*", + guide_reference, + )) + require( + guide_urls == BALANCE_GUIDE_URLS, + "balance guide must configure all 10 published routes without query data or invented paths", + ) require("Poll `get_generation_task`" in skill, "skill must document asynchronous polling") agent_metadata = WORKFLOW_AGENT_PATH.read_text(encoding="utf-8") - require('value: "sonilo"' in agent_metadata, "skill agent metadata must depend on Sonilo MCP") + require('value: "sonilo_platform"' in agent_metadata, "skill dependency must match the hosted MCP server name") require(f'url: "{MCP_URL}"' in agent_metadata, "skill agent metadata has the wrong MCP URL") entries = marketplace.get("plugins", []) @@ -335,6 +368,14 @@ def validate_resource_metadata(metadata_url: str, body: bytes) -> None: status, _, _ = request(interface[field]) require(status == 200, f"{field} returned {status}, expected 200") + for url in sorted(BALANCE_GUIDE_URLS): + status, headers, body = request(url) + require(status == 200, f"balance guide {url} returned {status}, expected 200") + require("text/html" in headers.get("content-type", ""), f"guide is not HTML: {url}") + html = body.decode("utf-8") + canonical = re.search(r']+rel="canonical"[^>]+href="([^"]+)"', html) + require(canonical is not None and canonical[1] == url, f"wrong guide canonical: {url}") + def main() -> int: parser = argparse.ArgumentParser(description=__doc__) diff --git a/tests/fixtures/README.md b/tests/fixtures/README.md new file mode 100644 index 0000000..db9ad18 --- /dev/null +++ b/tests/fixtures/README.md @@ -0,0 +1,14 @@ +# Live-test media + +`speech-demo.mp4` is a 22-second test card with locally synthesized English +narration. Its exact script is `speech-demo.txt`. It contains no customer media, +private account data, recorded person, or cloned voice. + +Use this fixture for subtitle translation, 15-second preview/full-video +continuation, and speech-preserving soundtrack checks. Keep `lipsync=false`: +the image is a static test card with no visible speaker. These files are test +inputs outside `plugins/sonilo` and are not included in either release ZIP. + +The initial file was generated with macOS `say -r 145` and FFmpeg, using H.264 +video at 640x360/25 fps and AAC audio. The last seconds leave room for music to +return after speech. This is a diagnostic fixture, not a product-quality demo. diff --git a/tests/fixtures/speech-demo.mp4 b/tests/fixtures/speech-demo.mp4 new file mode 100644 index 0000000..41ba3de Binary files /dev/null and b/tests/fixtures/speech-demo.mp4 differ diff --git a/tests/fixtures/speech-demo.txt b/tests/fixtures/speech-demo.txt new file mode 100644 index 0000000..10b9a17 --- /dev/null +++ b/tests/fixtures/speech-demo.txt @@ -0,0 +1 @@ +Welcome to the Sonilo release test. This short video contains clear English speech. First, we review the subtitles. Then, we translate the same video into Spanish. Background music should become quieter while the narrator is speaking. Thank you for listening.