- Drawer tabs 当前/历史: searchable run list (status/source/time filters,
cursor auto-load), opens runs inside the drawer, keeps scroll and filters,
remembers the last tab; 在页面打开 is secondary
- Ended runs: append to resume, or 用同目标新建 with 先看方案再执行 toggle
- Session page: 88ch conversation column, turns grouped with one timestamp,
tool cards collapse to one line, 3+ activities fold; steps/subagents/budget
moved to a right rail (collapsible overview bar on narrow screens and in
the drawer), long model reports fold behind 展开全文
- Smoke covers the drawer history flow
Co-Authored-By: Claude Opus 5.5 <[email protected]>
RSS/list releases that match no subscription or local work no longer hit
TMDB or open a pending item; they go to the identity pool as `unmatched`.
TMDB is queried only on a local alias hit, a published new-title rule whose
prefilter passes, manual/Agent retry, search results, or when the new
setting identity.eagerTmdbLookup (default off) is on. Mirrors MoviePilot:
torrents are recognised only when matched to a subscription or searched,
and recognition failures are logged, not alerted.
- Pending items only for related releases (ambiguous after lookup, or
related to a local work but unresolved); same source + normalized title
merge into one decision ("来源「X」的 N 条资源待识别"), resolve/dismiss
applies to the whole group, members detach as they get resolved.
- Pool governance: unmatched reviews capped (daily, 3x), title_alias
insert trigger wakes similar entries, stale unmatched entries archived
after identity.poolArchiveDays (30); review idempotency keys include the
due time.
- Migration 0047_identity_pool: reason/outcome `unmatched`,
identity_candidate.normalized_title (+trgm index), wake trigger, and
cleanup of open identity_unresolved items with no related work
(dismissed, resolution=moved_to_pool).
- Inbox: group by type + work/source with collapse, hide info-only items
by default (minSeverity=warning), badge counts warning+ only; pool sheet
gets manual TMDB/local-work assign and dismiss; settings card for the
new identity settings.
- Runbook docs/runbook/identity.md.
Requires rebuild/restart (migration runs on start).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Adding a feed from a site page now only asks for the URL, purpose
(discover / track / promotion) and an optional note. POST /feed-sources
with siteId infers the site account (passkey holder first), records the
site definition version as parserVersion, defaults the name to
"<site> RSS" and polling to 30 minutes, and returns non-blocking
warnings when the URL host does not match the site. The dialog shows the
redacted URL, fetches once right after saving and reports the item count
or the error.
The generic feed form on the sources page suggests linking a registered
site when the URL host matches (can be undone). name is now optional.
Takes effect after the stack is rebuilt.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The worker's adapter still held a session the api process had closed,
so browser.navigate {siteId} from an agent run failed with "该站点/账号
已有未关闭的浏览器会话" (REAL-2 task 4). When the database has no live
session for the profile, stale in-memory ones are marked closed before
opening. Needs image rebuild.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
api and worker processes each keep adapter state in memory. After the
upstream reaped an idle tab and the api process reopened one, the agent
worker kept pointing at the lost tab and every screenshot / snapshot in
solve_challenge failed with not_found (REAL-2 task 4). loaded() now
restores from the database when its current tab differs. Needs image
rebuild.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With only a browser.solve_challenge grant, the vision child run was
denied browser.click_at, so the loop stopped after the first screenshot
(REAL-2 task 4). Steps inside a run spawned by solve_challenge are now
allowed; ancestors' allowlist and step budget still apply. Needs image
rebuild.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Built-in agent checks kept the config stored at first registration, so
check.pending never received decision.list / subscription.list added in
AG-3 and the patrol could not read pending items (REAL-2 scenario g).
Track config_customized (migration 0045); sync replaces config with the
code default unless the user PATCHed it. Needs image rebuild.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Uses the desktop iframe when the backend reports it available and the page is
a secure context; otherwise falls back to screenshot mode and says why.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
/api/v1/browser/desktop/{sessionId}/* proxies Selkies HTTP and WebSocket,
console sessions only. The holder of the active human lease gets the control
password, everyone else the view-only one; the WS bridge rechecks every 3s
and drops the connection on role change so the client reconnects downgraded.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Selkies baseimage (debiantrixie, digest-pinned) with camofox 1.17.0 copied
from camofox-browser:1.17.0, run headed under labwc so the desktop shows the
same Firefox the agent drives. s6 service waits for wayland-0 and depends on
svc-de and svc-dbus so shutdown persists login state. Selkies control/view
passwords derive from CAMOFOX_ACCESS_KEY. Compose defaults to the new image;
CAMOFOX_IMAGE=camofox-browser:1.17.0 switches back. Optional caddy (profile tls).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- tool results carry a budget reminder near limits; text before the last tool
call is dropped as report when cut by budget/abort
- usage persisted when a run suspends, so waiting sessions show spent tokens
- task page shows budget_exceeded via business.outcome instead of completed
- budget-exceeded decisions merge by root cause (decision / schedule / goal
template); new summaries added to investigate allowlist
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- three read-only summary capabilities (38 registered); task.inspect points
download task ids to download.inspect
- probe queries reject empty / single-letter / wildcard-only terms; externalId
documented as string; every description says when to use it
- download.plan requiresApproval uses coveringGrant (shared with authorize)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- 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]>
When download.recheck / task.retry was executed but errored (e.g. the
downloader was unreachable), applyRecoveryOutcome only looked at
succeeded actions and recorded outcome=not_attempted with the summary
"没有执行恢复动作", which is false. Record outcome=failed and name the
failed action instead. Needs image rebuild to take effect.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
torrentSource threw plain Error for a missing site or a site without an
adapter definition, so submitDownload retried to max attempts, left the
plan and slot reserved, and sweepReservedPlans re-enqueued it forever
(agent runs stayed waiting on goal-settled). Throw SiteError
(not_found / unsupported) so the plan fails and the slot is released.
Needs image rebuild to take effect.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The search page 订阅 button and the new-subscription wizard threw away
the TMDB id and created a title-only subscription, which stays pending
(metadata_pending) with no seasons, episode totals or coverage matrix
until some release happens to be identified. Call
/files/entity-from-candidate first and subscribe the entity; fall back
to title tracking only if that fails. Picking a TMDB row in the wizard
now creates the library entity even if the dialog is then cancelled
(idempotent per TMDB id).
Found in REAL-1b on the v0.2.2 test stack; needs an image rebuild to
take effect.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
catalog.lookup only returned the zh-CN localized title, so an English
release name (Dune.Part.Two.2024...) was rejected against "沙丘2" and
files/identify came back with zero candidates. Pass originalTitle
through catalog.lookup and use it as an extra title in ranking.
Found in REAL-1b on the v0.2.2 test stack; needs an image rebuild to
take effect.
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]>