Skip to content

ai-node-canvas 0.84.0: the review is answered, the progress panel lets go, and a stopped call says what to do - #78

Merged
MyPrototypeWhat merged 2 commits into
MiniMax-AI:mainfrom
antianqi:feat/ai-node-canvas-review-fixes
Oct 26, 2026
Merged

MyPrototypeWhat merged 2 commits into
MiniMax-AI:mainfrom
antianqi:feat/ai-node-canvas-review-fixes

Conversation

@antianqi

@antianqi antianqi commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

What it does

This PR answers the review of #71 (APPROVED with comments) and fixes two
problems a user reported on the released build.

ai-node-canvas 0.84.0 — 13 review items fixed, 1 kept as it is, plus the
progress panel and the generation timeout.

The review items

# Item What changed
1 A download could reach a machine of its own The address that was judged is the address that is used: the name is resolved once, and the connection is pinned to that address with a lookup override — on every hop of a redirect, not only the first
2 Private addresses were judged by a hand-written list The rules are Node's own net.BlockList, and an IPv6 address that carries an IPv4 inside it (::ffff:7f00:1, ::7f00:1, NAT64 64:ff9b::7f00:1, 6to4 2002:7f00:1::) is judged by that IPv4 — one rule, one place
3 A file could be written through a dangling symlink The name is taken with lstat first and written with flag: 'wx'; a taken name gets a (1) suffix instead of being written through
4 A template of the user could be overwritten The id of a template is the name of the file that holds it. Saving with a name that is taken is refused (taken), and the API answers 409
5 A template that could not be applied could be saved One rule (templateStepsError) is used by the three places that read, apply and save a template
6 A canvas was named before the steps were on it The canvas takes the name of a template only when the steps are really on it (canvasTitleAfterTemplate)
7 A run the runtime refused left the node busy for ever The refusal carries the id of the run that is going; the chain waits for that run, and the node is free the moment the run is refused. The refusal is now judged after the request is built, so it cannot be slipped past by the client
8 An audio-only request was refused by the client The reference set is judged by the material the runtime really sends, not by what the page claims
9 The export wrote whatever the page asked for Only a .json file, and only a template document — an object is serialized, a string must pass the template rules
10 An old mode name was found by a partial match Object.hasOwn — generate no longer finds generate-video
11 The MCP description did not name its tools canvas_list_templates and canvas_apply_template are described, and putting a template on a canvas is said not to run it
12 The READMEs did not say what a deleted template does Both READMEs say a template of the user is gone for good, and that a download can only go to an address the runtime is willing to reach
13 The reviewer's "as it is" item Kept as it is

The user-reported problems

1. The progress panel could not be put away, and a failed run stayed for ever

The panel is a progress panel, not a history:

  • A run that is over is kept 30 seconds — long enough for its reason to be
    read — and then it goes. A failed run is a finished run.
  • It has a 「收起」 button. The panel sits over the canvas, so putting it
    away has to be the user's choice.
  • A run that starts brings it back, because that is what the panel is for.
  • At most 8 lines, newest first: a burst of failures must not cover the
    canvas.

The rules live in one place (miniapp/client/run-watch.mjs) as plain
functions, and the button is built by one small module
(miniapp/client/task-panel.mjs) so it can be tested without a browser.

2. A picture generation died after 30 seconds

调用 generate_image 失败(耗时 30.0s):调用已经发出但没收到回执,结果未知 (可能已经扣费)…

That 30 seconds is the Host's, not ours. The connector API is
hostConnector.call(toolRef, args, { signal }) — the options hold a signal and
nothing else, so a MiniApp cannot ask for a longer window. A picture is asked
for in one synchronous call (generate_image has no task number to poll),
which is why a slow picture is simply lost; a video is submitted and
polled (submit_video_generation + query_video_generation), which is why
the same limit never shows up there.

So the limit cannot be widened from here. What can be done is said out
loud, and it is:

  • the message on the card now says where the limit is (about thirty
    seconds, and it is the Host's) and what the user can still change — a
    picture has no task to poll, so the way out is a smaller request: 1K
    instead of 2K/4K, fewer reference images, then generate again;
  • the MCP server is given a minute instead of half of one. A call that
    starts a run hands the work to the page and waits for the page's answer, and
    a busy page must not turn the call into a failure.

Verification

  • 248 unit tests, all green (node --test tests/smoke.test.mjs)
  • 248 mutation self-checks, all green — every contract was broken on
    purpose and the test had to turn red (the script lives outside the package,
    it is the checker's own harness)
  • Static guards: no undefined references, no self-referencing
    declarations
  • Official validator: 0 errors, 1 warning (HOST_CONNECTOR_UNVERIFIED —
    the Host grants the declared providers at install time)
  • The repository's own check: npm run check → 69/69, exit 0;
    npm run validate → plugins/antianqi/ai-node-canvas: 0 errors, 1 warning
    and repository: OK
  • On the real host (build 269afb3cee84): the 「收起」 button puts the
    panel away while the run keeps going; a new run brings the panel back; a
    finished run leaves the panel by itself after 30 seconds; no error in the
    console

… lets go

The review of 0.82.0 left thirteen things to fix. They are fixed here; the one
that was approved as it is stays as it is.

A download could be sent to a machine of its own: the address was judged
once, and the connection could go somewhere else. The address that was judged
is now the address that is used - the name is resolved once, and the
connection is pinned to that address, on every hop of a redirect. The private
addresses are judged by the rules of Node's own BlockList, and an IPv6
address that carries an IPv4 inside it is judged by that IPv4.

A file could be written through a link that points at nothing. A name that is
already taken is refused instead of written through, and the name of a
template is the name of the file that holds it: a template of the user cannot
be overwritten, and a template that could not be applied is not saved.

A run the runtime refused left the node busy for ever. The refusal now names
the run that is going, the chain waits for that run, and the node is free the
moment the run is refused.

The progress panel could not be put away, and a run that had failed stayed on
the screen for ever. It is a progress panel, not a history: a run that is over
is kept thirty seconds so its reason can still be read, and then it goes. It
has a button that puts it away, and a run that starts brings it back.

The READMEs and the MCP description now say what the tools do: that a
template can be listed and put on a canvas, that putting one there does not
run it, and that a download can only go to an address the runtime is willing
to reach.

246 tests, 246 mutations, 0 errors.
…e MCP server gets a minute

A call the Host stops is stopped by the Host, not by us. The connector API
takes a signal and nothing else, and a picture is asked for in one
synchronous call - there is no task to poll, the way a video has one. The
message on the card now says where that limit is (about thirty seconds, and
it is the Host's), and what the user can still change: a smaller request.
A video is submitted and polled, which is why the same limit reads as a
mystery for a picture and not for a video.

The MCP server is given a minute to answer instead of half of one. A call
that starts a run hands the work to the page and waits for the page's
answer, and a page that is busy must not turn the call into a failure.

248 tests, 248 mutations, 0 errors.
@antianqi antianqi changed the title ai-node-canvas 0.83.0: the review is answered, and the progress panel lets go ai-node-canvas 0.84.0: the review is answered, the progress panel lets go, and a stopped call says what to do Oct 26, 2026

@MyPrototypeWhat MyPrototypeWhat left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 9cd9379 (v0.84.0). Looks good to merge.

  • The new stop messages only cover CONNECTOR_OUTCOME_UNKNOWN and CONNECTOR_TIMEOUT, and the error code passed to callers is unchanged.
  • The 60s MCP timeout is a per-call limit and within what other plugins here already use.
  • The PR touches only plugins/antianqi/ai-node-canvas/, the version is 0.84.0 everywhere, and it merges cleanly into main.
  • CI passes on Node 22, and all 248 plugin tests pass.

Optional

  • An unknown outcome isn't always a timeout. The call can also fail when the connection drops. Wording like "this often happens when the call takes too long" keeps the advice without stating the cause as fact.
  • The 30-second figure. The public docs don't promise it, so the message could go stale quietly. "A time limit (currently about 30 seconds)" or no number would age better. The test could check for 宿主 and the advice instead of 30 秒.
  • Duplicate image sentence. Moving it into one constant keeps the two branches from drifting apart.
  • Also worth a look later: object export validation, the remaining IPv4-in-IPv6 forms, card content on a busy refusal, the saveToDownloads race, and realpath on directories.

@MyPrototypeWhat
MyPrototypeWhat merged commit 373d565 into MiniMax-AI:main Oct 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants