- browser.screenshot returns the page as an image content block (≤1280px wide, JPEG quality
configurable) for vision-capable upstreams; non-vision upstreams get a redacted text snapshot.
Login pages / password fields are marked sensitive and withheld unless a
browser.sensitive_screenshot grant exists and allowSensitive is passed.
- browser.click_at / type_at / scroll / wait act in screenshot pixels; the server converts via
viewport/DPR, requires automation control and an allowlisted host, and writes a thumbnail
evidence file (DATA_DIR/tmp/evidence) referenced from agent_run_event.
- browser.solve_challenge runs a bounded child agent_run (vision caps only, maxSteps ≤ 12,
budget), judges success from page facts, stops immediately on 2FA / SMS / device confirmation,
and otherwise moves the session to waiting with evidence and a takeover link.
- PiAgentRuntime passes modelImage outputs as pi-ai image content when model.input has image;
the ledger never stores image data; coordinate actions use per-invocation idempotency keys.
- Tests: image codec, runtime image content, E14 e2e (login solved, 2FA stop, step limit,
sensitive withheld). Real camofox 1.17.0 check on example.com recorded in the runbook.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- POST /pi/upstreams/{id}/models/refresh calls the provider's model list
endpoint (OpenAI-shaped /models, Anthropic /v1/models, Google
/v1beta/models, OpenRouter /api/v1/models; Radius via pi-ai refresh),
normalizes {id, displayName, contextWindow, supportsVision,
supportsTools, pricing} and stores it in pi_upstream.models (0044)
- GET .../models returns {source, fetchedAt, models[]}: remote merged
with the static catalog (remote first); failures (401/network/
unsupported) fall back to static with a one-line error
- POST /pi/models/probe for trying credentials before creating
- wizard and edit drawer: fetch button, searchable dropdown with
vision/tool tags, manual id, source line; default upstream needs a model
- tests: mock /models, 401, network failure; smoke step; runbook
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- compose: camofox service on by default (internal only, volume umt-camofox,
healthcheck, CAMOFOX_REPLICAS=0 to skip); app depends on it with
service_started/required=false and auto-registers camofox-env from
BROWSER_PROVIDER_URL / BROWSER_ACCESS_KEY / BROWSER_ENABLED
- docker/camofox/Dockerfile: builds upstream tag v1.17.0 (389c996); fixes the
upstream Dockerfile (missing postinstall.js before npm ci, sh -c not
forwarding SIGTERM so logins were never persisted on restart)
- adapter fixes from the real container: coordinate click via evaluate +
selector, focus typing with mode=keyboard, password values redacted from
ARIA snapshots/extract, downloads field names, 410 after browser restart
treated as lost tab; navigate reopens a tab in the same session
- verified-capability table keyed by upstream version (1.17.0 all ten);
other versions stay unverified; probe reports auth failures
- settings: browser providers block (create/edit/delete/probe, capability
table); API POST/PATCH/DELETE /browser/providers, POST .../probe
- scripts/browser-verify: umtbr stack + mock NexusPHP run of REQ 4.7.4 items
- docs: runbook browser §1/§7 (real-instance record), deploy §2.1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- GET /system/health splits processes into current instances (hosts with
a beat in the last 10 min, newest per host+kind, not stopped) and
retiredProcesses[]; 45s stale and level only consider current ones
- heartbeat writer marks stopped_at on graceful exit (migration 0043),
upsert clears it; pure api role now stops its writer on SIGTERM
- scheduled job system.heartbeat_cleanup (maintain, 1h) deletes rows
without a beat for 24h and samples older than 7 days
- status page: per-role alarms ("io worker 45 秒没有心跳"), collapsible
retired instances; glossary/runbook updated
- bump all members to 0.2.2, CHANGELOG [0.2.2]
Co-Authored-By: Claude Opus 5.5 <[email protected]>
GET /img/tmdb/{size}/{file} ported from MP ImageHelper / fetch_image
(sanitized cache path under DATA_DIR/cache/images, GLOBAL_IMAGE_CACHE
read switch, md5 ETag / 304, day-based cleanup job). Cache module adds
cache.sweep / cache.images_clean jobs and /cache/tmdb, /cache/images
list and clear endpoints. Library list/detail and the search palette
show posters with the neutral placeholder as fallback; metadata settings
get TMDB cache and image cache cards. Runbook docs/runbook/cache.md and
porting notes docs/porting/moviepilot-tmdb-cache.md.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- GET /auth/totp/setup returns the pending secret and otpauth URL (created
on first call, reused on refresh) for the console to render as a QR code.
- Enabling TOTP issues 8 one-time recovery codes (argon2id hashes in
admin_recovery_code); login accepts recoveryCode in place of totpCode,
disable accepts either, and recovery_codes regenerates with the password.
- Passkeys via @simplewebauthn/server: register/list/rename/delete under
the console session, public login options/verify that reuse the login
rate limit and session cookies. Challenges are one-time rows in
webauthn_challenge; RP ID and origins come from WEBAUTHN_RP_ID /
WEBAUTHN_ORIGINS, falling back to the request host. Signature counter
regressions are rejected. SessionInfo gains passkeyEnabled.
- --role rotate-master-key re-encrypts the secret table from
SECRET_MASTER_KEY to SECRET_MASTER_KEY_NEW one row per transaction,
skips rows already on the new key (resumable, swap keys to roll back),
logs progress and audits secret.master_key_rotate. Secrets now record
the master key fingerprint in kdf_params.kid.
- Runbook: recovery codes, passkey configuration, rotation steps and
rollback.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
agent.await and agent.spawn{await:true} now return immediately when
children are still running: the session closes, runAgent registers a
wake signal in agent_run.await_wake and waits with waitForFact without
holding an io/agent worker slot. A child that settles (terminal or
waiting) signals the parent's registered fact, so the parent resumes at
once instead of polling; the suspended call replays and the child
report becomes the tool result. agent.spawn is registered replay-safe;
a suspended spawn is recorded as failed in the ledger so the replay
re-runs it against the same child.
Migration 0037 also adds the admin_recovery_code, admin_passkey and
webauthn_challenge tables used by the account work.
E11 asserts the parent is suspended with no run held by any worker
while it waits, and covers spawn{await:true} end to end.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- glossary: 10 punctuation rules (full-width in Chinese, CJK/Latin spacing,
full-width parens around Chinese, single ellipsis, 「」 quotes, period rules)
- scripts/check-copy-punctuation.mjs: checks string literals, templates and JSX
text (not comments, types, keys, match/compare strings); --fix, --strict,
--by-file; wired into deno task ci as check:copy in warn mode
- --fix applied to web, server and contracts; files other lanes were editing
(subscriptions, sites, browser, agent, auth/account, inventory) left for them
- tests updated for the changed messages
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- G6: FileRoot.pendingCount/pendingPartial for download roots (same rule as the
unidentified filter, recursive, cached 5 min, invalidated by files.changed)
- G7: FileEntry.parsedHint from parseReleasePath (file + parent dirs); the web
subline uses it and no longer parses names itself
- G8: FileIdentifyCandidate.posterUrl (TMDB poster_path via adapter and
catalog.lookup, local entities from metadata.posterPath); thumbnails in the
identify panel
- S-7 contract: FilesIdentifyResult.remoteTaskId/remotePending,
MediaServerInventory.partial, EventType identify.completed with
IdentifyCompletedPayload; identify panel (useIdentifyLive) and organize wizard
merge remote results by root + path, buffering events that beat the response
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- GET /inventory/media-servers: per-instance Promise.allSettled, per-instance
concurrency cap (2) and timeout (8s); failed/timed-out instances are unknown,
response carries partial=true (field not yet in contracts)
- S-7: POST /files/identify returns local matches plus remoteTaskId/remotePending
and enqueues identifyRemote; the worker runs the TMDB lookups and publishes SSE
identify.completed {taskId,status,items,truncated,excludedBySize,error};
?wait=true keeps the synchronous path, capped at 15s overall
- catalog.lookup / release.search honour the caller's abort signal
- scripts/dev-db-clean.ts: dry-run by default, --yes to drop leftover test
schemas and w32-/w33-/fe4-/qa- prefixed entities; refuses non localhost:55433
- runbook: dev DB cleanup
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- GET /dashboard: automation.library {entities, movies, series, seasons,
episodes, assets} and automation.slotSatisfiedRate; home media overview
shows the counts instead of placeholders.
- GET /engine/runs: `status` accepts comma-separated values; new
`triggeredBy` filter (comma-separated) evaluated server-side by scanning
batches and paging with the last inspected row's cursor (scan cap 2000).
- Task stream: source filter and the "active" tab (running,pending) are now
one paginated server query.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Conflicts resolved: routes.ts keeps both /console/status (FE-1) and
/console/search (FE-3); smoke list keeps FE-1/FE-4/FE-5 routes plus the
rules tabs, search results and preferences→rules redirects. Top bar mounts
SearchTrigger and CommandPalette next to the FE-1 inbox bell.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- slot: new manual_verify event (attention -> verifying, with reason); a
successful organize of a download whose slot is in attention goes back
through verifying and lands by verify_passed on the actually imported
assets; the download's organize_blocked decision is resolved with
resolution {key: manual_organize | organize_retry, organizationRecordId} (U1)
- acquisition: plan purpose is upgrade only when the slot already has a
current asset; a movie slot gap is a fill (U7)
- identity: entities created from TMDB details get metadata fields projected
in the same transaction; TMDB aliases deduped by normalized text, site-title
aliases skipped when the text already exists (U9)
- pi: auth-mode test of a custom Base URL no longer marks the upstream ok, and
says to use the minimal completion to probe the endpoint (U2)
- sites: PUT /sites/{id}/credentials replaces same-kind secrets in place and
names new secrets with site id + random suffix (secret.name is unique; was a
500 on update); M-Team user data uses config/definition apiBaseUrl instead
of deriving api.<domain> from the site URL (Invalid URL on IP/local bases)
- copy: setup error says 初始化口令; backup job points at umt backup
- tests: schema expected tables add site_user_data; migration round-trip
timeout 60s; fake secrets honor label as name; vertical/resolve/pi/slot/
site-center tests for the above
- docs: 09-visual-qa W10-pre status table (U3 root cause: outbox events are
never mirrored to SSE)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- /console/rules tabs: rule groups, custom conditions, defaults, acquisition policies, suppressions
- editor: rule string parsed live via /rules/parse (position + suggestions) and synced both ways with layer cards; hard excludes; expectedRevision conflicts keep local edits
- preview against recent releases or pasted titles with pri / level / reasons / trace
- single-rule groups shown as preference tags; disable/delete writes suppression records
- /console/preferences redirects to /console/rules (origin passed through)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- site.list / site.health_check / site.refresh_login / site.user_data capabilities
over the W9-A site center services; outputs whitelisted (no secret refs,
cookies, passwords, login names); execute kinds need automation grant.
IDs use snake_case (CapabilityId regex); registry count 17 -> 21.
- settings.organize.minSizeBytes (default 50 MiB); file-manager identify /
organize and planImport take request > setting > port default.
- tests and runbook notes (sites, files).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- POST /files/suggest-name renders the effective template (entity > root >
global) with the same variables as organize: file name segment for files,
work/season dir segment for directories; identity_uncertain when no entity
- planImport plan.naming.templateSource/templateVersion now come from
effectiveNaming + the registered template version (fixes the leftover
DEFAULT_TEMPLATES reference after the naming chain landed)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- GET /naming/variables, POST /naming/preview (builtin/fileName/sourceRelease/asset samples,
line:col errors, warnings, binding-aware), GET/PUT /naming/templates,
PUT /library-roots/{id}/naming, PUT /media-entities/{id}/naming-override
- planImport picks templates entity > library root > global, registers the template
version for new path bindings; naming vars include en title, external ids, season/
episode metadata, slot and root names; optional lock of file-name template for bound works
- runbook: docs/runbook/naming.md
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- process_heartbeat / process_heartbeat_sample (0034): api / worker / worker:agent / all
upsert every 15s (event loop lag last/max, concurrency, running runs, SSE connections, memory)
- GET /system/health (admin): processes with 45s stale flag, SSE, DB latency/size/connections,
disks (DATA_DIR / library roots / download dirs, cache thresholds), mount sentinel checks
with IO timeout, engine pending/oldest/24h failure rate, schedules
- GET /system/health/history?hours=: bucketed lag / running / SSE series
- /engine/runs and /engine/runs/{id}: business {kind, refId, title, triggeredBy} joined from
run input / task / agent_run / tasks_run_meta / outbox; businessKind / businessRefId filters
- docs/runbook/ops.md: field meanings and alert thresholds
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- GET /search?q=&scope=all|media|releases|actions&limit=: local works (title_alias
exact/prefix/trgm + name prefix, slot status summary), TMDB candidates (cached,
localEntityId), cross-site releases, page/action jump list; tookMs per group
- cross-site: enabled sites via shared per-site limiter, 15s AbortSignal timeout,
site concurrency cap 4, same-query coalescing, 30s cache; partial + failedSites;
results upserted to source_release; inventory overlay via GET /inventory
- GET /search/releases: site/resolution/source/size/free/H&R filters, sort, cursor,
facets; shares the cache
- POST /search/releases/{siteId}/{siteReleaseId}/plan: forwards in-process to
POST /acquisition-plans (same auth, validation, audit, rejections)
- contracts/search.ts, runbook, tests (partial/timeout, coalesce, cache, overlay,
filters/sort, plan)
Co-Authored-By: Claude Opus 5.5 <[email protected]>