A task's pictures no longer in the output folder are marked gone when the
task is read and shown as "Picture deleted" (timeline, results strip,
stage panels and picks) instead of a broken picture; "@" no longer offers
them. The AI hears plainly that the user deleted one, and a tool reading a
gone picture says it may have been deleted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new task an AI begins while on another, or one the user starts while
looking at a task, keeps it as its "from". Links under the title lead both
ways, the list marks follow-ups, the user can link one by hand or take the
link off (never a loop), and an AI beginning a follow-up hears what was
decided in the task before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The AI records what was settled with log_decision (a card on the timeline:
what, why, what was left out, who decided, the steps it is about); the
user's own choices in Anomalous count as decisions too (panel answers,
gates, materials, the canvas mode, stops). A Decisions view in the side
column lists them all, links each to its step and notes work done since
the AI's last one; begin_task repeats them to an AI picking the task up.
A step's words move to ui_ai_step_body.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"@" lists the task's pictures, combos, models, workflows, prompts, node
packs and node types; what is named rides with the message and the AI gets
each in full (pictures attached with what made them). "/" lists commands
that act at once (stop, undo, side views, new task, canvas mode) or fill in
words to send. get_toolbox shares the lookup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pack shown in the nodes' second column has a bar to pick it whole
(packs, by folder). The AI gets its node types in get_toolbox and finds
them first; with "only" every node of the pack is allowed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every answer, message, set of marks and handed picture shows a pill:
sent and waiting, then the AI has it with the time it did (delivered_at).
Panel questions can be skipped; the AI reads skipped: true.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Each run keeps its shape read from the prompt ComfyUI queued (ai_run_map.py): boxes by role
with their settings, typed links, plumbing wired through, and what changed since the task's
run before. The run step shows a one-line summary, the changes as chips and the boxes (top
down, fitted to the column, each locating its node); show_run_map puts it on the stage.
- By default the AI works in a tab of the task's own (ai_task_canvas.py, mcp_task_tab.js): the
open workflow is copied before its first change, the tab is brought back (a click on
ComfyUI's tab) before later changes, reads and runs, and the user's tab stays untouched.
Per-task switch in the header, default in the Gates window; the AI is told once.
- Output pictures can be handed to the task an AI is on (gallery card menu, the inspector's
✦), with the user's words: the AI gets them attached with what made them (ai_pictures.py).
search_images(with_pictures) attaches the first six.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A stop button on a running run (timeline, and beside the run-status button), and stop_run for
the AI: only this run's prompts leave the queue or are interrupted; the step ends "cancelled"
and a user's stop reaches the AI as a "stop" message (ai_run_control.py).
- The run's progress carries ComfyUI's console since it began (the bar's note, models loading),
time left and per step, and VRAM; once queued, each sampler's working size is estimated from
the prompt, with warnings for an upscaled picture sampled again, a size past the card and a
huge decode, for the user and in the AI's run news (ai_run_watch.py, ui_ai_run_live.js).
- The materials picker opens as a side panel that leaves the page usable (the full-screen
browser makes room), expands to the large window and back, and remembers the choice.
- Esc closes the run-status card only (data-anomalous-escape).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The marker gains a repaint brush (size adjustable) and "make a repaint
mask": the page sends a mask (white where boxes, circles, lines and brush
strokes are) and the server writes the picture with that area as its
alpha to ComfyUI's input folder (anomalous_inpaint_<step>.png, a new file
each time), so LoadImage gives the picture and the area as MASK. The AI's
marks message carries the file and how to inpaint with it; the timeline
says the mask is ready. Canvas tools read ComfyUI's lists anew once when a
value is not among a drop-down's choices, so they take the new file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
report takes reply_to, the id of the user's message or marks it answers:
the two steps point at each other and the timeline links them both ways
("Reply to step 2" / "The AI replied · step 4"). The AI's notes are laid
out (headings, lists, bold, code) by ai_text_format.js with textContent
only. The notice that brings the user's marks and messages, the report
description and the server instructions tell the AI to answer in
Anomalous, and to ask which fix with ask_user.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ask_user shows a question in the AI workbench (options with an optional
recommended one, and unless other=false a line for an answer of the
user's own; no options: a line) and waits in the same call for the
answer, returning the choice or the text. Choices blocks gain
recommended and other; the step is named ask_user ("Asked you"). The
server instructions, begin_task and the workbench prompt tell the AI to
ask every question this way, never in its own chat.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A large window: one search over every kind (/ jumps to it), kinds on the
left with picks and matches, a column by source (each custom node pack
with a badge of its own colour, ComfyUI's own, paid API nodes), prompt
origin or model type, a card grid drawn as it scrolls, the card in focus
in full on the right, and the picks below with thumbnails and a prefer /
only switch. A node card is the node in miniature, sockets in ComfyUI's
colours; combos, workflows and models show their covers; prompts their
text, including the prompts inside combos and workflows (combo:<name>,
workflow:<name>), which get_toolbox now resolves. Vector icons, badges
and the empty picture follow the theme.
It opens at once: node types come from the page's own definitions, the
rest in one answer (api/ai_toolbox_catalog.py) read ahead when a task
header is drawn and kept a minute, refreshed behind an open window.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Materials button on each AI task opens a picker: every node type of this
ComfyUI grouped by source (built-in, each custom node pack from
/object_info's python_module, page-only helpers, paid API nodes last), plus
combos, saved workflows, prompts and models by the names the AI's tools
use. Picks go to the AI as a user_toolbox step (message kind toolbox), in
begin_task and in full from get_toolbox; "only" makes add_node and
new_workflow refuse other custom or paid node types (built-in stay
allowed). Favourites stand in for tasks without picks. find_nodes lists
picks first and marks paid API nodes. open_workflow opens a saved
workflow in a new tab; a workflow's page can hand it to the AI.
Checked on the isolated ComfyUI with copies of the user's 10 node packs:
all 1642 node types can be placed, and a Comfyroll workflow ran.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
find_nodes looks up this ComfyUI's node types (inputs, outputs, choices)
from its own /object_info. new_workflow opens a new tab, blank or built and
laid out from a workflow in API form, never touching the open one;
add_node, connect_nodes, set_values and remove_nodes edit it. Everything is
checked against the nodes before the canvas changes (types, loops, settings,
choices with close matches); each is one Ctrl+Z step, logged as the AI's
and shown under its workbench step. Receipts now show rewiring ("wired":
what feeds an input, before and after). A run ComfyUI refuses reports
ComfyUI's own reason instead of "Prompt execution failed".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bar under a run shows a percent like ComfyUI's tab title (the running
node's steps, else the nodes done). While a run goes, every other tool answer
carries your_run with one sentence on where it stands, and the next answer
after it ends says so once (run_status telling it counts), so the AI need not
look in the output folder or at the screen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ui_ai_marker.js: ✎ on a picture of the AI page opens the marker (box, circle, arrow, pen; three
colours; a note per mark and one for the whole picture). Send draws the marks onto the picture
and hands both to the task; the timeline shows the marked picture and the notes.
- api/ai_marks.py: the PNG is checked (signature, size, dimensions) and kept with the task in
anomalous/ai_marks/ (never the output folder; removed with the task); a user_marks step the AI
gets as a message of kind "marks", where each mark is in fractions of the picture, with the
marked picture attached as an image to the same answer (also from get_answers, whose tool now
returns the answer and those pictures).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ai_task_runs.py reads where a run stands from ComfyUI's queue and progress registry (never
changing them): queued and how many are ahead, or the node running, its steps and the nodes
done. It is kept on the step in memory and sent as "progress" events that only move the
step's bar; the run's step is saved only when something new is kept.
- run_status (read-only): the task's latest run and its progress, its pictures once done;
wait_seconds (<= 55) waits for the end. run_workflow's answer tells the AI to use it instead
of guessing from the time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ui_canvas_changes.js: a prompt written as tags lists the tags taken out (red, struck) and put
in (green) first; "Show meanings" puts each one's translation under it (prompt_gloss.js);
"Whole text" marks those tags in both texts and dims the rest. Other text marks the part that
differs. A tag the log cut in half at either end is never called changed.
- activity_log.py: before/after values keep up to 2000 characters (was 400), room for a whole
prompt; longer ones are still cut around the first difference.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ui_ai_handoff.js: "Hand to AI" on a model's page, the image inspector (✦) and combo cards.
The user says what they want (prefilled), Anomalous starts a waiting task with `about` (the
model_id, the output image, the combo's name; checked in ai_tasks.start_task) and shows the
sentence to send the AI; begin_task hands `about` to the AI so it needs no search.
- The AI windows sit above the image inspector they can open from; going to the AI page from
there closes the inspector.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- api/mcp_apps.py + api/mcp_app/task_card.html (MCP Apps, io.modelcontextprotocol/ui): apps that
support it (Claude, ChatGPT, VS Code…) show Anomalous's own card under begin_task, show_panel,
report and run_workflow results: the task's steps, latest thumbnails (inline), the AI's open
questions answered with a click (app_answer, plus a chat message so the AI goes on). The card
reads through app_task_view; both tools are for the card only (visibility "app"), are never
steps and never take the user's news. The card never answers a gate: an OK is only given in
Anomalous, so no tool an AI can reach grants one.
- api/mcp_prompts.py: prompts/list and prompts/get, four ready-made requests (slash commands):
workbench_task, pick_best, fix_missing_models, explain_canvas.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- api/ai_gates.py: changing the canvas, using the GPU, downloading and scanning online can each
go ahead, ask first or never (downloads ask by default; anomalous/ai_gates.json). A call that
asks waits on its step (state "asking", what it would do listed) up to 50 s for Allow or No
(with the user's reason for the AI); unanswered, the AI is told to wait and call again, the
later answer reaches it as a message, and the same call after an Allow goes ahead.
- ui_ai_gate.js: the card under a waiting step and the Gates window; a toast while elsewhere.
- ui_ai_flow.js + ai_task_view.flowStages: the task's stages in order, each done, running,
waiting or failed; a stage leads to its first step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- api/action_catalog.py: every MCP tool's kind, stage of the work and gate in one place (a test
keeps it and the tools in step); steps now carry their stage.
- api/ai_task_changes.py: changes the user makes in Anomalous while an AI works become steps of
its task, reach the AI once with its next call (kind "change": keep it), and the activity
entry names the task; the activity page links to it ("AI task ->").
- api/ai_task_runs.py: run watching moved out of ai_tasks.py (600-line rule); a step a restart
left "running" is marked stopped when the record is read again.
- Pictures: inspector / compare with canvas / keep as tools on hover instead of three buttons
under every picture; tick any two (any step or task) and compare from the bar at the foot of
the page; the results strip shows thumbnails again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New task: the user starts a task (POST /anomalous/ai_tasks) that waits for
an AI; the next app that calls takes it over, so its work starts there
instead of joining its last task. Connect an AI (ui_ai_connect.js): the
address, a sentence to send the AI so it adds the server itself and
starts on a waiting task, and each common app's way; beside the task list,
on the empty page and in a waiting task. The stage now lists the task's
panels oldest first with their step numbers, the newest at the bottom (a
new one scrolls there, or "New panel" says it came), and the strip of
results runs oldest to newest. The routes move to ai_tasks_routes.py.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A box under a task's timeline sends the user's message (POST
/anomalous/ai_tasks/{id}/message), kept as a 'user' step; get_answers now
also brings the user's messages and, with no panel named, waits for one.
The step says whether the AI has seen it; when the task has ended the box
says to ask in the AI app. The task list is grouped by day with an icon
for where each task stands.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New panel blocks, data only and drawn by Anomalous: marks on pictures
(circles or boxes at fractions of the picture, numbered, labels under it
that zoom there), a before / after slider, picks (pictures with the AI's
reason, Take this one as an answer, Put on the canvas through ComfyUI's
own file loading, drag onto the canvas) and diagrams (boxes and arrows laid
out by ai_stage_layout.js; a box naming a canvas node finds it). No SVG
or drawing code of the AI's is taken.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
show_panel lays out a panel as a step of the AI's task from fixed blocks
(heading, text, pictures in a row, grid or side by side, the user's models
as cards with covers, table, list, params, progress, divider) and can ask:
buttons (one or several) and a line of text. api/ai_panels.py checks every
block (only these kinds, bounded plain text, pictures only from the output
folder, models only from the user's folders); nothing is run or read as
markup, and ui_ai_panel.js writes text only. Answers come in through
POST /anomalous/ai_tasks/{id}/answer and wait for get_answers (which can
wait up to 55 s); update_panel changes or closes a panel. A toast says
when a panel comes while the browser is elsewhere.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new rail page keeps each MCP call as a step of a task (api/ai_tasks.py,
one file per task in the user folder, the newest 300): named by the AI
with the new begin_task tool, else one app's calls without a long pause.
A step keeps what it did and what it made (models, the combo, the canvas
change by its activity entry, the pictures a run made, watched in the
history until it ends, spoken audio, scans); the new report tool leaves
the AI's notes and pictures it made another way. The page shows the tasks
and one task's results strip and timeline with links (picture viewer,
model page, combo, find or undo on the canvas, audio), updates live, and
gives the rail a dot while you are elsewhere; Home lists the last tasks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A download by name says where the list's file is; an entry of no stated type no longer
fits a node of known type; without the Manager, the page says it would be one more
source. docs/model-check-rules.md gets a section on the Manager.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The combo editor gets a head with its picture (See the picture), a large name with a
Rename button, chips of what it holds and Put on canvas; models show only what is chosen,
Change / Add open the grid of compatible ones. No Save button: everything saves itself
(a tag changed in the bilingual view now saves too). The gallery's ★ Kept lists only the
pictures kept as a workflow, combo or prompt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A bar over the canvas finds combos, models and saved prompts as you type
(GET /anomalous/quick_index lists the models in a few words) and puts the chosen one
where the pointer was. Also in the empty canvas's right-click menu, now registered.
Saved model paths in a linked model folder get their real place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A user found model folders cluttered after scans: four files per model.
model_sidecars.py now says where the plugin's own side files are (.info,
.anomalous.json, .civitai_bak.*): beside the model, or in the folder's
hidden .amb (Settings -> Models -> Files that come with models). Every
reader and writer goes through it and reads both places; covers stay
beside the model for ComfyUI and other plugins.
Scans no longer keep a copy of Civitai's picture identical to the cover:
the cover is noted as Civitai's (SHA-256 in the .info) and a copy is kept
aside only while the user's own cover shows. api/sidecar_layout.py moves
existing files and drops the duplicates to the Recycle Bin. The folder
tree leaves .amb out; the duplicated tree builder and cover owner check
are shared now (model_catalog.py, backup.py back under 600 lines).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The version manager takes an optional Checkout, so the companion node pack is
handled like the plugin itself: api/tts_version.py finds it among the
custom_nodes folders by its node class and accepts only a git clone of the
official repository; releases are fetched only on "Check for updates", an
update checks out the newest stable tag and can be undone. A checkout without
the release tags (cloned before it, then pulled) is matched by ls-remote.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With many models, or models on a network drive, every visit to the scan
page read every folder and, after a restart, every new model's header
again (79 s for 1041 models on an AutoDL model disk).
api/model_heads.py keeps what each file's header says by real path, size
and time in user/anomalous/model_heads.json (at most 50,000 files, the
longest unused dropped first, written a few seconds after the last
change, only once setup_routes called keep_on_disk); get_metadata reads
headers through it, so a header is read once per file version.
scan_summary keeps the last summary (memory, and scan_summary.json when
it changed); ?quick=1 hands it out at once and the page shows it with
"showing the last counts while they are updated", then the fresh ones.
Requests meanwhile await one count (_fresh). The models found on Civitai
(components apart, at most 5000 kept) are listed by GET
/anomalous/scan_matched; the page's "Found on Civitai" count opens them,
with Civitai's name, base model, a box to find one by name and Open.
Checked on the isolated ComfyUI reading the real loras and checkpoints
read-only (scans stubbed; no file there changed): the last counts showed
at once also after a restart, the fresh ones replaced them, the found list
had 68 models with names, the box filtered, Open and Back returned to it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Before an online scan (the scan page, a card's radar, after a download or
an import), scan_connection.js asks GET /anomalous/civitai_check, which
tries both of Civitai's addresses. When neither answers, a dialog says
why and offers Scan from the files (the offline scan, so nothing waits on
every model), Set a proxy (Settings → Models, Network) or Cancel. A check
that reached Civitai is trusted for five minutes. anomalousConfirm takes
okClass, so this choice is not drawn as a delete.
A model that is a link with no info beside it shows the info beside the
file it links to (metadata._info_base): A1111's Civitai Helper leaves
.civitai.info there. Only read there; covers, edits and scans stay beside
the link.
Checked on the isolated ComfyUI with the check and the scan request
stubbed in the page: the dialog's three choices (from files posts
offline_only, set a proxy opens the Models tab, cancel posts nothing),
an answering Civitai scans online without asking, and a second scan
skips the check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1.60's own places that took a model file by name use the new helpers
too: the scan choice (model_file_within), the editor's covers, Tidy's
move (model_reference_within). Opening a folder keeps 1.60's "no file
name opens the folder". move_into tells a rename from a copy by where the
link is (lstat), so Tidy moving a linked model renames the link instead
of copying the model disk's file onto the server. The open-folder test
stubs through path_utils and also refuses "..".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On an AutoDL server every model in the models folder is a link into the
platform's shared, read-only model disk, named like 6b4679... without an
extension. Scans wrote the .info beside the link, but get_metadata
resolved the path and read beside the disk's file: a scanned model
showed as not scanned, under the disk's file name. Delete, edit, open
folder, MCP and saved-workflow lookups resolved the file too, so they
refused it as outside the models folder (or, for a link inside it, would
have acted on the file behind it).
path_utils.model_file_within joins a resolved folder with a bare name
(require_filename, which now refuses "." and ".." too);
model_reference_within resolves the subfolders of a relative path inside
the root, through links as before, and takes the file as named.
get_metadata reads beside the path it is given (abspath). Edits, renames
and deletes act on the link; nothing writes into a model file.
Checked with a link stood in by patching realpath (this computer cannot
make links): the info beside the link is read and the name is the
link's; "..", ".", "a/b" and a relative path leaving the root are refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On an AutoDL server civitai.com timed out while civitai.red, Civitai's
other address with the same API, answered in about a second, so scans
there fell back to guessing from the files.
civitai_client.api_hosts gives both addresses, the one that answered
last first; the scan's lookups, import's and Model Check's
(download_sources._civitai_json) and the network test ask the other one
when the first does not answer at all. An HTTP error is Civitai's own
answer and is not asked again. The test answers which host answered,
and Settings says so when it was civitai.red.
The pictures stay on image.civitai.com, which such networks often cannot
reach either: after COVERS_DOWN_AFTER (2) covers in a row that would not
download, a scan fetches no more covers instead of waiting on each.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a cloud server with 1041 models not scanned yet, on the platform's
network model disk, GET /anomalous/scan_summary took 79 s, each time the
page opened, and the page stayed blank meanwhile: it read each new
model's header (for "looks like no drawing model") one after another.
The headers are now read 16 at a time (HEAD_READERS) and through
get_metadata, whose cache keeps each answer until the file changes, so
the page opened again reads none of them and the picker after it finds
them there. Opened afresh, a count slower than 400 ms shows "Counting
your models…" instead of a blank page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cloud users had to find the plugin folder and make allow_remote_changes
themselves. GET /anomalous/access now also answers another device with
"command": the line that makes the file (touch, or echo off > on Windows,
which works in cmd and PowerShell), or removes it when it is there, with
this server's real path, quoted. The chip's dialog shows it with a Copy
button; when the browser refuses the clipboard the line is selected for
Ctrl+C. The line only runs where someone pastes it, on the server.
anomalousAlert takes options.copy; copyText moved from ui_share.js to
ui_dialog.js so both use it.
Checked behind the stand-in gateway: without the file the dialog offers
the line that makes it, with the file the line that removes it; both
Windows lines tried in cmd and PowerShell.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The header's button rules are !important, so a <button> chip lost its own
colours and size; it is a span again, with role="button", a tab stop and
Enter / Space opening its explanation. The start-up line about
allow_remote_changes is flushed at once, so it shows even when ComfyUI's
output goes to a file.
Checked on an isolated ComfyUI behind a stand-in gateway that adds
X-Forwarded-For (as a cloud platform's proxy does): without the file the
page is view only and changes answer 403; with it the chip reads "remote
changes on" in yellow, a scan choice is saved and reset, and a picked
offline scan runs to the end.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ComfyUI on a rented cloud server (AutoDL, RunPod) is only ever opened
from another device, so since v1.58.1 the plugin was view only there.
An empty allow_remote_changes file in the plugin folder, made on the
server and read once at start, now makes local_guard.may_change true for
every request; the middleware and the routes that check for themselves
(import, placement, scan choice, recipe focus) use it. is_local stays for
import's search of this computer's Downloads and for MCP. The page cannot
make or remove the file, and the file is git-ignored.
GET /anomalous/access answers {local, changes}. The header chip is now a
button: "view only" says what is refused and the two ways for cloud users
(SSH tunnel, the file); with the file it reads "remote changes on", in
yellow, and says that anyone with the address may change things.
docs/guides/cloud-server.md explains both ways and the risk; README,
AGENTS.md rule 4, backend.md and the CHANGELOG (v1.61.0) say so too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>