Skip to content

Enable media uploads in Instagram direct messages and improve decision server handling - #3256

Merged
esokullu merged 90 commits into
webbrain-one:mainfrom
esokullu:main
Oct 24, 2026
Merged

esokullu merged 90 commits into
webbrain-one:mainfrom
esokullu:main

Conversation

@esokullu

Copy link
Copy Markdown
Collaborator

No description provided.

claude and others added 30 commits October 22, 2026 23:21
Instagram keeps its message box's file input formless and display:none,
several wrappers deep in a thread too large for the page monitor's owner
bounds, so no action certificate covers it. Every upload_file was refused
as action_binding_unavailable/target_uncovered, even with the exact
selector get_interactive_elements returned for that input, often right
after the user had confirmed the recipient on WebBrain's card (trace
b71fd1dc: the card waited 14 minutes, then the upload was refused).
page-feedback-target-binding is a quality guard that advises, but its
advisory covered ref_id calls only, and a hidden file input never has a
ref, so default and Lean runs refused it too. The model then sent a link
to the picture as a success, or said Instagram blocks extension uploads.

- page-feedback.js (both builds): the target_uncovered advisory also
  covers an upload_file whose selector is exactly one the file-input
  inventory returned for the run's current top document and URL. Strict
  checks still refuse; guessed selectors, another document's or URL's
  inventory, ambiguous selectors and pending page activity keep their
  refusal. The recipient checks still bind an attachment in a protected
  conversation.
- content.js (both builds): a blocked native file chooser names its
  input with the inventory's selector instead of a path from the root
  ("div:nth-of-type(3) > input" in the trace).
- Instagram adapter: attach direct-message media through the composer's
  hidden input instead of clicking "Add Photo or Video"; a link to a
  picture is not the picture.
- Tests: guard-advisory-actions (default, strict and lean, both builds),
  file picker guard fixtures, an Instagram-shaped composer variant in
  recipient-clarify-mv3 (on main: card, then refusal, nothing sent; now
  sent once), and the adapter note.
- Docs: page-feedback-monitor.md, CHANGELOG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0154CpcHDcp22D4DjRv4Xp7W
Codex review of #502 (P1): the advisory took selector equality for element
identity. The inventory scope was current while the document and URL were,
so if a page re-mounted its composer, a later upload by the same selector
went to a node no inventory had listed once the mutation was observed.

- page-monitor.js (shared): keeps the nodes the latest get_interactive_
  elements result listed, with the selector it gave each, under that
  inventory's token and the run. A target_uncovered upload_file validation
  reports `inventoried` only when its target is that very node under that
  selector and token.
- content.js (both builds): the inventory hands its file inputs to the
  monitor when the agent names the inventory (_fileInventoryToken).
- page-feedback.js (both builds): each model inventory gets a token, kept
  in uploadObservationScope and sent with an upload's validation; the
  advisory also requires `inventoried`. Guard against a call without an
  execution context.
- agent.js (both builds): pass the token to the inventory's content action.
- Tests: the monitor test checks the listed node, another or no token, a
  re-mounted input and a fresh inventory on a real Instagram-shaped page; a
  recipient-clarify-mv3 variant re-mounts the input after the inventory
  (refused, then sent once after a fresh inventory; the selector-only
  version sent twice); the advisory tests require the listed node. The
  Instagram thread fixture is shared in test/lib/upload-release-fixture.mjs.
- Docs: page-feedback-monitor.md, CHANGELOG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0154CpcHDcp22D4DjRv4Xp7W
Let Act send photos and videos in Instagram direct messages
Providers → Decision refused any plain-HTTP endpoint but localhost,
127.0.0.1 and [::1], so a self-hosted decision server on another machine
of the LAN, such as http://192.168.1.51:8091/, could neither list its
models nor pass Test: both stopped with "Decision endpoints require HTTPS
outside localhost." Servers like that rarely have a certificate.

Plain HTTP is now accepted for private-network hosts
(isPrivateNetworkHost): 10/8, 172.16/12, 192.168/16, 100.64/10
(Tailscale), 169.254/16, unique-local and link-local IPv6, IPv4-mapped
forms of these, and .local, .home.arpa, .internal and .localhost names.
Public hosts still need HTTPS. A LAN server is still not this device:
`local` stays loopback-only, so it gets the credential-redacted texts and
the online requirement of any other non-local route.

Decision requests used the service worker's own fetch, which Chrome's
Private Network Access rules can refuse for local-network hosts; that is
why the chat providers use fetchWithFallback. In Chrome, requests to a
private-network host other than loopback now go through it too
(decisionFetch): model discovery tries directly, then the offscreen
proxy, and the decision POST takes the proxy. The proxy now carries a
request's redirect and credentials modes, so these requests keep
redirect: 'error' and credentials: 'omit'; no other caller sets them.
Discovery names GET because the proxy sends a request without a method
as POST. Firefox's background page reaches LAN hosts directly and keeps
its fetch.

Vision and speech endpoints never had this check and already use
fetchWithFallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xnq2MNUxHfmeT4ofyEP3Z1
fix(decisions): accept a decision server elsewhere on the local network
The planner sees only the user's text, the page URL and title and a short
history digest, and its clarify ends the turn. In a trace it asked three
times in a row for the text and files of "post the same on LinkedIn" while
the X post was on screen, and each answer re-ran the same blind planner.

- Build the history digest from the last ten authored turns, newest first
  within 2,000 characters (the latest answer keeps up to 1,200), without
  tool-call narration, and name earlier attachments. Tool calls and browser
  notes no longer push the authored turns out, and the latest answer is no
  longer cut at 300 characters.
- Give a planner or intent-check clarify one second look before the user
  sees it. A /no_think model (Qwen 3, QwQ, DeepSeek R1) retries with
  thinking on, unless reasoning is off for the provider or the model has no
  thinking mode. On the turn that answers the planner's previous question,
  the planner is told it cannot see the page and may ask again only for
  values that must come from the user (schedule timing, recipients,
  accounts, amounts, credentials, whether to publish at all, conflicting
  answers), and that second look may not add a submission, a recipient or
  a schedule. The second answer then goes through the gate's usual checks;
  a failed, unparseable or rejected one, or a spent cost allowance, keeps
  the first question.

Chrome and Firefox. Tests: test/planner-clarify-loop.mjs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaN7KUTVo3uH3CPok628vX
A second look told not to re-ask may not add a recipient its first answer
did not name, but the check read only `messaging`. For a plan that sends
nothing, normalizePlan moves the addressees into `draft_recipients`, so a
retry answering "use the current chat" or "look at the screen" could
address a draft to the open conversation and pass. Compare
`draft_recipients` against the first answer's recipients too. A draft for
the recipient the first answer named still passes.

Also replace the RegExps the new tests built from strings with exact
line comparisons (CodeQL: incomplete string escaping).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaN7KUTVo3uH3CPok628vX
When the planner asks how often a scheduled task should run and the user
answers "every five minutes", a repeated question can only be corrected
by a second look that plans the schedule. _plannerSecondLookWidens
rejected every schedule, so the same question came back.

A planner question now keeps the schedule of the task it asks about:
normalizePlan keeps `scheduling` on a clarify plan, where nothing runs
from it. Every gate that ends a turn with a planner question returns the
task's `schedulingTool`, the stored question records it, and
_plannerQuestionAnswerContext reads it back on the answer. A second look
may plan a schedule of the kind its first answer or the answered
question carried, so a schedule_resume pause never becomes a recurring
schedule_task. A schedule the task never carried is still rejected, and
in the planner gate the plan is still checked for alignment with the
task and the answer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaN7KUTVo3uH3CPok628vX
Two cases the Codex review on #501 found:
- A question about an unsent draft lost the draft's addressees:
  normalizePlan built draft_recipients only for executable plans, so a
  second look that drafted for the same person was rejected and the
  question came back. A clarify plan now keeps them too.
- A second look could drop the schedule_task the task carried, and the
  intent check, which has no alignment check, would then run the task
  once. Such a plan is now rejected.

The check is now _plannerSecondLookRejection and returns its reason
(adds_submission, adds_recipient, adds_schedule, drops_schedule), which
the planner_second_look_rejected trace note records. A recipient counts
as carried when the first answer or the answered question named it (the
stored question now records its messageTarget), or when the user's own
words in the task name every person, so "Who is your accountant?"
answered with a name is planned. The open conversation is never taken
from the page. The cloud-run check that a named recipient appears in the
user's words moved into _userTextNamesRecipients, which both use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaN7KUTVo3uH3CPok628vX
When a send to the open conversation (or a draft written into it) could
not be pinned, the gate asked the user to open it but stored no target
on that question. If the planner then asked again without one, a second
look that addressed the same conversation counted as a new recipient
and the question came back (Codex review on #501).

The pinning questions now return the target they could not pin as
`messageTarget`, which the stored question records like any other
planner question's target. Once the user has opened the conversation, a
second look may address it again, and it is pinned again before
anything is sent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaN7KUTVo3uH3CPok628vX
The first answer on the answering turn is model output like the second
look itself, but its requires_submission counted as carried authority:
a repeated question that wrongly marked the task submit-required let the
retry Send, and the intent check has no alignment check that would stop
it (Codex review on #501). Its recipients and schedule counted the same
way.

An execute plan from the second look is now measured against what the
user backed: the question they answered, as the app stored it (its
requiresSubmission, messageTarget and schedulingTool), and their own
words in the task. The first answer can still keep the question (a
schedule_task it carried may not be dropped) but authorizes nothing.
Another question, an answer or a plan to show runs nothing and is no
longer checked, so a second question from the second look reaches the
user.

The answered question's target is now compared by kind and by the set
of roles and identities (_sameMessageTarget), so the same people in
another order or casing are not a new recipient (Codex review on #501).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaN7KUTVo3uH3CPok628vX
…s replacements

Two cases the Codex review on #501 found:
- When the planner asked whether to send and the user answered "yes,
  send it", a second look that sent was rejected: the stored question
  carried no submission, so the question came back. In the full planner
  gate the plan is checked for alignment with the user's words next, and
  that independent check now decides such a submission. The intent check
  has no alignment check, so there the user keeps the question.
- When the alignment check denied a second look's plan, the planner's
  replacement took its place without the second look's limits. A
  replacement is now held to them (_plannerSecondLookRejection), and when
  it goes beyond them the user gets the question back instead.

The flag for whether the alignment check runs is computed once and used
for both. The trace note for a rejected second look moved into
_tracePlannerSecondLookRejection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaN7KUTVo3uH3CPok628vX
…ion carried

The compact intent check (Planner off) has no alignment check after the
second look, so nothing there reads what the user's answer settled. Two
cases the Codex review on #501 found:
- A name anywhere in the user's words counted as a chosen recipient, so
  after "send to Alice or Bob, ask me which" answered with "Bob" a plan
  for Alice passed.
- A schedule of the kind the question carried passed without its timing,
  so "I'm not sure yet" could be followed by an invented interval.

Recipients from the user's words and a carried schedule now count only
when the alignment check follows (the full planner gate), which reads the
answer. Without it, a second look may run only the submission, people and
no schedule that the answered question carried; a schedule there is
rejected as unchecked_schedule. The full planner gate is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaN7KUTVo3uH3CPok628vX
With the Planner off, the compact intent check has no alignment check
after the second look, so nothing there reads what the user's answer
settled, and each structural stand-in fell short (Codex review on #501):
a name anywhere in the user's words, a carried schedule without its
timing, and now a recipient the earlier question's plan may have made up
while asking about something else.

The intent check therefore no longer re-asks. A model told not to think
still rethinks there, which is the same request judged like its first
answer. The full planner gate re-asks only when its alignment check runs
(always, on a turn that answers a planner question), and that check
decides what only the answer can settle: which person it chose, a
schedule's timing and whether it approved sending. The second-look rules
keep what the check could miss: a recipient neither the answered
question nor the user's words name, a schedule of another kind, and a
dropped schedule_task. The intent-only rules from the previous commit
and the answered question's submission flag in the answer context are
gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaN7KUTVo3uH3CPok628vX
_plannerCanRethink read only chat_template_kwargs.enable_thinking,
enable_thinking, reasoning.enabled and DeepSeek's thinking.type. With
reasoning turned off another way, for example DeepSeek Responses'
reasoning.effort "none", hosted DeepSeek's chat_template_kwargs.thinking
false or a top-level reasoning_effort of none or off, a first planner
question still got a second, paid request that could not think (Codex
review on #501). _extraBodyDisablesReasoning now covers these shapes,
plus thinking:false and reasoning:false. A reasoning effort that keeps
thinking on still allows the rethink.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaN7KUTVo3uH3CPok628vX
With the Planner off, a model told not to think still rethought a
question on a turn that answers a planner question. That retry reads
the user's answer like a re-ask would, and the compact intent check has
no alignment check after it, so it could run a recipient, schedule or
submission nothing checked (Codex review on #501). On such a turn the
intent check now takes no second look at all, and its first answer
stands. A first question is still rethought, and the full planner gate
is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaN7KUTVo3uH3CPok628vX
The second look rejected a plan that dropped a schedule_task the task
carried, or that added a schedule the answered question did not carry,
before the alignment check could read the user's answer. So "never mind,
just do it once" and an answer that adds a schedule both brought the old
question back (Codex review on #501). The drop rule existed because the
compact intent check had no alignment check, and that check no longer
takes a second look at an answer.

A second look now re-asks only where the alignment check follows, and
that check reads a schedule the answer gave, changed or dropped, as it
reads a send it approved. The one structural limit left is the
recipient: a re-ask plan may address only the people the answered
question addressed or people the user's own words name. The alignment
check cannot keep out a recipient taken from the page when the answer
points at it. The stored question's schedulingTool, the schedule kept on
clarify plans and the first answer as an input to the check are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaN7KUTVo3uH3CPok628vX
esokullu and others added 28 commits October 24, 2026 08:56
…o-plugin/zod-4.6.6

build(deps): bump zod from 3.25.76 to 4.6.6 in /lmstudio-plugin
…s/sources/deepseek-v4-flash/filelock-4.2.1

build(deps): bump filelock from 3.32.2 to 4.2.1 in /docs/vision-models/sources/deepseek-v4-flash
…ver/fast-uri-3.1.9

build(deps): bump fast-uri from 3.1.8 to 3.1.9 in /mcp-server
generate_image sent only a prompt to OpenRouter video models, so an
"explainer" came back as one default-length clip (8 s for Grok Imagine)
while the agent described it as a full video.

The tool now takes estimated_duration, and for a single clip duration,
aspect_ratio and resolution, checked against the model's catalog entry
before anything is billed. A longer video is passed as scenes: WebBrain
fits them to the model's clip lengths, generates up to three clips at a
time with one retry each, and joins them into one MP4 without
re-encoding (video-join.js, parsing with vendored mp4box.js 2.4.1).
Videos are capped at 120 s and 24 clips; an estimate over 120 s asks on
WebBrain's own card first (video-length-cap), and an unattended run
generates nothing. Clips that cannot be joined are stored and shown in
order. The model gets the real durationSeconds and clipCount and is told
it has not seen the video.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XEjrmWRKwMNkwhEq3Yr97
Clips without edit lists (fragmented MP4s) start their picture after a
B-frame delay. The joiner measured each clip from that first picture but
wrote no edit list, so two such clips became a declared 2 s movie whose
picture ran from 0.17 s to 2.17 s.

Each track of a clip without edit lists now starts at the first picture
(audio by the same amount, so it stays in sync), and every output track
gets an edit list, so a joined movie runs from 0 to its declared length.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XEjrmWRKwMNkwhEq3Yr97
A clip's edit list can hide whole frames at either end of its picture.
The joiner measured such a clip by its edited length but copied every
frame, so the hidden frames would play over the next clip and push the
later clips past their start (a 0.5 s trim put the second clip at
0.92 s instead of 0.5 s). Joining now refuses these clips, and they are
shown one after another instead. Shortening the last frame alone still
joins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XEjrmWRKwMNkwhEq3Yr97
The joined file's decode times start at 0, but its edit list and the join
targets still used the first clip's own media times. Fragments that start
later (a nonzero tfdt) therefore had their first clip skipped by the edit.
Both are now measured from the first clip's first sample, so such clips
join into the same file as ones that start at 0. A clip with a track that
starts more than a millisecond after the clip itself would need a gap
that one edit cannot show, so it is kept as a separate clip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XEjrmWRKwMNkwhEq3Yr97
A clip with a longer composition delay than the one before it must start
decoding before that clip's last frame. The joiner clamped the gap to one
tick and pushed the clip's picture later than the declared timeline. Such
joins are now refused, and the clips are shown one after another. The
same refusal covers an edit that plays at another speed and a clip that
does not open on a keyframe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XEjrmWRKwMNkwhEq3Yr97
The joined file shows every clip with the first clip's track and movie
matrices and display size, so a later clip lost its rotation or size.
Clips whose placement differs are now kept apart, and so is a clip whose
samples run past the end of its file (a cut-off download), instead of
being read past the end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XEjrmWRKwMNkwhEq3Yr97
The joiner wrote no sample groups, so a joined file lost the 'roll' group
that tells players how many audio frames to decode before a seek point
(required for Opus in MP4). Each clip's groups now carry over with its
frames, and clips whose group descriptions differ are kept apart.
Joining also refuses encrypted tracks, whose per-sample keys are not
carried over, and tracks that keep their samples in another file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XEjrmWRKwMNkwhEq3Yr97
An audio track's own edit can end before the picture, leaving the rest of
its samples silent. The joined file gives every track one edit for the
whole movie, so that sound would play on into the next clip. A clip whose
track edit ends more than one of that track's frames before the picture
is now kept apart; one that ends within a frame still joins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XEjrmWRKwMNkwhEq3Yr97
Generate videos longer than one clip and join them into one
Bumps [setuptools](https://github.com/pypa/setuptools) from 83.0.0 to 84.1.0.
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst)
- [Commits](pypa/setuptools@v83.0.0...v84.1.0)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-version: 84.1.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
…s/sources/deepseek-v4-flash/setuptools-84.1.0

build(deps): bump setuptools from 83.0.0 to 84.1.0 in /docs/vision-models/sources/deepseek-v4-flash
…ejection

Automatically decline cookie-consent banners
Grok Imagine's clips end their sound 42 ms before their picture (7.000 s
of sound in a 7.042 s clip). The joiner read that as an audio edit hiding
sound and kept the four clips of a 30-second video apart.

The early-edit check now compares a track's edit with the track's own
samples, so only an edit that hides sound keeps a clip apart. AAC-LC sound
is now laid out as players play it: every frame at its full 1024 samples,
one after another, with silent frames where a clip's sound is shorter than
its picture, starts after it, or has a gap. A clip's own start-up frame
fills the last of those slots, so its first frame decodes as encoded.
Players such as Chrome time audio by the samples they play, so the sound
stays with the picture, every frame within half a frame of its time.
Other codecs join as before.

Clips that still cannot be joined say why in the tool result (joinError),
so a trace shows it. Clips that claim to run for more than an hour are not
joined, since silence would fill them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XEjrmWRKwMNkwhEq3Yr97
The test patches the audio object type in place; it now fails clearly if
the configuration bytes are not found, instead of patching another byte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XEjrmWRKwMNkwhEq3Yr97
Join clips whose sound ends before their picture
Generated videos made of several clips explained little and changed their
characters between clips: each clip is made from its own text, and the
agent wrote story beats plus abstract "visual logic" a video model cannot
draw.

A new default skill, loaded only when the agent writes the scenes of such
a video, asks for a cast and set sheet in the shared prompt, one filmable
action per scene, state carried between scenes, demonstrations instead of
diagrams or text, optional short narrator lines, and no facts beyond the
page or request. Only its one-line catalog entry is always offered; the
scenes parameter points to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XEjrmWRKwMNkwhEq3Yr97
Each clip's prompt began "Scene 3 of 8:", and Grok drew it as a title.
A scene longer than the model's longest clip was split into parts with
the same prompt plus "(part 1 of 2)", so the model made the same clip,
narration included, twice, and drew the part label too.

Clips now get the shared prompt and their scene, nothing else. A scene
longer than the longest clip is refused with the limit, so the agent
splits it into scenes with their own prompts; the Video scenes skill
says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XEjrmWRKwMNkwhEq3Yr97
Video scenes skill, and no repeated or numbered clips
Providers -> Generative had one model, so a saved video model left
generate_image unable to make images, and an image model unable to make
videos. Each provider now has an image model and a video model, with
defaults used when a field is left empty: fal.ai (fal-ai/flux/schnell,
xai/grok-imagine-video/v1.5/lite/text-to-video), OpenRouter
(google/gemini-2.5-flash-image, x-ai/grok-imagine-video-1.5-lite) and
Comfy Router (bfl/flux-2-pro, xai/grok-imagine-video-1.5-lite, each with
its own input JSON). ComfyUI takes an optional video workflow and uses
the image workflow for videos without one.

generate_image takes media "image" or "video"; video options imply a
video. Saved settings keep their model as the image model, and an
OpenRouter model the video catalog lists keeps making videos, including
for calls that do not say, as before. Test Connection checks both
models and names them.

A refused request (a 4xx other than 408 or 429, such as HTTP 402) is no
longer retried, and a 402 says the account is out of credit.

The settings labels and hints are translated into all 23 interface
languages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XEjrmWRKwMNkwhEq3Yr97
Separate image and video models for every media provider
@vercel

vercel Bot commented Oct 24, 2026

Copy link
Copy Markdown

@esokullu is attempting to deploy a commit to the esokullu's projects Team on Vercel.

A member of the Team first needs to authorize it.

@esokullu
esokullu merged commit 6df6e17 into webbrain-one:main Oct 24, 2026
1 of 4 checks 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