diff --git a/CHANGELOG.md b/CHANGELOG.md index a9a8e1b8e..877ceca7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,25 @@ it shipped into a `## [x.y.z] - date` section of their own; the tag's ### Added +- `message_format.MessageFormatError` (an `AutoControlException` and a + `ValueError`); locales other than en/fr use Babel's CLDR plural rules when + Babel is installed. +- `multipart.MultipartError` (an `AutoControlException` and a `ValueError`). +- `dotenv.DotenvError`, raised by `dump_dotenv` for a key the parser would not + read back. +- `HotkeyDaemon.is_running`, `Scheduler.is_running` and + `TriggerEngine.is_running`: whether the engine's thread is alive. +- `usb_watcher.hold_default_watcher()` / `release_default_watcher()`: share + the default USB hotplug watcher by count. +- `rate_limit.named_bucket(name, rate, capacity)`, the named token buckets + `AC_rate_limit` and `ac_rate_limit` share. +- `box_format` (`"xywh"` / `"ltrb"`) on `cluster_grid`, `locate_cell`, + `AC_grid_cell` and `ac_grid_cell`, so `locate_all_image` boxes address cells. +- `repair_store(db=None)`: the repair store in `db`, or the one the process + shares. +- `HistoryStore.list_runs(statuses=...)` and `FINISHED_STATUSES`. +- `element_box()` (`utils.accessibility.element`): an element's + `(left, top, width, height)` from `bbox`, `bounds` or `x/y/width/height`. - The MCP server speaks the stateless protocol revision 2026-07-28 over stdio and HTTP, per request, beside the `initialize`-based ones: `server/discover`, `resultType` and caching hints on results, the `-32020`–`-32022` error @@ -84,6 +103,53 @@ it shipped into a `## [x.y.z] - date` section of their own; the tag's ### Changed +- `format_message` raises `MessageFormatError` for patterns ICU rejects + (unterminated argument, selector without `{...}`, no `other`, duplicate + selector, late `offset:`), and an unsupported locale raises instead of + getting English rules. +- `occurrences(count=, until=)` narrows the rule's own COUNT / UNTIL instead of + replacing it, and `parse_rrule` refuses `BYMONTHDAY` with `WEEKLY`, a numbered + `BYDAY` with `DAILY` / `WEEKLY`, and an unknown `WKST` (RFC 5545 3.3.10). +- `decode_jwt` / `AC_jwt_decode` refuse a token that carries `aud` unless the + policy's `audience` names one of its values (RFC 7519 4.1.3); pass the + audience to keep accepting such tokens. +- The JSON document MCP tools accept a root array as well as an object. +- Malformed JSON-RPC envelopes are answered `-32600`; `"id": null` is a + request, answered with `"id": null`. +- `evaluate_trajectory` raises `ValueError` for a rubric that is not an + object or has unknown keys; `scale_sweep` / `detect_scale` reject + non-finite or non-positive scales; `rbac.UserRecord` is frozen. +- `match_subpixel` reports `cx` / `cy` as the pixel-index centre plus the + fitted offset (half a pixel less than before). +- `RetryBudget` validates `jitter` (case-insensitive), `base_delay_s` and + `max_delay_s`; `AC_plan_retry_delays` accepts at most 10,000 attempts. +- Asset types refuse values they cannot hold (`3.7` or `True` as an `int`, + `"enabled"` as a `bool`); element locators refuse blank filters. +- Skills nested deeper than 50 fail the outermost `AC_skill_run`. +- `is_interactive_role` and `flatten_tree` recognise AT-SPI, macOS AX and ARIA + role names as well as UIA; `tab_order` leaves out disabled controls. +- `effect_near_point` / `classify_effect` measure `radius` as a circle. +- `diff_rows` / `cell_changes` raise `ValueError` for an empty key or a row + without a key column; `score_step_rule_based` raises for an unknown effect; + `verify_totp` raises `TOTPError` for a negative window. +- `RenewalScheduler` rejects a `check_interval_s` that is not finite and + positive; `build_timeline` / `critical_steps` reject NaN, infinite and + negative durations; `verify_artifact` stores non-text values as JSON. +- `find_color_regions`, `segment_hsv` and `dominant_hue_regions` return + screen coordinates for blobs found in a grabbed `region` (they were + relative to the region's corner); a supplied `haystack` keeps its pixels. +- `psi` / `ks_two_sample` / `detect_drift` raise `ValueError` for NaN. +- `profile_rows` reports `mixed` for a column with no single type, and + `infer_schema` gives it no `type` rule; int columns have exact bounds. +- `diff_runs` compares a 0 s step as 0.1 s (`ratio` `None`) and skips + non-finite durations. +- `find_repeated_sequences` / `mine_action_log` and `Baggage` refuse empty + lengths, counts and keys with `ValueError`. +- `validate_config` / `ConfigSchema.from_dict` raise `ValueError` for an + unknown field type, and a `str` field rejects `None` and containers. +- Search terms: runs of kana, CJK ideographs and Hangul are indexed as + character bigrams, so a word is found inside a sentence. `mode="tfidf"` + uses a smoothed IDF, so a term in every document still matches. - An MCP HTTP request whose `MCP-Protocol-Version` header names an unsupported version is still a 400, now with a JSON-RPC `UnsupportedProtocolVersion` (`-32022`) body listing the supported @@ -323,6 +389,18 @@ it shipped into a `## [x.y.z] - date` section of their own; the tag's ### Security +- `ac_egress_reset` and `ac_approve_artifact` are destructive, so the + confirmation gate asks before loosening egress or overwriting an approved + baseline. +- The rbac user store saves a change before applying it, so a refused or + failed save no longer takes effect in memory, and it hands out copies of + its records. +- Failure bundles mask secrets inside nested mappings, tuple actions and + objects serialised through `repr`. +- `AC_resolve_ref` / `AC_resolve_refs` and the MCP tools `ac_resolve_ref` / + `ac_resolve_refs` refuse `secret://` references: the resolved secret was + returned into executor records and MCP results. Use `${secrets.NAME}` in + the step that needs the value, or `resolve_ref` from Python. - The egress policy matches hosts by their IDNA encoding, so soft hyphens, fullwidth characters and ideographic full stops no longer slip past a deny list. @@ -427,6 +505,165 @@ it shipped into a `## [x.y.z] - date` section of their own; the tag's ### Fixed +- French `selectordinal` follows CLDR (`21e`, not `21er`), `fr_FR` / `fr-CA` + use the French rules, and `=1.0` matches 1. +- `attributes_to_otlp` / `spans_to_otlp` write a `bytes` attribute as OTLP + `bytesValue` (base64), and `is_problem` / `parse_problem` match the + `application/problem+json` media type exactly instead of as a substring. +- RRULE: a yearly `BYDAY` ordinal counts within the year when `BYMONTHDAY` is + present and `BYMONTH` is not, DTSTART with microseconds stays the first + occurrence, DAILY applies `BYSETPOS`, long intervals and large counts are no + longer cut short, and year 9999, a malformed `UNTIL` or a naive `now` no + longer raise builtin errors. +- `GettextCatalog.compile_mo` / `to_mo_bytes` leave untranslated entries out + of the `.mo`, as `msgfmt` does, so readers fall back to the `msgid` instead + of showing an empty string. +- `build_multipart` refuses a boundary RFC 2046 does not allow or that a part + contains (a field value could inject a part), redraws a generated one, and + `parse_multipart` reads only the `boundary` parameter. +- `SSEParser.feed` no longer re-splits the whole partial line on every chunk: + a 2 MB `data` line in 1 KB chunks took 10.75 s and now takes 0.02 s. +- `parse_dotenv` keeps a value that starts with `#` (`COLOR=#ff0000`), keeps + the trailing whitespace of a multi-line quoted value's lines, skips a leading + BOM, and parses an unclosed quote in linear time (20k lines took 190 s). +- W3C trace context: a tracestate value keeps its leading spaces, a member that + breaks the value grammar (including a CR LF) is discarded on parse and refused + on format, a 16k-member header parses in linear time, `format_traceparent` + validates a hand-built context, and `child_context` clears unknown flag bits. +- `parse_link_header` / `next_url` follow RFC 8288 Appendix B: a `<` in an + unquoted parameter value no longer swallows the next link, a valueless + parameter is kept as `""`, and relations split on space and tab only. +- `CookieJar`: `Expires` follows the RFC 6265 5.1.1 date algorithm (an + out-of-range year no longer raises `OverflowError`), the last valid `Max-Age` + or `Expires` decides, `Max-Age` takes ASCII digits only, and a `Set-Cookie` + with a control character is ignored instead of reaching the `Cookie` header. +- `normalize_url` / `canonicalize_url` resolve a percent-encoded `..`, never + turn a path that begins with `//` into a host, normalise the query before + sorting it, and keep an empty `?` or `#` (RFC 3986 6.2.2, 3.3, 6.2.3). +- JWT: an empty or non-string key raises `JwtError` instead of signing and + verifying (or raising `TypeError`), and non-JSON claims such as a `datetime` + or `NaN` raise `JwtError`. +- `is_fresh` counts the `Age` header `store_validators` now records (RFC 9111 + 4.2.3), a repeated `Cache-Control` directive keeps its first value, and + `is_not_modified` answers `False` for a non-numeric status. +- `json_query`: a surrogate pair written as two `\u` escapes finds its key; + `[01]`, `[-0]`, an index past 2**53-1, a bald `..`, an unpaired surrogate and a + raw control character in a quoted name raise `ValueError` (RFC 9535). +- JSON Schema `pattern` / `patternProperties` match as ECMA-262 does: `$` no + longer matches before a trailing newline, `\d` / `\w` / `\b` are ASCII, and + `\p{...}`, `\cX`, `\u{...}`, `[]` / `[^]` and named groups are understood. + `multipleOf` no longer raises `OverflowError` past float range. +- The VLM tab no longer freezes the window while the model answers, and the + Hotkeys, Scheduler and Triggers tabs show an engine started from Tools > Start + or a script as running. +- The USB Devices tab and the passthrough panel no longer run a ~5 s device + enumeration on the GUI thread, and one no longer stops the other's hotplug + watcher; email Poll now and Admin broadcast no longer freeze the window. +- Every MCP `tools/call` is answered with `isError: true` whatever the + tool raises; a plain HTTP POST answers in its body even on a concurrent + server; an unwritable audit log no longer fails a tool that ran. +- Auto-click refuses an interval below 1 ms; the Assertions, Data Source, + LLM Planner, Self-Healing, Media Checks, Trace Replay and Variables tabs + show framework and decode errors instead of leaking them; a recording that + did not start is reported; template crops write into non-ASCII folders; + the Live HUD no longer logs its own sampling; the View > Tabs menu no + longer leaks on every rebuild. +- `VariableScope.update_many` sets nothing when any name is invalid. +- Sub-pixel and scale matching accept 16-bit and float images and contain + OpenCV errors; one unscorable scale no longer aborts a sweep. +- An aborted `drag_path` releases where the pointer stopped; a `click` step + without a point clicks in place instead of at (0, 0). +- `AC_rate_limit` takes a changed rate or capacity for a reused name. +- `wait_until_app_idle` never sleeps past its timeout or spins. +- Reading an in-memory store while another thread writes it no longer + raises; hand-written skill tags given as a string stay one tag. +- `ac_run_saga` reports `compensation_errors`. +- `fuse_elements`, `observation_index` and `classify_effect` read elements + with `bounds` or without a size; unchanged zero-area elements are no + longer reported as changes. +- `image_quality` and `motion_regions` measure 16-bit and float frames in + 8 bits and contain OpenCV errors. +- `client_point` / `get_client_rect` return `None` for a minimized window; + `wait_for_focus_change` refuses a NaN timeout. +- Every `AC_*` flag parameter reads a string by its spelling: `"false"`, + `"no"`, `"off"` and `"0"` are off. 85 adapters and two flow commands + took any non-empty string as on. +- `set_field_text` clears with Backspace on macOS; `deep_merge` no longer + shares nested values with its inputs; co-failure clustering keeps a + one-name run whole; TOTP time errors are `TOTPError`. +- `AC_repair_*` commands and MCP tools without `db` see each other's + suggestions. +- Quarantine stores sharing a file no longer overwrite each other's names. +- ACME renewals never overlap after a restart; force mode accepts NumPy + bboxes; a plugin that fails to import is reported, not raised. +- Test selection, sharding and flakiness read finished runs, so runs + killed mid-flight no longer hide a flow's history. +- `find_lines` finds lines shorter than 50 px and reads 16-bit images; + `annotate_screenshot` keeps 16-bit and float tones; profiles survive huge + values; `True` and `1` are distinct values. +- On a Retina Mac, `[left, top, right, bottom]` region captures (colour, + HSV, histogram, SSIM, contrast, colour waits, QR, VLM) are taken in points, + so region results line up with the mouse instead of doubling. +- Script Builder `region` hints name the convention each command reads: + `[x, y, width, height]` for the matchers, OCR and most vision commands, + `[left, top, right, bottom]` for the colour, histogram, SSIM and QR + commands. Most said left, top, right, bottom whatever the command read. +- On Windows, `[left, top, right, bottom]` regions on a monitor other than + the primary one were captured black by the colour, HSV, histogram, SSIM, + contrast, preprocess, colour-wait, QR, colour-stats, VLM and MCP screenshot + paths. They capture every monitor through the new + `cv2_utils.region_capture.grab_screen_region`. +- `AC_wait_actionable` samples stability on the monitor where the target was + matched, instead of reading a target off the primary monitor as stable. +- `match_color` / `match_color_all` return screen coordinates for a region + search, score only a template's coloured pixels, and cap their candidates. +- Mark labels stay beside marks at negative coordinates and inside bounds + when crowded. +- `snapshot_json` matches its own payload; `match_json` takes one ignored + path as a string. +- Action lists nested in `AC_circuit_call`, `AC_with_modifiers`, + `AC_bulkhead_run` and similar commands run on the executor running the + outer list, so `run_on_devices` keeps each device's variables. +- Dark-theme widgets classify correctly in `classify_icon`; `ß` collates as + `ss`; checksums accept whole floats and numbers past 4,300 digits; + `wait_for_composition_commit` never sleeps past its timeout; a unique + `ccorr_normed` match is no longer reported ambiguous; OCR text satisfies + `text_present`; `flow_order` reads `bbox`, `bounds` and match objects. +- Set-of-marks numbers OCR boxes, and grounding consensus votes by each + element's real geometry; consensus refuses non-finite points. +- Borderless tables are found left of x = 0 (monitors left of the primary). +- The recording editors leave `${var}` sleeps alone; the flow debugger takes + an `{"auto_control": [...]}` file; a compliance framework can be passed + as one string. +- Python 3.15 turns UTF-8 mode on by default. `AC_shell_to_var`, the MCP + `shell_command` tool, `ShellManager` and the remote host's `status` still + decode a Windows console program's output in its code page there, + instead of as UTF-8: `sc query` raised, and the others returned + replacement characters. The SBOM skips a distribution that has no + metadata instead of failing on 3.15, or listing it as `unknown` before. +- WebRTC host annotations are drawn at the viewer's position on the + captured screen, including other monitors and scaled displays. +- Remote desktop hosts map viewer input and the broadcast cursor through + the captured frame's origin, so clicks land correctly on a second monitor, + a capture region, or a virtual desktop that extends above or left of the + primary screen. `dispatch_input` takes an optional `origin`. +- Perceptual diff contains OpenCV errors and checks its budget unrounded. +- `find_text_regions` / `find_text_lines` return screen coordinates for a + region search; `match_rotated_all` returns every match, not one per pose. +- Actionability treats a falsy enabled probe as disabled, accepts NumPy + stability tokens, and can pass with no stability wait. +- The generated project's keyword example validates and runs. +- Grid fill counts a spanning box once, at its anchor cell, and reads + Tesseract's box shape. +- Semantic replay keeps the recorded point for an unnamed or off-screen + anchor, and recording anchors on the clicked control, not its window. +- A failed self-healing lookup is a failed step instead of aborting replay. +- Video motion checks clip their region and stream frames; preprocessing + handles 16-bit, single-channel and float images; OpenCV errors in both are + recorded as step failures. +- HTML report generation is linear in the number of records. +- D-Bus socket addresses are unescaped, and unmarshallable values raise + `DBusError`. - The Flow Editor opens action files saved with a BOM, keeps a wrapped file's other keys on save, and writes atomically. - The region selector (template cropping, OCR / screenshot / WebRTC regions) diff --git a/CLAUDE.md b/CLAUDE.md index 7e8826f65..a484951b5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -163,7 +163,7 @@ These tools own the generic rules (bare `except`, mutable defaults, unused names Suppressions need an inline justification — `# noqa: # reason: ` or `# nosec B404 # reason: `. Blanket file- or module-level suppressions are forbidden. -A broad `except` (`Exception`, `BaseException`, bare) that swallows rather than re-raises needs `# reason:` on its own `except` line. `test/unit_test/headless/test_broad_except_reasons.py` fails CI on one that does not — the linters cannot: CI runs `ruff` with its default rules (no `BLE`) and does not run pylint. +A broad `except` (`Exception`, `BaseException`, bare) that swallows rather than re-raises needs `# reason:` on its own `except` line. `test/unit_test/headless/test_broad_except_reasons.py` fails CI on one that does not — the linters cannot: CI runs `ruff` with the rule set pinned in `pyproject.toml` (`E4`/`E7`/`E9`/`F` plus `E501`; no `BLE`) and does not run pylint. ## Stage commits, `Progress.md`, `docs/updates/` and `architecture.md` diff --git a/Progress.md b/Progress.md index 9aebbe833..cfb9295a3 100644 --- a/Progress.md +++ b/Progress.md @@ -176,6 +176,9 @@ pip install --dry-run --only-binary=:all: --platform win_arm64 --python-version - **部分超出螢幕的 `screen_region` 被補黑**:`monitor_layout/logical_frame.py:143` 沒有先和畫面取交集,PIL `crop` 補零,可能回傳螢幕外的命中;寬或高為負時丟裸 `ValueError`。做法:先取交集(回傳裁過的原點),非正的寬高丟框架例外。 - **OCR 跨框比對漏掉從長框中段開始的字串**:`ocr/text_span.py:330` 的視窗超過「目標長度+40」就整個丟掉最左框,即使目標從那框開始;`"Save As"` 在長句框之後就找不到。做法:只有剩下的部分仍不短於目標時才丟左框。 - **負座標的中心點差一**:`wrapper/auto_control_image.py:48`、`:73` 的 `int((x1 + x2) / 2)` 向零截斷。做法:`(x1 + x2) // 2`。 +- **Unicode 打字把換行與 Tab 當字元送**:`utils/text_unicode/text_unicode.py:49` `plan_unicode_keys("a\nb\tc")` 送出碼位 10 與 9,多數程式會丟掉 Unicode 的 LF;`write` 早就把它們對到 Return/Tab(`WRITE_CONTROL_KEYS`)。這個模組被 `wrapper/auto_control_keyboard.py:27` 載入。做法:控制空白改成按鍵。 +- **鍵盤配置表的 Shift 半邊與非美式鍵**:`utils/keyboard_layout/keyboard_layout.py:98-100` 的 Shift 半邊是死鍵時退回未按 Shift 的字(美式國際配置的 Shift+6 回 `'6'`,契約是回 `None`);`:96` 只翻譯美式鍵碼,德/法/北歐鍵盤的 `VK_OEM_102`(0xE2)與英式 `VK_OEM_8` 永遠沒有標籤;`:68-74` 把原型設在全程序共用的 `ctypes.windll.user32` 上,之後別的呼叫者用 `c_ubyte` 陣列呼叫 `ToUnicodeEx` 會 `ArgumentError`。Jeffrey_RPA 的 `_gui_control.py:3322` 呼叫 `ac.foreground_keyboard_layout()`。做法:死鍵半邊回 `None`;候選鍵碼加上 0xDF、0xE1、0xE2…;改用私有的 `ctypes.WinDLL("user32")`。 +- **剪貼簿格式名稱 `None`**:`utils/clipboard_formats/clipboard_formats.py:46` `_coerce` 把 tuple/list 描述的 `None` 名稱變成字串 `"None"`,dict 形式卻是 `""`,`diff_formats` 因此回報有變動。Jeffrey_RPA 的 `_gui_control.py:1126` 呼叫 `ac.clipboard_formats()`。做法:兩種形式都把 `None` 正規化成 `""`。 **解除條件**:Jeffrey_RPA 沒有批次在跑(`webrunner.pid` 的行程不在、Discord bot 停止);改完在 Jeffrey_RPA 跑 `test/test_je_facade.py`。 @@ -429,6 +432,62 @@ MCP 工具的檔案參數(`path`、`file_path`、`db`、`image_path`、`golden **為什麼要拍板**:根目錄從哪來(新的環境變數、沿用 `roots/list`、或兩者),唯讀模式要不要預設開啟; 預設開啟會讓現有讀取工作區外檔案的用法失效。 +同一個問題也在 `ac_resolve_ref`/`ac_resolve_refs`(`_factories.py:7182`,標為 `READ_ONLY`):`file://` 沒有 +`base_dir` 限制,`env://` 可讀任何環境變數,包括放 API 金鑰的那些,結果直接回給模型。`secret://` 已經拒絕; +`env://` 要不要改成允許清單、`file://` 要不要套同一個根目錄,跟上面一起決定。 + +--- + +## Windows 的 DPI 感知是系統層級,混合 DPI 的螢幕座標被虛擬化 + +`DECIDE` — 改成 per-monitor 會移動那些螢幕上的所有座標,Jeffrey_RPA 在那些螢幕上錄的座標與樣板要重錄 + +`windows/screen/win32_screen.py:50` 在 import 時呼叫 `SetProcessDPIAware()`,那是系統 DPI 感知,不是 +per-monitor。DPI 與主螢幕不同的螢幕會被 Windows 虛擬化:本機第二螢幕 125%,實際 1920×1080,但 Win32、 +`mss` 與 Qt 都回報 `(1920, -164, 1536, 864)`,截圖是 Windows 縮小過的影像,那個螢幕上的樣板比對與 OCR +用的是模糊的畫面。同檔註解說之後「所有 Win32 座標查詢都會拿到實體像素」,只在主螢幕 DPI 的螢幕上成立。 + +**做法**:先呼叫 `SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)`,失敗再退回 +`SetProcessDPIAware()`,並改正註解;`utils/monitor_layout` 的換算與 `gui/_screen_geometry.py` 一起檢查。 + +**為什麼要拍板**:這個檔在 Jeffrey_RPA 正在跑的截圖路徑上。換成 per-monitor 之後,縮放螢幕上的座標與截圖 +尺寸都會變,既有的樣板和錄好的座標在那些螢幕上會失準。 + +--- + +## `pil_screenshot`/`screenshot` 的區域擷取在 Windows 只看得到主螢幕 + +`BLOCKED` — 要改的是 Jeffrey_RPA 正在跑的截圖路徑(`cv2_utils/screenshot.py`、`wrapper/auto_control_screen.py`、`utils/window_capture/window_capture.py`),依工作區規則在它執行期間不動 + +`cv2_utils/screenshot.py:58` 把 `screen_region` 交給 `ImageGrab.grab(bbox=...)`,Pillow 在 Windows 沒帶 +`all_screens=True` 時只擷取主螢幕再裁切,主螢幕外的部分補黑。分析類指令已改走 +`cv2_utils/region_capture.grab_screen_region`(顏色、直方圖、SSIM、對比、顏色等待、QR、VLM、MCP 截圖), +下面這些仍是舊路徑,在主螢幕左側或上方的螢幕得到全黑影像: + +- `pil_screenshot(screen_region=...)`、`screenshot(screen_region=...)` 與 `AC_screenshot`。 +- `utils/pytest_plugin/keywords.py:41` `keyword_screenshot`(與 `AC_screenshot` 同一語意,一起改)。 +- `utils/window_capture/window_capture.py:66` `capture_window`:視窗在副螢幕時截到黑的。 +- `utils/set_of_marks/set_of_marks.py:121` 把標記畫在 `pil_screenshot()`(只有主螢幕)上,副螢幕的元件沒有標記。 + +**做法**:`pil_screenshot` 的區域路徑在 Windows 改走 `grab_screen_region`(它已處理 DPI 與負座標), +`capture_window` 同樣;set-of-marks 改用 `grab_logical(None)` 並把原點加回標記座標。 + +--- + +## macOS 的 `grab_logical` 在 Retina 上是像素座標,而且只看得到主螢幕 + +`BLOCKED` — `utils/monitor_layout/logical_frame.py` 在 Jeffrey_RPA 正在跑的截圖路徑上,依工作區規則在它執行期間不動 + +`grab_logical` 在 macOS 呼叫 `ImageGrab.grab(all_screens=True)`。讀 Pillow 12.3.0 的 darwin 分支:`all_screens` +不被使用,`screencapture -x` 只擷取主螢幕;Retina 螢幕的影像是點座標的 2 倍(Pillow 文件:「screen captures will +be at 2x if on a Retina screen」,`scale_down=True` 只在帶 `bbox` 時生效)。`logical_virtual_rect` 只讀 Windows 的 +`GetSystemMetrics`,所以 macOS 不縮放:樣板比對、OCR 與其他走 `grab_logical` 的定位,在 Retina 上回傳的座標是 +滑鼠(Quartz,點座標)的 2 倍,副螢幕上的目標則找不到。GitHub 的 macOS runner 是 1x 虛擬螢幕,CI 測不到。 + +**做法**:darwin 上以 `CGDisplayBounds`/`CGGetActiveDisplayList` 取得各螢幕的點座標範圍;有 `region` 時交給 +`ImageGrab.grab(bbox=..., scale_down=True)`(`screencapture -R` 接受全域點座標,包括負值),整個桌面則逐螢幕擷取、 +各自縮到點座標後拼接,原點取所有螢幕的最小 x/y。需要在 Retina Mac 上實測。 + --- ## 遠端桌面的 viewer 槽位由各面板共用 diff --git a/architecture_explore.md b/architecture_explore.md index 63987baf6..51c059478 100644 --- a/architecture_explore.md +++ b/architecture_explore.md @@ -19,8 +19,8 @@ iOS(WebDriverAgent)。核心能力是滑鼠/鍵盤控制、影像辨識、 | 指標 | 數值 | | --- | ---: | -| Python 模組總數(含周邊子專案) | 1,059 | -| 程式碼總行數 | 154,389 | +| Python 模組總數(含周邊子專案) | 1,063 | +| 程式碼總行數 | 156,916 | | `je_auto_control/utils/` 子套件數 | 310 | | `AC_*` 動作指令數(`known_commands()` 實測) | 775 | | 套件門面 `__all__` 公開名稱數 | 1,244 | @@ -272,7 +272,7 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 ### 5.4.1 執行引擎與腳本資產 -> 24 個套件、約 14,553 行。 +> 24 個套件、約 14,668 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | @@ -283,19 +283,19 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 | `utils/dag/` | 536 | 跨主機 DAG 編排器(圖模型 + runner) | | `utils/decision_table/` | 112 | DMN 風格決策表:規則 + 命中策略,把分支外部化 | | `utils/deterministic/` | 116 | 決定性執行控制:固定亂數種子 + 凍結時鐘 | -| `utils/executor/` | 9,504 | **核心**。`Executor` 指令分派表(775 個 `AC_*`)、參數插值、乾跑、逐步 callback;`flow_control` 提供 34 個區塊指令(迴圈/分支/try/巨集/變數) | -| `utils/flow_debugger/` | 155 | action list 的單步除錯器與追蹤器 | -| `utils/input_macro/` | 451 | 定時輸入事件:錄製結果的整形(`timeline`/`InputRecorder`,Windows 與 macOS 共用)、重播與宣告式輸入序列 DSL | +| `utils/executor/` | 9,539 | **核心**。`Executor` 指令分派表(775 個 `AC_*`)、參數插值、乾跑、逐步 callback;`flow_control` 提供 34 個區塊指令(迴圈/分支/try/巨集/變數) | +| `utils/flow_debugger/` | 166 | action list 的單步除錯器與追蹤器 | +| `utils/input_macro/` | 462 | 定時輸入事件:錄製結果的整形(`timeline`/`InputRecorder`,Windows 與 macOS 共用)、重播與宣告式輸入序列 DSL | | `utils/json/` | 99 | action JSON 檔讀寫與正規化格式化(`fmt --check` 的後端) | -| `utils/json_store/` | 271 | JSON 字典檔持久化的共用小工具(內部管線) | +| `utils/json_store/` | 278 | JSON 字典檔持久化的共用小工具(內部管線) | | `utils/loop_guard/` | 158 | 機械式卡死迴圈偵測(agent loop 用) | | `utils/plugin_loader/` | 147 | 掃描外部 Python 外掛目錄並註冊其 `AC_` callable | | `utils/plugin_sdk/` | 80 | 外掛 SDK:透過 entry points 發佈/載入第三方 `AC_*` 指令 | -| `utils/project/` | 187 | 專案腳手架:建立目錄結構與範本 action 檔 | -| `utils/recording_edit/` | 150 | 不重錄的前提下裁切/過濾/縮放已錄製的 action list | -| `utils/saga/` | 100 | Saga 協調器:失敗時以 LIFO 補償動作回滾 | -| `utils/script_vars/` | 211 | 執行期變數作用域與 `${var}` / `${secrets.*}` 插值 | -| `utils/skill_library/` | 115 | 具名可重用 action 序列(skill)的持久化倉庫 | +| `utils/project/` | 183 | 專案腳手架:建立目錄結構與範本 action 檔 | +| `utils/recording_edit/` | 165 | 不重錄的前提下裁切/過濾/縮放已錄製的 action list | +| `utils/saga/` | 103 | Saga 協調器:失敗時以 LIFO 補償動作回滾 | +| `utils/script_vars/` | 218 | 執行期變數作用域與 `${var}` / `${secrets.*}` 插值 | +| `utils/skill_library/` | 145 | 具名可重用 action 序列(skill)的持久化倉庫 | | `utils/state_machine/` | 268 | 宣告式有限狀態機驅動 action JSON | | `utils/stubs/` | 311 | 為 `AC_*` 指令面產生型別 stub | | `utils/test_record/` | 70 | 全域測試紀錄單例,記錄每個動作的參數與例外 | @@ -303,7 +303,7 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 ### 5.4.2 框架基礎設施 -> 14 個套件、約 3,014 行。 +> 14 個套件、約 3,053 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | @@ -311,58 +311,58 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 | `utils/config_bundle/` | 424 | 使用者設定的單檔匯出/匯入 | | `utils/critical_exit/` | 132 | 監看緊急停止鍵的守護執行緒,用於中止失控腳本 | | `utils/diagnostics/` | 330 | 跨子系統的「一切正常嗎」健檢,附 `python -m` 進入點 | -| `utils/dbus_client/` | 703 | 只用標準函式庫的 D-Bus session bus 客戶端。原本在 `linux_wayland/` 為 portal 交握而寫,AT-SPI 無障礙後端成為第二個使用者後搬到這裡(`utils/` 在分層上在各 OS 套件之上) | +| `utils/dbus_client/` | 714 | 只用標準函式庫的 D-Bus session bus 客戶端。原本在 `linux_wayland/` 為 portal 交握而寫,AT-SPI 無障礙後端成為第二個使用者後搬到這裡(`utils/` 在分層上在各 OS 套件之上) | | `utils/exception/` | 213 | **例外階層根**。所有錯誤繼承 `AutoControlException`,加上集中式錯誤訊息字串(`exception_tags`) | -| `utils/failure_bundle/` | 219 | 可攜、已遮蔽的失敗診斷 ZIP(截圖 + 診斷 + log 尾段) | +| `utils/failure_bundle/` | 229 | 可攜、已遮蔽的失敗診斷 ZIP(截圖 + 診斷 + log 尾段) | | `utils/file_process/` | 40 | 目錄檔案列舉(`execute_dir` 的後端) | | `utils/logging/` | 168 | `autocontrol_logger` 單例 + 家目錄共用記錄檔 handler(`JE_AUTOCONTROL_LOG_FILE` 可改) | -| `utils/package_manager/` | 101 | 動態載入套件並把 executor 注入其中 | +| `utils/package_manager/` | 103 | 動態載入套件並把 executor 注入其中 | | `utils/path_guard/` | 114 | 命令列傳入路徑的正規化與邊界檢查(防路徑穿越) | | `utils/platform_id/` | 62 | 作業系統家族的單一判定點。`sys.platform` 原本在一百多處跟字面清單比對,而那些清單都沒有 BSD;`is_x11_unix()` 問的是「這是不是 X11 unix」,這才是守衛一直想問的問題 | -| `utils/shell_process/` | 263 | `ShellManager`:以 argv list 執行外部命令(禁用 `shell=True`) | +| `utils/shell_process/` | 279 | `ShellManager`:以 argv list 執行外部命令(禁用 `shell=True`) | | `utils/start_exe/` | 36 | 啟動另一個執行檔行程 | ### 5.4.3 排程、觸發與背景監看 -> 11 個套件、約 4,210 行。 +> 11 個套件、約 4,300 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | -| `utils/hotkey/` | 846 | 全域熱鍵守護行程,把 OS 層熱鍵綁到 action 檔(Win/macOS/X11 三後端) | +| `utils/hotkey/` | 852 | 全域熱鍵守護行程,把 OS 層熱鍵綁到 action 檔(Win/macOS/X11 三後端) | | `utils/idle_keepawake/` | 245 | 偵測使用者閒置時間並在無人值守執行期間阻止系統睡眠 | | `utils/lock_session/` | 166 | 鎖定工作站、等待解鎖並分類鎖定狀態轉換 | | `utils/observer/` | 234 | 反應式畫面觀察者,在出現/消失/變化時觸發 | -| `utils/recurrence/` | 398 | RFC 5545 重複規則解析與發生時間展開 | -| `utils/scheduler/` | 500 | 間隔式與 cron 式的 action JSON 排程器 | +| `utils/recurrence/` | 470 | RFC 5545 重複規則解析與發生時間展開 | +| `utils/scheduler/` | 506 | 間隔式與 cron 式的 action JSON 排程器 | | `utils/session_guard/` | 62 | 驅動輸入前先偵測工作階段是否已鎖定/非互動 | -| `utils/triggers/` | 1,377 | 事件驅動觸發引擎:影像/視窗/像素/檔案/webhook/IMAP 郵件 | +| `utils/triggers/` | 1,383 | 事件驅動觸發引擎:影像/視窗/像素/檔案/webhook/IMAP 郵件 | | `utils/voice/` | 97 | 語音指令路由:把辨識到的語句對應到 `AC_*` action list | | `utils/watchdog/` | 195 | 背景彈窗/中斷看門狗,供無人值守自動化 | | `utils/watcher/` | 90 | 無頭輪詢原語:滑鼠位置、像素顏色、log tail | ### 5.4.4 輸入模擬與動作品質 -> 22 個套件、約 2,768 行。 +> 22 個套件、約 2,843 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | | `utils/act_in_view/` | 86 | 先把目標捲進視野,待其可操作後再動作 | -| `utils/act_modes/` | 68 | actionability 閘門之上的 trial/force 動作模式 | -| `utils/action_effect/` | 110 | 判定一個動作是否真的產生效果,並歸因到目標區域 | +| `utils/act_modes/` | 69 | actionability 閘門之上的 trial/force 動作模式 | +| `utils/action_effect/` | 109 | 判定一個動作是否真的產生效果,並歸因到目標區域 | | `utils/action_grounding/` | 83 | 動作前的接地守衛(邊界檢查 + 吸附到元素) | -| `utils/actionability/` | 168 | 動作前就緒閘門(可見 + 穩定 + 啟用 + 未被遮擋) | +| `utils/actionability/` | 193 | 動作前就緒閘門(可見 + 穩定 + 啟用 + 未被遮擋) | | `utils/ensure_state/` | 74 | 冪等地把控制項/設定帶到期望狀態 | -| `utils/field_entry/` | 76 | 清空再輸入的欄位填寫慣用法(Playwright `fill`) | +| `utils/field_entry/` | 79 | 清空再輸入的欄位填寫慣用法(Playwright `fill`) | | `utils/gamepad/` | 333 | 虛擬遊戲手把後端(Windows ViGEmBus 驅動) | | `utils/humanize/` | 191 | 擬人輸入:貝茲曲線滑鼠路徑 + 抖動打字節奏 | -| `utils/ime_state/` | 146 | 讀取即時 IME 組字/轉換狀態,確保 CJK 輸入安全 | +| `utils/ime_state/` | 149 | 讀取即時 IME 組字/轉換狀態,確保 CJK 輸入安全 | | `utils/key_hold/` | 109 | 按住按鍵一段時間,或以固定頻率自動重複 | | `utils/modifier_state/` | 81 | 跨一組動作按住修飾鍵,並保證安全釋放 | -| `utils/mouse_path/` | 106 | 多路徑點滑鼠手勢(沿折線移動或拖曳) | +| `utils/mouse_path/` | 115 | 多路徑點滑鼠手勢(沿折線移動或拖曳) | | `utils/mouse_relative/` | 59 | 相對位移滑鼠移動 | -| `utils/postcondition/` | 146 | 宣告式的動作預期結果規格,對照畫面驗證 | +| `utils/postcondition/` | 157 | 宣告式的動作預期結果規格,對照畫面驗證 | | `utils/step_repair/` | 136 | 失敗/無效動作的修復策略(自我修正迴圈) | -| `utils/table_grid_fill/` | 163 | 以 OCR 文字填滿格線表格,取得可定址的表格 | +| `utils/table_grid_fill/` | 187 | 以 OCR 文字填滿格線表格,取得可定址的表格 | | `utils/input_reach/` | 111 | 送出去的輸入到不到得了:桌面鎖定查詢(免費)+ 實際送一個 F13 確認沒有被過濾(有副作用,只給診斷用) | | `utils/keyboard_layout/` | 152 | 向系統問「這個鍵盤配置下每個鍵印出什麼字」(`ToUnicodeEx`),問不到退回 US 對照表 | | `utils/text_unicode/` | 151 | 輸入任意 Unicode(emoji/CJK/重音字):優先送字元按鍵事件,不支援時退回剪貼簿貼上 | @@ -371,43 +371,43 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 ### 5.4.5 影像辨識與畫面分析 -> 37 個套件、約 5,782 行。 +> 37 個套件、約 6,064 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | -| `utils/annotate/` | 121 | 截圖標註:畫框、highlight、箭頭、標籤 | +| `utils/annotate/` | 122 | 截圖標註:畫框、highlight、箭頭、標籤 | | `utils/barcode/` | 59 | 一維條碼(EAN/UPC)解碼,解碼器可注入 | -| `utils/color_match/` | 127 | 在 HSV 通道上做顏色感知的樣板比對 | -| `utils/color_region/` | 96 | 以顏色定位畫面區域(遮罩 + 連通元件) | +| `utils/color_match/` | 151 | 在 HSV 通道上做顏色感知的樣板比對 | +| `utils/color_region/` | 107 | 以顏色定位畫面區域(遮罩 + 連通元件) | | `utils/color_stats/` | 103 | 區域顏色統計:平均色與主色 | | `utils/coordinate_space/` | 93 | 模型網格座標與實體像素之間的座標空間對映 | -| `utils/cv2_utils/` | 798 | OpenCV 基礎層:擷取後端選擇(`screen_grabber`,Pillow/mss 或平台後端)、截圖、樣板比對(走 `grab_logical`,涵蓋所有螢幕)、螢幕錄影、影片錄製(兩者都經 `frame_clock` 依 fps 配速)、連通元件、影像堆疊的取用口(`optional`,Windows arm64 沒有 wheel 時語意報錯)、非 ASCII 路徑也讀寫得到的影像檔存取(`image_file`) | -| `utils/edge_lines/` | 122 | 以 Hough 轉換偵測線條/格線/分隔線 | +| `utils/cv2_utils/` | 847 | OpenCV 基礎層:擷取後端選擇(`screen_grabber`,Pillow/mss 或平台後端)、截圖、`[left, top, right, bottom]` 區域擷取(`region_capture`,Windows 經 `grab_logical` 涵蓋所有螢幕)、樣板比對(走 `grab_logical`,涵蓋所有螢幕)、螢幕錄影、影片錄製(兩者都經 `frame_clock` 依 fps 配速)、連通元件、影像堆疊的取用口(`optional`,Windows arm64 沒有 wheel 時語意報錯)、非 ASCII 路徑也讀寫得到的影像檔存取(`image_file`) | +| `utils/edge_lines/` | 131 | 以 Hough 轉換偵測線條/格線/分隔線 | | `utils/edge_match/` | 115 | 邊緣形狀(Chamfer/距離轉換)樣板比對 | | `utils/feature_match/` | 143 | ORB 特徵比對:在旋轉/縮放/主題變更下定位樣板 | | `utils/hsv_segment/` | 104 | HSV 色彩空間分割(抗光照的顏色遮罩 + blob 框) | -| `utils/icon_classify/` | 132 | 從像素形狀判斷一個框是哪一類元件 | +| `utils/icon_classify/` | 151 | 從像素形狀判斷一個框是哪一類元件 | | `utils/image_dedup/` | 100 | 感知雜湊影像去重(Pillow aHash/dHash) | -| `utils/image_quality/` | 77 | 在 OCR/比對前評分影像品質(銳利度/對比/亮度) | +| `utils/image_quality/` | 82 | 在 OCR/比對前評分影像品質(銳利度/對比/亮度) | | `utils/img_histogram/` | 112 | 顏色直方圖指紋與變化偵測(抗光照) | -| `utils/marks_layout/` | 149 | Set-of-Marks 標籤的不重疊排版與可讀配色 | +| `utils/marks_layout/` | 175 | Set-of-Marks 標籤的不重疊排版與可讀配色 | | `utils/match_autothresh/` | 114 | Otsu 自動門檻,免去手動調 `min_score` | | `utils/match_ensemble/` | 67 | 多樣板共識比對(多張參考圖投票到同一位置) | | `utils/match_stability/` | 70 | 比對前的靜止閘門與跨影格的比對持續性 | -| `utils/match_trust/` | 144 | 樣板比對可信度評分(次峰比 + peak-to-sidelobe) | +| `utils/match_trust/` | 154 | 樣板比對可信度評分(次峰比 + peak-to-sidelobe) | | `utils/monitor_layout/` | 320 | 多螢幕/虛擬桌面幾何(在哪個螢幕、位置、重映射)+ `logical_frame` 以滑鼠座標空間擷取畫面 | -| `utils/motion_regions/` | 73 | 兩影格間的局部變化/活動偵測(absdiff) | -| `utils/perceptual_diff/` | 196 | 感知式(YIQ)影像差異,抑制反鋸齒邊緣誤報 | -| `utils/preprocess/` | 219 | OCR/比對前的影像前處理(灰階、二值化、去傾斜…) | +| `utils/motion_regions/` | 78 | 兩影格間的局部變化/活動偵測(absdiff) | +| `utils/perceptual_diff/` | 202 | 感知式(YIQ)影像差異,抑制反鋸齒邊緣誤報 | +| `utils/preprocess/` | 276 | OCR/比對前的影像前處理(灰階、二值化、去傾斜…) | | `utils/qr/` | 59 | 從影像或螢幕區域解碼 QR code(OpenCV) | -| `utils/rotated_match/` | 166 | 容忍旋轉與縮放的樣板比對(尺度空間 × 角度掃描) | +| `utils/rotated_match/` | 201 | 容忍旋轉與縮放的樣板比對(尺度空間 × 角度掃描) | | `utils/saliency/` | 114 | 頻譜殘差視覺顯著性:顯著圖與排序後的顯著區域 | -| `utils/scale_detect/` | 84 | 偵測樣板實際渲染的顯示縮放/視覺 DPI | +| `utils/scale_detect/` | 105 | 偵測樣板實際渲染的顯示縮放/視覺 DPI | | `utils/screen_grid/` | 146 | 供 VLM 接地用的粗粒度標號網格(點 ↔ 格對映) | -| `utils/set_of_marks/` | 154 | Set-of-Marks 疊圖:為畫面元素編號供 VLM 指認 | +| `utils/set_of_marks/` | 153 | Set-of-Marks 疊圖:為畫面元素編號供 VLM 指認 | | `utils/shape_locator/` | 108 | 以邊緣/輪廓偵測定位元件(矩形/形狀,免樣板) | -| `utils/ssim/` | 163 | 結構相似度比較:感知分數 + 變化區域 | -| `utils/subpixel_match/` | 103 | 以二次曲面擬合做次像素級比對精修 | +| `utils/ssim/` | 162 | 結構相似度比較:感知分數 + 變化區域 | +| `utils/subpixel_match/` | 109 | 以二次曲面擬合做次像素級比對精修 | | `utils/theme_normalize/` | 94 | 主題無關的影像正規化,讓亮色樣板能配對深色模式 | | `utils/video_report/` | 171 | 影片步驟疊圖報告:把截圖加字幕串成操作導覽影片 | | `utils/visual_match/` | 515 | 會回傳信心值的樣板比對(分數、多尺度、find-all + NMS);擷取走 `grab_logical`,命中座標已加回虛擬桌面原點,單色樣板直接拒收 | @@ -415,47 +415,47 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 ### 5.4.6 OCR 與文字理解 -> 19 個套件、約 3,469 行。 +> 19 個套件、約 3,576 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | | `utils/bidi_check/` | 138 | 雙向文字 QA(bidi 控制碼、巢狀平衡、Trojan-source 掃描) | -| `utils/column_layout/` | 153 | 從垂直空白推斷欄位,處理無框線表格 | +| `utils/column_layout/` | 167 | 從垂直空白推斷欄位,處理無框線表格 | | `utils/confusables/` | 146 | 易混淆/同形字偵測(Unicode 欺騙骨架) | | `utils/form_fields/` | 134 | 多方向關聯表單標籤與值,並讀取核取方塊狀態 | | `utils/fuzzy/` | 111 | 模糊字串比對與去重(預設 difflib,有 rapidfuzz 則優先) | -| `utils/grid_locator/` | 71 | 以 (row, column) 從邊界框定址表格/網格儲存格 | +| `utils/grid_locator/` | 100 | 以 (row, column) 從邊界框定址表格/網格儲存格 | | `utils/guardrail/` | 117 | 針對畫面/OCR 文字的啟發式 prompt-injection 防護 | | `utils/heading_segment/` | 71 | 判定 OCR 行是標題或內文,建出文件大綱 | | `utils/near_dup/` | 108 | 近似重複文字偵測(SimHash/MinHash) | | `utils/ocr/` | 1,140 | OCR 引擎門面 + 三個後端(Tesseract/EasyOCR/PaddleOCR)、版面結構化與跨詞比對(`text_span`) | | `utils/pii_text/` | 141 | 自由文字中的 PII 偵測與遮蔽(email/電話/SSN/卡號/IP/IBAN) | | `utils/readability/` | 140 | 可讀性評分(Flesch、Flesch-Kincaid、Gunning Fog、SMOG、ARI) | -| `utils/reading_flow/` | 145 | 以遞迴 XY-cut 推導欄位感知的閱讀順序 | -| `utils/search_index/` | 145 | 記憶體內 BM25/TF-IDF 全文檢索 | -| `utils/text_blocks/` | 88 | 把 OCR 行組成段落與項目符號/編號清單 | +| `utils/reading_flow/` | 165 | 以遞迴 XY-cut 推導欄位感知的閱讀順序 | +| `utils/search_index/` | 183 | 記憶體內 BM25/TF-IDF 全文檢索 | +| `utils/text_blocks/` | 89 | 把 OCR 行組成段落與項目符號/編號清單 | | `utils/text_diff/` | 202 | unified diff 產生、套用與三方合併 | | `utils/text_normalize/` | 84 | Unicode 正規化與 slug 產生 | -| `utils/text_regions/` | 163 | 免模型的畫面文字區域偵測(MSER):區域與行 | +| `utils/text_regions/` | 168 | 免模型的畫面文字區域偵測(MSER):區域與行 | | `utils/text_similarity/` | 172 | 字串距離度量(文字比對用) | ### 5.4.7 無障礙樹與原生控制項 -> 16 個套件、約 4,619 行。 +> 16 個套件、約 4,678 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | | `utils/a11y_audit/` | 362 | 以無障礙樹 + OCR 進行無障礙與 i18n 稽核 | -| `utils/accessibility/` | 3,117 | 跨平台無障礙樹定位與錄製;Windows UIA/macOS AX/null 三後端。支援限定視窗(換搜尋起點,不是過濾)、逐節點可中斷走訪、`IUIAutomation2` 連線逾時、名稱子字串比對與排序、`control_get_state` 一次讀完值/勾選/選取/數值(密碼欄位不回內容) | -| `utils/ax_events/` | 29 | 反應式 UIA 事件等待(focus-changed) | +| `utils/accessibility/` | 3,143 | 跨平台無障礙樹定位與錄製;Windows UIA/macOS AX/null 三後端。支援限定視窗(換搜尋起點,不是過濾)、逐節點可中斷走訪、`IUIAutomation2` 連線逾時、名稱子字串比對與排序、`control_get_state` 一次讀完值/勾選/選取/數值(密碼欄位不回內容) | +| `utils/ax_events/` | 35 | 反應式 UIA 事件等待(focus-changed) | | `utils/ax_props/` | 44 | 讀取豐富 UIA 屬性(enabled/offscreen/help/status/快捷鍵) | | `utils/ax_text/` | 102 | 透過 UIA TextPattern 取得原生文字(讀取/尋找/選取/屬性) | | `utils/ax_tree_walk/` | 119 | 可讀、可定址的無障礙樹後處理(角色名 + 節點路徑) | | `utils/contrast_map/` | 130 | 取樣實際顏色以評定畫面文字的可讀性(WCAG) | | `utils/control_patterns/` | 88 | 延伸 UIA 控制項模式動作(Expand/Select/Range/Scroll) | | `utils/cvd_simulate/` | 140 | 模擬色覺缺陷並標示在該狀況下會撞色的顏色 | -| `utils/element_repository/` | 113 | 原生 UI 元素的具名定位器倉庫(object repository) | -| `utils/focus_order/` | 95 | 鍵盤焦點順序:預期 Tab 序列、WCAG 稽核與設定焦點 | +| `utils/element_repository/` | 118 | 原生 UI 元素的具名定位器倉庫(object repository) | +| `utils/focus_order/` | 117 | 鍵盤焦點順序:預期 Tab 序列、WCAG 稽核與設定焦點 | | `utils/legacy_accessible/` | 45 | MSAA 橋接,處理 UIA 無法建模的舊控制項 | | `utils/selection_view/` | 57 | 容器選取狀態與檢視切換(Selection/MultipleView 模式) | | `utils/table_pattern/` | 65 | 原生表格的表頭與儲存格定址(UIA TablePattern/GridItem) | @@ -464,37 +464,37 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 ### 5.4.8 元素定位、自我修復與智慧等待 -> 23 個套件、約 4,316 行。 +> 23 個套件、約 4,426 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | | `utils/ab_locator/` | 386 | A/B 定位器框架:同時競速 N 種策略並記錄各自勝率 | | `utils/adaptive_timeout/` | 92 | 由觀測到的步驟耗時推導等待逾時,而非硬猜 | | `utils/anchor_locator/` | 476 | 錨點定位器:以空間關係組合 影像/OCR/VLM/a11y 四種來源 | -| `utils/app_idle/` | 109 | 等應用程式不再忙碌,再驅動下一步 | +| `utils/app_idle/` | 112 | 等應用程式不再忙碌,再驅動下一步 | | `utils/change_localize/` | 83 | 把畫面變化歸因到實際改變的元素框 | -| `utils/critic_features/` | 85 | 每步的 critic 特徵集合與規則式步驟評分 | +| `utils/critic_features/` | 88 | 每步的 critic 特徵集合與規則式步驟評分 | | `utils/element_diff/` | 94 | 跨影格的幾何感知元素比對(穩定 ID、移動追蹤) | -| `utils/element_parse/` | 106 | 融合並排序畫面元素框(IoU、合併、多來源融合、閱讀順序) | +| `utils/element_parse/` | 127 | 融合並排序畫面元素框(IoU、合併、多來源融合、閱讀順序) | | `utils/element_proposal/` | 92 | 免樣板、免模型地從原始像素提出乾淨元素清單 | | `utils/element_scoring/` | 105 | 加權候選評分(角色 + 名稱相似度 + 鄰近度 + 啟用狀態) | | `utils/expect_poll/` | 149 | 反覆取值直到符合條件(Playwright `expect.poll` 風格) | -| `utils/grounding_consensus/` | 153 | 對同一目標的多個接地提案做自我一致性投票 | +| `utils/grounding_consensus/` | 173 | 對同一目標的多個接地提案做自我一致性投票 | | `utils/heal_analytics/` | 77 | 自癒事件記錄的分析(治癒率、脆弱定位器) | | `utils/locator_chain/` | 112 | 可組合/可過濾的候選定位器(chained-locator 慣用法) | -| `utils/locator_repair/` | 117 | 自癒回寫:把修正後的定位器持久化 | -| `utils/observation/` | 92 | 供 VLM/agent 接地用的 token 預算內、帶索引的 a11y 文字觀察 | +| `utils/locator_repair/` | 147 | 自癒回寫:把修正後的定位器持久化 | +| `utils/observation/` | 89 | 供 VLM/agent 接地用的 token 預算內、帶索引的 a11y 文字觀察 | | `utils/observation_delta/` | 122 | token 預算內的觀察差異:兩個 UI 影格之間變了什麼 | | `utils/screen_state/` | 191 | 語義畫面狀態:快照/差異與結構化畫面描述 | | `utils/scroll_find/` | 103 | 捲動直到目標影像/文字可見 | | `utils/self_healing/` | 359 | 自癒定位器:先影像樣板、失敗改用 VLM,並留稽核記錄 | -| `utils/semantic_recording/` | 460 | 為錄製內容加上語義錨點,支援換機重播與自癒重播 | +| `utils/semantic_recording/` | 498 | 為錄製內容加上語義錨點,支援換機重播與自癒重播 | | `utils/settle_detector/` | 79 | 以純函式介面判定 UI 是否已靜止 | -| `utils/smart_waits/` | 674 | 智慧等待:以影格差異取代 `time.sleep` | +| `utils/smart_waits/` | 672 | 智慧等待:以影格差異取代 `time.sleep` | ### 5.4.9 AI / Agent / LLM -> 13 個套件、約 23,127 行。 +> 13 個套件、約 23,194 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | @@ -507,43 +507,43 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 | `utils/cua_action/` | 204 | 標準化 computer-use 動作結構(Anthropic/OpenAI → `AC_*`) | | `utils/llm/` | 365 | 自然語言 → action list 規劃器 + Anthropic/null 後端 | | `utils/mcp_registry/` | 97 | MCP registry `server.json` 資訊清單產生(可被發現) | -| `utils/mcp_server/` | 18,798 | **無頭 MCP 伺服器**(16K LOC,預設註冊 678 個工具=659 個 `ac_*` + 19 個別名):stdio + HTTP 傳輸、工具工廠與處理器、資源、prompt、稽核、限流、外掛熱重載 | +| `utils/mcp_server/` | 18,846 | **無頭 MCP 伺服器**(16K LOC,預設註冊 678 個工具=659 個 `ac_*` + 19 個別名):stdio + HTTP 傳輸、工具工廠與處理器、資源、prompt、稽核、限流、外掛熱重載 | | `utils/tool_use_schema/` | 195 | 把 `AC_*` 指令匯出成 Claude/OpenAI 的 tool-use schema | -| `utils/trajectory_eval/` | 113 | agent 軌跡評估:依評分規準為一次執行打分 | +| `utils/trajectory_eval/` | 132 | agent 軌跡評估:依評分規準為一次執行打分 | | `utils/vision/` | 538 | VLM 元素定位器(依描述找元素)+ Anthropic/OpenAI/null 後端 | ### 5.4.10 遠端桌面與 USB -> 6 個套件、約 19,332 行。 +> 6 個套件、約 19,487 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | | `utils/admin/` | 418 | 多主機管理主控台:平行輪詢 N 個 AutoControl REST 端點 | | `utils/config_sync/` | 332 | 透過訊令伺服器做跨機器設定同步 | -| `utils/device_matrix/` | 138 | 行動裝置矩陣:同一 action list 於多台裝置平行執行 | -| `utils/remote_desktop/` | 12,912 | **遠端桌面子系統**(56 檔/11.7K LOC):TCP/WebSocket/WebRTC 三條傳輸路徑、主機與檢視端、訊令伺服器、TURN/中繼、多檢視者、錄影、信任清單、TOTP、稽核鏈 | -| `utils/usb/` | 4,524 | 跨平台 USB 列舉/熱插拔/裝置直通(WinUSB、IOKit、libusb 後端 + ACL + WebRTC DataChannel 通道) | +| `utils/device_matrix/` | 143 | 行動裝置矩陣:同一 action list 於多台裝置平行執行 | +| `utils/remote_desktop/` | 13,014 | **遠端桌面子系統**(56 檔/11.7K LOC):TCP/WebSocket/WebRTC 三條傳輸路徑、主機與檢視端、訊令伺服器、TURN/中繼、多檢視者、錄影、信任清單、TOTP、稽核鏈 | +| `utils/usb/` | 4,572 | 跨平台 USB 列舉/熱插拔/裝置直通(WinUSB、IOKit、libusb 後端 + ACL + WebRTC DataChannel 通道) | | `utils/usbip/` | 1,008 | USB/IP 線路協定主機端(協定封包、TCP 伺服器、libusb URB 後端) | ### 5.4.11 伺服器、網路協定與外部整合 -> 24 個套件、約 6,721 行。 +> 24 個套件、約 6,996 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | | `utils/acme_v2/` | 617 | 完整 ACME v2 用戶端(RFC 8555),不依賴 certbot | | `utils/chatops/` | 667 | Chat-ops bot:接收 Slack/Discord/webhook 的 slash 指令並路由到動作 | -| `utils/cookie_jar/` | 122 | RFC 6265 cookie jar | +| `utils/cookie_jar/` | 191 | RFC 6265 cookie jar | | `utils/email_send/` | 118 | SMTP 寄信(email 觸發器的發送端搭檔) | | `utils/events/` | 106 | 對外 CloudEvents 發送(執行生命週期事件) | | `utils/http_cassette/` | 200 | 錄製/重播 HTTP 互動,做離線決定性 API 測試 | | `utils/http_client/` | 245 | 零依賴 HTTP(S) 用戶端,供 action 步驟呼叫 API | -| `utils/http_conditional/` | 115 | 條件式 HTTP 請求與快取驗證器 | +| `utils/http_conditional/` | 148 | 條件式 HTTP 請求與快取驗證器 | | `utils/http_content/` | 158 | HTTP 內容協商與回應解壓縮 | -| `utils/http_problem/` | 118 | RFC 9457 problem+json 解析 | -| `utils/jwt/` | 240 | JWT(HMAC 家族)編碼、解碼與 claim 驗證 | -| `utils/link_header/` | 150 | RFC 8288 Link header 解析與分頁 | -| `utils/multipart/` | 181 | multipart/form-data 建構與解析 | +| `utils/http_problem/` | 122 | RFC 9457 problem+json 解析 | +| `utils/jwt/` | 270 | JWT(HMAC 家族)編碼、解碼與 claim 驗證 | +| `utils/link_header/` | 176 | RFC 8288 Link header 解析與分頁 | +| `utils/multipart/` | 224 | multipart/form-data 建構與解析 | | `utils/notify/` | 106 | 跨平台桌面通知 | | `utils/notify_channels/` | 105 | 對外聊天/webhook 通知(Slack/Discord/Teams/raw) | | `utils/otp/` | 37 | TOTP 一次性密碼產生(自動化 2FA 登入) | @@ -551,75 +551,75 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 | `utils/pytest_plugin/` | 380 | pytest 外掛 + BDD step library(`pytest11` entry point) | | `utils/rest_api/` | 1,881 | 純標準庫 REST 前端:路由、Bearer 驗證、限流、Prometheus 指標、OpenAPI 3.1 產生 | | `utils/socket_server/` | 160 | 執行 action JSON 的執行緒式 TCP 指令伺服器(預設綁 127.0.0.1) | -| `utils/sse_client/` | 128 | Server-Sent Events 用戶端解析 | -| `utils/tls_acme/` | 455 | TLS 自動化:HTTP-01 挑戰伺服器、金鑰/CSR、自動續期 | -| `utils/url_canon/` | 156 | RFC 3986 URL 正規化與查詢字串工具 | +| `utils/sse_client/` | 142 | Server-Sent Events 用戶端解析 | +| `utils/tls_acme/` | 473 | TLS 自動化:HTTP-01 挑戰伺服器、金鑰/CSR、自動續期 | +| `utils/url_canon/` | 194 | RFC 3986 URL 正規化與查詢字串工具 | | `utils/webrunner_bridge/` | 169 | 把 action JSON 橋接到 WebRunner(`je_web_runner`) | ### 5.4.12 報表、可觀測性與測試治理 -> 34 個套件、約 7,506 行。 +> 34 個套件、約 7,674 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | | `utils/anomaly/` | 114 | 單一序列異常偵測 | -| `utils/approval/` | 118 | Approval testing:以核可基準線驗證產出物 | +| `utils/approval/` | 131 | Approval testing:以核可基準線驗證產出物 | | `utils/assertion/` | 887 | 斷言 DSL:畫面狀態驗證 + 組合子 | -| `utils/baggage/` | 120 | W3C Baggage 傳遞 | +| `utils/baggage/` | 123 | W3C Baggage 傳遞 | | `utils/canonical_log/` | 96 | canonical log line 與結構化 JSON 日誌 | | `utils/ci_annotations/` | 65 | 由執行結果輸出 CI 工作流程註記(GitHub Actions) | -| `utils/compliance/` | 153 | 合規:把治理證據對應到 SOC2/ISO 27001 控制項 | +| `utils/compliance/` | 166 | 合規:把治理證據對應到 SOC2/ISO 27001 控制項 | | `utils/failure_hooks/` | 415 | 失敗 → 工單自動化:開 Jira/Linear/GitHub issue | | `utils/failure_signature/` | 76 | 把錯誤訊息正規化成穩定的 SHA-256 失敗簽章並分群 | -| `utils/flake_cluster/` | 103 | 以共同失敗 Jaccard 相似度為易碎測試分群 | -| `utils/flakiness/` | 150 | 以執行歷史分析不穩定測試 | -| `utils/generate_report/` | 293 | HTML/JSON/XML 三種報表產生器(Template Method) | -| `utils/media_assert/` | 242 | 媒體斷言:音訊活動與影片動態檢查 | +| `utils/flake_cluster/` | 112 | 以共同失敗 Jaccard 相似度為易碎測試分群 | +| `utils/flakiness/` | 151 | 以執行歷史分析不穩定測試 | +| `utils/generate_report/` | 294 | HTML/JSON/XML 三種報表產生器(Template Method) | +| `utils/media_assert/` | 273 | 媒體斷言:音訊活動與影片動態檢查 | | `utils/observability/` | 710 | Prometheus 格式指標 + OpenTelemetry 相容 trace + `/metrics` 匯出伺服器 | -| `utils/otlp_export/` | 109 | OTLP/JSON span 匯出 | +| `utils/otlp_export/` | 114 | OTLP/JSON span 匯出 | | `utils/percentiles/` | 119 | 可合併的串流延遲摘要與精確百分位數 | | `utils/process_doc/` | 108 | 由錄製的 action list 產生逐步 SOP 文件 | -| `utils/process_mining/` | 123 | 流程探勘:從動作日誌挖掘可自動化的候選 | +| `utils/process_mining/` | 131 | 流程探勘:從動作日誌挖掘可自動化的候選 | | `utils/profiler/` | 451 | 逐動作效能剖析器 + 資源剖析器 | -| `utils/quarantine/` | 200 | 易碎測試隔離區,讓套件執行器跳過已知不穩定案例 | -| `utils/run_diff/` | 123 | 兩次執行軌跡的差異(LCS 對齊:新增/移除/狀態翻轉/退化) | -| `utils/run_history/` | 439 | 執行歷史儲存與產出物管理 | +| `utils/quarantine/` | 204 | 易碎測試隔離區,讓套件執行器跳過已知不穩定案例 | +| `utils/run_diff/` | 143 | 兩次執行軌跡的差異(LCS 對齊:新增/移除/狀態翻轉/退化) | +| `utils/run_history/` | 449 | 執行歷史儲存與產出物管理 | | `utils/sarif/` | 187 | 以 SARIF 2.1.0 匯出發現項,供 GitHub/Azure code scanning | | `utils/slo/` | 115 | SLO 評估:SLI、錯誤預算與多視窗燃燒率告警 | | `utils/smoothing/` | 67 | 數列移動平均平滑 | | `utils/soft_assert/` | 79 | 軟斷言:累積檢查並在區塊結束時一次拋出 | | `utils/stats/` | 236 | 描述統計與 A/B 顯著性檢定(純標準庫) | -| `utils/step_timeline/` | 81 | 每次執行的步驟瀑布圖與瓶頸(關鍵路徑)步驟排名 | -| `utils/test_select/` | 129 | 以執行歷史做風險導向的測試選取 | -| `utils/test_shard/` | 105 | 以耗時為權重的套件切分與分片結果合併 | +| `utils/step_timeline/` | 87 | 每次執行的步驟瀑布圖與瓶頸(關鍵路徑)步驟排名 | +| `utils/test_select/` | 131 | 以執行歷史做風險導向的測試選取 | +| `utils/test_shard/` | 106 | 以耗時為權重的套件切分與分片結果合併 | | `utils/test_suite/` | 547 | QA 套件編排:把扁平 action list 評分為測試案例 + CI 報表 | | `utils/time_travel/` | 388 | 錄製 session 的時光回溯除錯(控制器 + 播放器) | | `utils/timeseries/` | 175 | 時間序列轉換(rate/降採樣/重採樣) | -| `utils/trace_context/` | 183 | W3C Trace Context 傳遞 | +| `utils/trace_context/` | 224 | W3C Trace Context 傳遞 | ### 5.4.13 資料來源、結構驗證與 i18n -> 24 個套件、約 4,662 行。 +> 24 個套件、約 5,196 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | -| `utils/checksum/` | 138 | 檢查碼演算法:Luhn、Verhoeff、Damm、ISO 7064 MOD 97-10 | -| `utils/config_schema/` | 130 | 型別化設定結構驗證 | -| `utils/data_drift/` | 128 | 分布漂移偵測 | -| `utils/data_profile/` | 129 | 資料剖析與結構推斷 | -| `utils/data_quality/` | 218 | 資料品質:列結構驗證、欄位擷取、遮蔽 | +| `utils/checksum/` | 160 | 檢查碼演算法:Luhn、Verhoeff、Damm、ISO 7064 MOD 97-10 | +| `utils/config_schema/` | 144 | 型別化設定結構驗證 | +| `utils/data_drift/` | 143 | 分布漂移偵測 | +| `utils/data_profile/` | 169 | 資料剖析與結構推斷 | +| `utils/data_quality/` | 232 | 資料品質:列結構驗證、欄位擷取、遮蔽 | | `utils/data_source/` | 235 | 資料驅動執行:從 CSV/JSON/SQLite/Excel 載入資料列 | -| `utils/dataset_diff/` | 89 | 表格資料列差異比對(CDC 風格) | -| `utils/gettext_catalog/` | 362 | GNU gettext 目錄 I/O(解析 .po、編譯/讀取 .mo、訊息查詢) | -| `utils/i18n_test/` | 231 | 國際化/在地化測試輔助 | -| `utils/json_contract/` | 145 | JSON 契約/快照比對:`match_json`、`diff_json`、`snapshot_json` | +| `utils/dataset_diff/` | 116 | 表格資料列差異比對(CDC 風格) | +| `utils/gettext_catalog/` | 371 | GNU gettext 目錄 I/O(解析 .po、編譯/讀取 .mo、訊息查詢) | +| `utils/i18n_test/` | 228 | 國際化/在地化測試輔助 | +| `utils/json_contract/` | 158 | JSON 契約/快照比對:`match_json`、`diff_json`、`snapshot_json` | | `utils/json_patch/` | 352 | JSON Pointer(6901)、JSON Patch(6902)與 Merge Patch(7386) | -| `utils/json_schema/` | 426 | JSON Schema(Draft 2020-12 子集)驗證 | -| `utils/jsonpath/` | 322 | 精簡 JSONPath 查詢 | +| `utils/json_schema/` | 666 | JSON Schema(Draft 2020-12 子集)驗證 | +| `utils/jsonpath/` | 360 | 精簡 JSONPath 查詢 | | `utils/list_format/` | 82 | 地區感知清單格式化(CLDR 風格的「A、B 和 C」) | -| `utils/locale_collation/` | 135 | 地區感知字串排序(決定性多層排序鍵) | +| `utils/locale_collation/` | 139 | 地區感知字串排序(決定性多層排序鍵) | | `utils/locale_parse/` | 80 | 地區感知數字/貨幣/日期解析與格式化(選用 babel) | -| `utils/message_format/` | 288 | ICU-lite MessageFormat(plural/select/selectordinal) | +| `utils/message_format/` | 389 | ICU-lite MessageFormat(plural/select/selectordinal) | | `utils/office/` | 198 | Office 文件無頭讀寫(Excel/Word/PowerPoint) | | `utils/pdf/` | 117 | PDF 讀取與斷言(選用 pypdf 後端) | | `utils/referential/` | 83 | 跨資料集的參照完整性檢查 | @@ -630,7 +630,7 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 ### 5.4.14 安全、機密與合規 -> 13 個套件、約 2,913 行。 +> 13 個套件、約 2,964 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | @@ -639,10 +639,10 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 | `utils/governance/` | 242 | 治理:maker-checker 核准閘門與即時憑證租約 | | `utils/license_policy/` | 240 | 以 SBOM 元件評估 SPDX 授權允許/拒絕政策 | | `utils/provenance/` | 126 | SLSA 建置來源證明(in-toto v1) | -| `utils/rbac/` | 299 | 角色型存取控制:使用者、角色與權杖驗證(尚未接到 REST/MCP) | +| `utils/rbac/` | 302 | 角色型存取控制:使用者、角色與權杖驗證(尚未接到 REST/MCP) | | `utils/redaction/` | 508 | 截圖遮蔽層:規則偵測 + 政策 + 協調器(上傳 VLM 前先遮) | -| `utils/sbom/` | 148 | SBOM(CycloneDX)產生 | -| `utils/secret_ref/` | 143 | URI scheme 形式的值參照解析 | +| `utils/sbom/` | 170 | SBOM(CycloneDX)產生 | +| `utils/secret_ref/` | 169 | URI scheme 形式的值參照解析 | | `utils/secrets/` | 360 | 加密機密儲存庫,供 `${secrets.NAME}` 解析 | | `utils/secrets_scan/` | 138 | 掃描 action JSON/資料中應入庫卻硬編碼的機密 | | `utils/vex/` | 178 | OpenVEX 陳述撰寫與漏洞分類處置 | @@ -650,28 +650,28 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 ### 5.4.15 韌性、流量控制與設定 -> 14 個套件、約 2,033 行。 +> 14 個套件、約 2,159 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | | `utils/artifact_store/` | 148 | S3 相容產出物儲存(報表/截圖/錄影) | -| `utils/assets/` | 178 | 環境範圍的型別化資產/設定儲存(UiPath Assets 風格) | +| `utils/assets/` | 203 | 環境範圍的型別化資產/設定儲存(UiPath Assets 風格) | | `utils/bulkhead/` | 141 | Bulkhead 併發隔離 + 伺服器限流標頭解析 | | `utils/chaos/` | 153 | 決定性混沌實驗(穩態假說 + 故障注入) | | `utils/dedup_window/` | 72 | 時間視窗內的訊息去重 | -| `utils/dotenv/` | 165 | `.env` 檔解析與序列化 | +| `utils/dotenv/` | 222 | `.env` 檔解析與序列化 | | `utils/feature_flags/` | 191 | 功能旗標評估,含目標規則與決定性灰度 | | `utils/idempotency/` | 142 | 冪等鍵儲存與已存回應重放 | -| `utils/layered_config/` | 110 | 分層設定解析 | +| `utils/layered_config/` | 114 | 分層設定解析 | | `utils/optimistic/` | 135 | 樂觀併發的版本化儲存 | -| `utils/rate_limit/` | 204 | 用戶端限流:token bucket、滑動視窗、throttle | +| `utils/rate_limit/` | 233 | 用戶端限流:token bucket、滑動視窗、throttle | | `utils/resilience/` | 146 | 韌性原語:退避重試與斷路器 | -| `utils/retry_budget/` | 158 | 重試預算:以牆鐘期限與 full jitter 約束重試 | +| `utils/retry_budget/` | 169 | 重試預算:以牆鐘期限與 full jitter 約束重試 | | `utils/sequence_gap/` | 90 | 逐串流的序號缺口偵測 | ### 5.4.16 系統、視窗與剪貼簿 -> 16 個套件、約 2,610 行。 +> 16 個套件、約 2,616 行。 | 模組 | 行數 | 職責 | | --- | ---: | --- | @@ -688,7 +688,7 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 | `utils/system_volume/` | 212 | 讀取與控制系統主音量與靜音狀態 | | `utils/trash/` | 93 | 把檔案移到系統資源回收筒(可復原刪除) | | `utils/window_capture/` | 304 | 逐視窗截圖、視窗版面儲存/還原、貼齊與排列 | -| `utils/window_geometry/` | 81 | 視窗客戶區幾何(外框內縮、client→screen 對映) | +| `utils/window_geometry/` | 87 | 視窗客戶區幾何(外框內縮、client→screen 對映) | | `utils/window_layout/` | 137 | 視窗拼貼/版面規劃器(左右半、四象限、網格、層疊) | | `utils/window_zorder/` | 76 | 視窗 z 序控制(最上層/移到最前/送到最後) | @@ -696,42 +696,43 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 上表以子套件為單位;以下把行數最大的幾個子系統展開到檔案層。 -#### `utils/executor/`(9,504 行)— 執行核心 +#### `utils/executor/`(9,539 行)— 執行核心 | 檔案 | 行數 | 職責 | | --- | ---: | --- | -| `action_executor.py` | 8,308 | `Executor` 類別與 `event_dict` 分派表(775 個指令),另含數百個把 utils 能力接成指令的 adapter 函式;全域單例 `executor` 與 `add_command_to_executor()` 擴充點。 | -| `flow_control.py` | 643 | 真正的流程控制:`AC_loop`/`AC_for_each`/`AC_while_*`/`AC_if_*`/`AC_try`/`AC_retry`/`AC_parallel`/`AC_define_macro`/`AC_call_macro`/變數指令(`AC_set_var`/`AC_get_var`/`AC_inc_var`)。`LoopBreak`/`LoopContinue` 以例外實作。34 個區塊指令的分派表 `BLOCK_COMMANDS` 也在這裡,含下一列匯入的資料來源指令。 | +| `action_executor.py` | 8,323 | `Executor` 類別與 `event_dict` 分派表(775 個指令),另含數百個把 utils 能力接成指令的 adapter 函式;全域單例 `executor` 與 `add_command_to_executor()` 擴充點。 | +| `flow_control.py` | 644 | 真正的流程控制:`AC_loop`/`AC_for_each`/`AC_while_*`/`AC_if_*`/`AC_try`/`AC_retry`/`AC_parallel`/`AC_define_macro`/`AC_call_macro`/變數指令(`AC_set_var`/`AC_get_var`/`AC_inc_var`)。`LoopBreak`/`LoopContinue` 以例外實作。34 個區塊指令的分派表 `BLOCK_COMMANDS` 也在這裡,含下一列匯入的資料來源指令。 | | `flow_data_commands.py` | 272 | `AC_*_to_var` 資料來源與轉換指令:shell、時鐘、亂數、PDF、TOTP、SQL、檔案、HTTP、OCR,加上 `AC_assert_var`/`AC_assert_db`/`AC_assert_duration`/`AC_transform_var`。都不執行巢狀 action list,所以沒有迴圈/分支語意。 | | `action_schema.py` | 159 | action list 的結構驗證:形狀、參數型別、未知指令拒絕。單一走訪同時支援兩種消費方式:`validate_actions()` 遇到第一個問題就拋、`unknown_command_names()` 收齊全部不認得的名字(REST `/execute` 用它回 400)。 | | `action_redaction.py` | 83 | 記錄與紀錄鍵用的遮蔽:`AC_secret_*` 的參數(金庫通行碼、機密值)在寫進 log、當成結果紀錄的鍵之前換成 `***`,巢狀在區塊指令裡的也一樣。 | | `mouse_aliases.py` | 39 | 單鍵點擊別名(`AC_click_left` 等),executor 與 callback executor 共用。 | +| `flags.py` | 19 | 旗標讀取 `as_bool`:依拼法(`"true"`/`"yes"`/`"on"`/`"1"`)而非 Python 真值判斷,所以 JSON、CLI、MCP 傳來的 `"false"` 是關;所有 adapter 與流程指令共用。 | -#### `utils/mcp_server/`(18,798 行,678 個工具)— 最大子系統 +#### `utils/mcp_server/`(18,846 行,678 個工具)— 最大子系統 | 檔案 | 行數 | 職責 | | --- | ---: | --- | -| `tools/_factories.py` | 9,023 | 工具工廠:每個函式回傳一個領域的 `MCPTool` 清單(把 `AC_*` 能力包成 MCP 工具)。 | +| `tools/_factories.py` | 9,029 | 工具工廠:每個函式回傳一個領域的 `MCPTool` 清單(把 `AC_*` 能力包成 MCP 工具)。 | | `tools/_handlers.py` | 545 | 把 MCP 工具呼叫橋接到 AutoControl 無頭 API 的 adapter;主題模組拆完之後這裡留的是資料/文字/HTTP 那一類與 WebRunner 橋接。 | | `tools/_handlers_qa.py` | 419 | 同一種 adapter,QA 主題:斷言 DSL、資料驅動、SQL/PDF/郵件/HTTP 步驟、codegen、視覺回歸、狀態機、flaky 偵測與隔離、suite runner、無障礙稽核、裝置矩陣、媒體斷言。從 `_handlers.py` 依主題拆出的第一塊(750 行上限);兩者互不引用。 | | `tools/_handlers_input.py` | 218 | 同一種 adapter,輸入主題:滑鼠、鍵盤、虛擬手把(ViGEm)。 | -| `tools/_handlers_screen.py` | 327 | 同一種 adapter,螢幕主題:擷取、像素、影像與文字搜尋、螢幕錄影。 | +| `tools/_handlers_screen.py` | 331 | 同一種 adapter,螢幕主題:擷取、像素、影像與文字搜尋、螢幕錄影。 | | `tools/_handlers_system.py` | 575 | 同一種 adapter,桌面工作階段:視窗、行程與 shell、開檔、閒置與睡眠、音量、鎖定、輸入法狀態、欄位驗證與重試、色彩對比、變更排序、元件分類、剪貼簿。 | | `tools/_handlers_runs.py` | 110 | 同一種 adapter,執行主題:executor、執行歷史、錄製、動作檔。 | | `tools/_handlers_scheduling.py` | 200 | 同一種 adapter,排程主題:排程器、觸發器、熱鍵常駐。 | | `tools/_handlers_remote.py` | 66 | 同一種 adapter,遠端桌面的 host 與 viewer。 | | `tools/_handlers_executor_bridge.py` | 1,429 | 252 個純委派(中位數 3 行,最長的 16 行全是參數簽章):每個都是 `from action_executor import _x` 再 `return _x(...)`,沒有分支邏輯。超過 750 行,理由記在 `Progress.md` 的豁免表(再切只能照 MCP 工廠領域分,會把同一種委派散進十幾個沒有語意邊界的檔)。 | | `tools/_handlers_locators.py` | 436 | 同一種 adapter,定位主題:無障礙樹、智慧等待、自我修復、螢幕觀察、座標空間、視覺與 OCR、影像去重、元件倉庫、A/B 定位。 | -| `tools/_handlers_operations.py` | 647 | 同一種 adapter,營運主題:agent 與其記憶/追蹤、治理與合規、成本與遙測、失敗掛鉤、看門狗、速率限制、檢查點、核可、產物與資產、測試選擇與分片、佇列與 saga。 | -| `server.py` | 700 | JSON-RPC 2.0 over stdio 的最小 MCP 伺服器:連線範圍狀態、行內/併發分派、工具與 resource/prompt 處理器;握手時代的方法表(`_run_method`),兩個協定時代的逐請求分派在 `_stateless.py`。 | -| `http_transport.py` | 715 | MCP 的 HTTP 傳輸;宣告 2026-07-28 的請求走 `_http_stateless.py` 的標頭規則,不發 session。 | +| `tools/_handlers_operations.py` | 629 | 同一種 adapter,營運主題:agent 與其記憶/追蹤、治理與合規、成本與遙測、失敗掛鉤、看門狗、速率限制、檢查點、核可、產物與資產、測試選擇與分片、佇列與 saga。 | +| `server.py` | 721 | JSON-RPC 2.0 over stdio 的最小 MCP 伺服器:連線範圍狀態、行內/併發分派、工具與 resource/prompt 處理器;握手時代的方法表(`_run_method`),兩個協定時代的逐請求分派在 `_stateless.py`。 | +| `http_transport.py` | 719 | MCP 的 HTTP 傳輸;宣告 2026-07-28 的請求走 `_http_stateless.py` 的標頭規則,不發 session。 | | `_http_stateless.py` | 185 | MCP 2026-07-28 在 Streamable HTTP 上的規則:`MCP-Protocol-Version`/`Mcp-Method`/`Mcp-Name` 必須與 body 相符(`=?base64?…?=` 先解碼),不符是 400+`HeaderMismatch`;版本與中繼資料錯誤 400、未知方法 404。純函式,由 `http_transport.py` 回覆。 | | `http_sessions.py` | 247 | MCP 的 HTTP 傳輸用的 session 身分:`Mcp-Session-Id` 註冊表,以及每個 session 那條常駐的 server→client SSE 串流。 | | `_client_requests.py` | 254 | 伺服器主動送出的請求:`roots/list`/`elicitation/create`/`sampling/createMessage`,對應表與回應路由,以及破壞性工具的確認交握。只屬於握手時代:無狀態請求裡送出會丟例外。 | | `_stateless.py` | 267 | MCP 2026-07-28 無狀態版本,與以 `initialize` 握手的版本並存:逐請求的 `_meta`(版本、client 能力、`logLevel`)、`server/discover`、結果的 `resultType`/`serverInfo`/快取提示、`-32020`~`-32022` 錯誤碼,以及兩個時代逐請求分派的 mixin。 | | `_input_required.py` | 160 | 多輪往返請求(MRTR):`input_required` 結果,與 HMAC 簽章、會過期、只兌換一次的 `requestState`;破壞性工具確認在無狀態請求裡的形式。 | | `_subscriptions.py` | 256 | 變更通知:握手時代的 `resources/subscribe`/`unsubscribe` 與未經訂閱的 `resources/updated`、`tools/list_changed`(不送給無狀態的對端);2026-07-28 的 `subscriptions/listen`:確認、以訂閱 id 標記的通知、取消與伺服器結束時的完成回覆。 | -| `_protocol.py` | 219 | JSON-RPC 線路格式:版本與識別常數、`_MCPError`、決定失敗工具行為的錯誤 tuple、envelope 產生器、工具回傳值轉 `content` 區塊。不碰伺服器狀態。 | +| `_protocol.py` | 243 | JSON-RPC 線路格式:版本與識別常數、`_MCPError`、決定失敗工具行為的錯誤 tuple、envelope 產生器、工具回傳值轉 `content` 區塊。不碰伺服器狀態。 | | `resources.py` | 307 | MCP resource 提供者。 | | `prompts.py` | 220 | MCP prompt 目錄。 | | `fake_backend.py` | 184 | CI/無頭測試用的記憶體內假後端。 | @@ -740,29 +741,29 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 | `tools/_validation.py` | 122 | MCP 工具用到的 JSON Schema 子集驗證器。 | | `tools/plugin_tools.py` | 89 | 把外掛載入的 `AC_*` callable 包成 `MCPTool`。 | | `log_bridge.py` | 118 | 把 Python logging 記錄橋接成 MCP `notifications/message`;2026-07-28 的請求只收到自己設了 `logLevel` 時產生的記錄。 | -| `audit.py` | 87 | MCP 工具呼叫稽核記錄。 | +| `audit.py` | 94 | MCP 工具呼叫稽核記錄。 | | `context.py` | 71 | 傳給 opt-in 工具處理器的每次呼叫上下文。 | | `rate_limit.py` | 48 | 工具呼叫的 token bucket 限流。 | | `__main__.py` | 92 | `je_auto_control_mcp` console script 進入點。 | -#### `utils/remote_desktop/`(12,912 行/56 檔) +#### `utils/remote_desktop/`(13,014 行/56 檔) 三條傳輸路徑並存:**TCP**(JPEG 影格)、**WebSocket**(同協定換傳輸)、**WebRTC**(aiortc 視訊 + DataChannel)。 | 檔案 | 行數 | 職責 | | --- | ---: | --- | -| `webrtc_host.py` | 716 | WebRTC 主機:串流螢幕視訊並接受檢視端輸入;session 生命週期、DataChannel 接線、檔案收發。 | +| `webrtc_host.py` | 722 | WebRTC 主機:串流螢幕視訊並接受檢視端輸入;session 生命週期、DataChannel 接線、檔案收發。 | | `webrtc_viewer.py` | 677 | WebRTC 檢視端:接收視訊並送出輸入。 | -| `host.py` | 669 | TCP 主機:接受迴圈、TLS 包裝、連線/認證握手、音訊與剪貼簿廣播、檔案推送、單次 token。 | +| `host.py` | 673 | TCP 主機:接受迴圈、TLS 包裝、連線/認證握手、音訊與剪貼簿廣播、檔案推送、單次 token。 | | `viewer.py` | 634 | TCP 檢視端。 | -| `host_service.py` | 558 | 無頭 WebRTC 主機執行器 + 多平台服務安裝器。 | +| `host_service.py` | 562 | 無頭 WebRTC 主機執行器 + 多平台服務安裝器。 | | `host_client.py` | 453 | TCP 主機的每連線處理器:一個檢視端一個實例,擁有它的認證交換、sender/audio/receiver 三條執行緒,以及入站訊息的路由表。 | | `registry.py` | 370 | `AC_remote_*` 指令使用的行程級單例。 | -| `webrtc_transport.py` | 411 | 共用 WebRTC 管線:asyncio 橋接執行緒、螢幕視訊軌、設定。 | -| `multi_viewer.py` | 339 | 每個連入檢視端各跑一個 `WebRTCDesktopHost` 的協調器。 | +| `webrtc_transport.py` | 421 | 共用 WebRTC 管線:asyncio 橋接執行緒、螢幕視訊軌、設定。 | +| `multi_viewer.py` | 357 | 每個連入檢視端各跑一個 `WebRTCDesktopHost` 的協調器。 | | `signaling_server.py` | 427 | 獨立的 WebRTC SDP 交換 rendezvous 服務。 | | `audit_log.py` | 355 | SQLite 雜湊鏈稽核記錄。 | -| `host_capture.py` | 297 | TCP 主機的影格與游標產生:螢幕列舉、監視器索引轉擷取區域、預設 JPEG/游標 provider,以及 `FrameProductionMixin`(游標輪詢、擷取迴圈、上線編碼)。 | +| `host_capture.py` | 323 | TCP 主機的影格與游標產生:螢幕列舉、監視器索引轉擷取區域、預設 JPEG/游標 provider,以及 `FrameProductionMixin`(游標輪詢、擷取迴圈、上線編碼)。 | | `ws_protocol.py` | 318 | 最小 RFC 6455 WebSocket 框架與握手。 | | `file_transfer.py` | 371 | 分塊檔案傳輸。 | | `relay.py` | 315 | NAT 穿透失敗時的 TCP 中繼。 | @@ -784,9 +785,9 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 | `signaling_client.py` | 164 | 純標準庫的訊令用戶端。 | | `trust_list.py` | 139 | 自動接受的檢視端信任清單。 | | `webrtc_inspector.py` | 138 | 行程級的 `StatsSnapshot` 滾動視窗。 | -| `input_dispatch.py` | 141 | 在主機端套用輸入訊息。 | +| `input_dispatch.py` | 161 | 在主機端套用輸入訊息。 | | `session_recorder.py` | 139 | 以 PyAV 把 WebRTC 影格錄成 mp4。 | -| `totp.py` | 146 | RFC 6238 TOTP(零外部相依)。 | +| `totp.py` | 160 | RFC 6238 TOTP(零外部相依)。 | | `file_sync.py` | 141 | 輪詢式資料夾鏡像。 | | `transport.py` | 126 | 可插拔的型別化訊息傳輸。 | | `host_access.py` | 112 | TCP 主機的檢視端核准與存取控制:`PendingViewer`、權限字串、分享碼的 TOTP 候選值、IP 白名單。`host` 與 `host_client` 共用,所以獨立成模組。 | @@ -796,7 +797,7 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 | `permissions.py` / `clipboard_sync.py` / `wake_on_lan.py` / `session_actions.py` / `auth.py` | 64 / 74 / 56 / 40 / 28 | 逐 session 權限、剪貼簿同步、WOL、SAS 注入與螢幕遮蔽、HMAC 挑戰回應。 | | `ws_host.py` / `ws_viewer.py` / `jpeg_recorder.py` | 40 / 29 / 146 | WebSocket 傳輸變體與 TCP 路徑錄影。 | -#### `utils/usb/`(4,524 行)與 `utils/usbip/`(1,008 行) +#### `utils/usb/`(4,572 行)與 `utils/usbip/`(1,008 行) | 檔案 | 行數 | 職責 | | --- | ---: | --- | @@ -813,7 +814,7 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 | `usb/passthrough/key_provider.py` | 125 | ACL 的可插拔 HMAC 金鑰來源。 | | `usb/passthrough/commands.py` | 150 | 無頭直通指令(單一真實來源)。 | | `usb/usb_devices.py` | 296 | 跨平台 USB 裝置列舉。 | -| `usb/usb_watcher.py` | 260 | 輪詢式 USB 熱插拔監看。 | +| `usb/usb_watcher.py` | 308 | 輪詢式 USB 熱插拔監看。 | | `usbip/protocol.py` | 342 | USB/IP 線路格式封裝/解析。 | | `usbip/server.py` | 295 | USB/IP 主機端 TCP 伺服器。 | | `usbip/libusb_backend.py` | 224 | 以 PyUSB/libusb 執行 URB 的正式後端。 | @@ -850,7 +851,7 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。 | `semantic_recording/` | `enrich.py`(加錨點)、`replay.py`(換機重播)、`self_healing.py`(自癒重播) | | `tls_acme/` | `challenge.py`、`keys.py`、`renewal.py` | | `pytest_plugin/` | `plugin.py`(pytest11 進入點)、`keywords.py`、`bdd_steps.py`(Gherkin) | -| `cv2_utils/` | `screen_grabber.py`、`screenshot.py`、`template_detection.py`、`screen_record.py`、`video_recording.py`、`frame_clock.py`、`blobs.py`、`optional.py`、`image_file.py` | +| `cv2_utils/` | `screen_grabber.py`、`screenshot.py`、`region_capture.py`、`template_detection.py`、`screen_record.py`、`video_recording.py`、`frame_clock.py`、`blobs.py`、`optional.py`、`image_file.py` | | `action_lint/` | `linter.py`、`schema.py`、`__main__.py`(CI 使用) | | `time_travel/` | `controller.py`、`player.py` | | `dag/` | `graph.py`、`runner.py` | @@ -880,20 +881,21 @@ GUI 是**選用 extra**(`pip install je_auto_control[gui]`,PySide6 + qt-mate | 模組 | 行數 | 職責 | | --- | ---: | --- | | `gui/__init__.py` | 23 | `start_autocontrol_gui()`:**唯一**會延遲匯入 PySide6 的地方,維持頂層套件 Qt-free。 | -| `main_window.py` | 293 | `QMainWindow`:選單列(File/Actions/View/…)、可關閉分頁、即時語言切換、字級預設、qt-material 主題。分頁分為 core/editing/detection/automation/system 五類。 | -| `main_widget.py` | 430 | 擁有 `QTabWidget`,註冊 48 個分頁,並暴露 show/hide/list API 給選單列。核心分頁在註冊時直接宣告 `(label_key, handler)` 動作對;分頁本體都在下列 mixin。 | -| `_auto_click_tab.py` | 286 | 自動點擊分頁的 mixin 建構器。 | +| `main_window.py` | 301 | `QMainWindow`:選單列(File/Actions/View/…)、可關閉分頁、即時語言切換、字級預設、qt-material 主題。分頁分為 core/editing/detection/automation/system 五類。 | +| `main_widget.py` | 437 | 擁有 `QTabWidget`,註冊 48 個分頁,並暴露 show/hide/list API 給選單列。核心分頁在註冊時直接宣告 `(label_key, handler)` 動作對;分頁本體都在下列 mixin。 | +| `_auto_click_tab.py` | 291 | 自動點擊分頁的 mixin 建構器。 | | `_screenshot_tab.py` | 137 | 截圖/取像素分頁 mixin。 | | `_image_detect_tab.py` | 115 | 影像偵測分頁 mixin。 | | `_script_tab.py` | 115 | 腳本執行分頁 mixin。 | -| `_record_tab.py` | 110 | 錄製/回放分頁 mixin。 | +| `_record_tab.py` | 114 | 錄製/回放分頁 mixin。 | | `_report_tab.py` | 88 | 報表分頁 mixin。 | | `_i18n_helpers.py` | 66 | 需要即時語言切換的分頁共用的翻譯註冊 mixin。 | | `_validators.py` | 29 | `int_validator()`/`double_validator()`:以 C locale 驗證的數字輸入框 validator,接受的正是 `int()`/`float()` 讀得懂的寫法(預設 locale 在法文、德文下只收小數逗號)。所有數字 `QLineEdit` 都用它。 | +| `_screen_geometry.py` | 52 | Qt 邏輯座標與截圖用的原生像素互轉:`native_region()`、`screen_at_native()`、`logical_point()`(每個螢幕的左上角在兩者相同,螢幕內依 device pixel ratio 縮放)。區域選取與主機端標註覆蓋層都用它。 | | `_daemon_thread.py` | 79 | `DaemonThread`:`QThread` 的替代品,保留遠端桌面 worker 用到的介面(`start`/`run`/`isRunning`/`wait`/`requestInterruption`/`started`/`finished`),但 `run()` 跑在 daemon `threading.Thread` 上,刪除物件或程式結束都不會銷毀執行中的執行緒。 | -| `_worker_thread.py` | 192 | `start_worker()`:在 daemon `threading.Thread` 上執行 `QObject` worker 的 `run()`(沒有 `QThread` 可被銷毀),並經由分頁擁有的中繼物件回報結果(回呼一律在 GUI 執行緒;worker 沒處理的例外也送到 `on_fail`);worker 留在模組登錄表直到 GUI 執行緒看到它結束,回傳 `WorkerHandle`(`isRunning()`);程式結束時先呼叫 worker 的 `request_stop()`,最多等 10 秒,仍在跑的隨行程結束。 | +| `_worker_thread.py` | 216 | `start_worker()`:在 daemon `threading.Thread` 上執行 `QObject` worker 的 `run()`(沒有 `QThread` 可被銷毀),並經由分頁擁有的中繼物件回報結果(回呼一律在 GUI 執行緒;worker 沒處理的例外也送到 `on_fail`);worker 留在模組登錄表直到 GUI 執行緒看到它結束,回傳 `WorkerHandle`(`isRunning()`);程式結束時先呼叫 worker 的 `request_stop()`,最多等 10 秒,仍在跑的隨行程結束。 | | `language_wrapper/` | 5,031 | 四語系字典(英/日/簡中/繁中)+ `multi_language_wrapper` 執行期切換器與監聽註冊表。 | -| `selector/` | 227 | 拖曳選取螢幕區域的半透明全螢幕覆蓋層與樣板裁切工具(互動式,但都有對應的程式化 API)。 | +| `selector/` | 216 | 拖曳選取螢幕區域的半透明全螢幕覆蓋層與樣板裁切工具(互動式,但都有對應的程式化 API)。 | > **分頁指令一律走 Actions 選單**:分頁本身只放輸入、表格與結果檢視,指令由視窗層選單暴露。 > 核心分頁在 `main_widget.py` 註冊時宣告動作;功能分頁實作 `menu_actions()`(目前 40 個檔案有此 hook)。 @@ -952,7 +954,7 @@ GUI 是**選用 extra**(`pip install je_auto_control[gui]`,PySide6 + qt-mate | diagnostics | `diagnostics_tab.py` | 91 | 執行子系統檢查並顯示結果。 | | report | `_report_tab.py` | 81 | 產生 HTML/JSON/XML 報表。 | -#### 遠端桌面 GUI(`gui/remote_desktop/`,19 檔/6,608 行) +#### 遠端桌面 GUI(`gui/remote_desktop/`,19 檔/6,646 行) | 模組 | 行數 | 職責 | | --- | ---: | --- | @@ -970,7 +972,7 @@ GUI 是**選用 extra**(`pip install je_auto_control[gui]`,PySide6 + qt-mate | `_helpers.py` | 249 | 面板共用輔助:翻譯、Qt→AC 鍵滑鼠對應、TLS context、狀態徽章、指紋與時間格式化。 | | `remote_screen_window.py` | 140 | 檢視端的彈出視窗。 | | `tray_icon.py` | 106 | WebRTC 主機的系統匣圖示。 | -| `annotation_overlay.py` | 136 | 主機端標註的透明最上層覆蓋。 | +| `annotation_overlay.py` | 174 | 主機端標註的透明最上層覆蓋。 | | `sparkline.py` | 77 | WebRTC 統計面板的迷你走勢圖。 | | `blanking_overlay.py` | 71 | 遠端連線期間的隱私遮蔽全螢幕覆蓋。 | | `viewer_screen_window.py` | 46 | 顯示連入檢視端分享畫面的彈出視窗。 | @@ -1068,26 +1070,26 @@ socket 預設綁 `127.0.0.1`;資源一律用 `with`。 | 層/子系統 | 檔案數 | 行數 | | --- | ---: | ---: | -| `gui/` | 94 | 27,525 | -| `utils/mcp_server/` | 35 | 18,798 | -| `utils/remote_desktop/` | 56 | 12,912 | -| `utils/executor/` | 7 | 9,504 | -| `utils/usb/` | 17 | 4,524 | +| `gui/` | 95 | 27,793 | +| `utils/mcp_server/` | 35 | 18,846 | +| `utils/remote_desktop/` | 56 | 13,014 | +| `utils/executor/` | 8 | 9,539 | +| `utils/usb/` | 17 | 4,572 | | `je_auto_control/`(頂層 3 檔) | 3 | 2,410 | -| `utils/accessibility/` | 14 | 3,117 | +| `utils/accessibility/` | 14 | 3,143 | | `wrapper/` | 19 | 3,631 | | `windows/` | 23 | 1,959 | | `utils/rest_api/` | 8 | 1,881 | | `utils/agent/` | 9 | 1,975 | | `linux_with_x11/` | 19 | 1,281 | | `linux_wayland/` | 17 | 2,921 | -| `utils/triggers/` | 4 | 1,377 | +| `utils/triggers/` | 4 | 1,383 | | `utils/ocr/` | 9 | 1,140 | | `utils/usbip/` | 5 | 1,008 | | `utils/assertion/` | 3 | 887 | | `osx/` | 17 | 925 | | `autocontrol-lsp/` | 8 | 744 | -| `utils/hotkey/` | 7 | 846 | -| 其餘模組(約 286 個 `utils/` 子套件 + `android/`/`ios/`/周邊小工具) | 679 | 54,959 | -| **總計** | **1,053** | **154,324** | +| `utils/hotkey/` | 7 | 852 | +| 其餘模組(約 286 個 `utils/` 子套件 + `android/`/`ios/`/周邊小工具) | 681 | 56,947 | +| **總計** | **1,057** | **156,851** | diff --git a/docs/source/Eng/doc/keyword_and_executor/keyword_and_executor_doc.rst b/docs/source/Eng/doc/keyword_and_executor/keyword_and_executor_doc.rst index 05386f1a7..3c8e4c3a2 100644 --- a/docs/source/Eng/doc/keyword_and_executor/keyword_and_executor_doc.rst +++ b/docs/source/Eng/doc/keyword_and_executor/keyword_and_executor_doc.rst @@ -27,6 +27,11 @@ For example: ["AC_write", {"write_string": "Hello"}] ] +An on / off parameter (``ignore_case``, ``present``, ``raise_on_fail``, ``paste`` …) +takes a JSON ``true`` / ``false``, or a string read by its spelling: ``"true"``, +``"yes"``, ``"on"`` and ``"1"`` are on, and any other string (``"false"``, ``"no"``, +``"off"``, ``"0"``) is off. + Available Action Commands ========================= diff --git a/docs/source/Eng/doc/mcp_server/mcp_server_doc.rst b/docs/source/Eng/doc/mcp_server/mcp_server_doc.rst index 9dba6643b..bf0622cda 100644 --- a/docs/source/Eng/doc/mcp_server/mcp_server_doc.rst +++ b/docs/source/Eng/doc/mcp_server/mcp_server_doc.rst @@ -104,6 +104,15 @@ are refused before the tool runs, as a tool execution error: a result with corrected arguments (MCP 2025-11-25). An unknown tool or a request that is not a ``tools/call`` at all is still a ``-32602`` protocol error. +A tool that fails with any exception is answered the same way, ``isError: true`` +with the error's type and message, so no call is left without a reply. A tool +that ran is not reported as failed because the audit log could not be written; +that is logged instead. A message that is not a JSON-RPC 2.0 request -- no or a +wrong ``jsonrpc``, a ``method`` that is not a string, an ``id`` that is not a +string, a number or ``null`` -- is ``-32600``. A request with ``"id": null`` is +answered with ``"id": null``; only a message without an ``id`` member is a +notification. + Resources, prompts, sampling ============================ diff --git a/docs/source/Eng/doc/new_features/v113_features_doc.rst b/docs/source/Eng/doc/new_features/v113_features_doc.rst index fe0d24189..4032004d9 100644 --- a/docs/source/Eng/doc/new_features/v113_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v113_features_doc.rst @@ -37,7 +37,14 @@ count placeholder, a plural ``offset:`` (``#`` becomes count − offset), nested arguments, and ICU apostrophe quoting (``''`` → ``'``; ``'{'`` → literal brace; ``'#'`` only inside a plural, elsewhere the apostrophes stay). ``plural_rules`` / ``ordinal_rules`` let you inject custom category functions; -``locale`` selects the built-ins (``en``, ``fr``). +``locale`` selects the rules by its language (``fr_FR`` and ``fr-CA`` use +``fr``): English and French are built in, any other locale uses Babel's CLDR +data when Babel is installed (``je_auto_control[locale]``) and raises +otherwise instead of falling back to English. ``=N`` selectors compare as +numbers (``=1.0`` matches 1). A pattern ICU rejects -- an unterminated +argument, a selector without ``{...}``, no ``other``, a duplicate selector, +``offset:`` anywhere but first -- raises ``MessageFormatError`` (an +``AutoControlException`` and a ``ValueError``). Executor commands ----------------- diff --git a/docs/source/Eng/doc/new_features/v114_features_doc.rst b/docs/source/Eng/doc/new_features/v114_features_doc.rst index b5ba22d20..0d98563eb 100644 --- a/docs/source/Eng/doc/new_features/v114_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v114_features_doc.rst @@ -35,8 +35,10 @@ Headless API ``ngettext`` evaluates the catalog's ``Plural-Forms`` expression (via ``gettext.c2py``) to pick the right form for ``n``; ``pgettext`` adds a disambiguation context. ``to_mo_bytes`` / ``compile_mo`` emit a standards- -compliant ``.mo`` that Python's own ``gettext.GNUTranslations`` can load, and -``read_mo`` / ``read_mo_file`` parse one back (little- or big-endian). +compliant ``.mo`` that Python's own ``gettext.GNUTranslations`` can load, +leaving out untranslated entries (an empty first ``msgstr``) and fuzzy ones as +``msgfmt`` does, so every reader falls back to the ``msgid``; ``read_mo`` / +``read_mo_file`` parse one back (little- or big-endian). Executor commands ----------------- diff --git a/docs/source/Eng/doc/new_features/v116_features_doc.rst b/docs/source/Eng/doc/new_features/v116_features_doc.rst index 3b39a672f..5355aad6d 100644 --- a/docs/source/Eng/doc/new_features/v116_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v116_features_doc.rst @@ -31,7 +31,9 @@ steps (``easing`` is any name from ``path_easings()`` — ``linear`` / ``ease_in_out_quad`` / ``ease_out_cubic`` / ``ease_in_cubic``) and does not duplicate the shared junction points. ``move_along_path`` emits move events through the path; ``drag_path`` presses at the first waypoint, moves through the -whole path, and releases at the last — for multi-stop drags. Both take a ``sink`` +whole path, and releases at the last — for multi-stop drags. If a move fails +partway, the button is released where the pointer stopped, so an aborted drag +does not drop at its target. Both take a ``sink`` override for headless testing. Executor commands diff --git a/docs/source/Eng/doc/new_features/v117_features_doc.rst b/docs/source/Eng/doc/new_features/v117_features_doc.rst index e36f20c53..fff2fabcf 100644 --- a/docs/source/Eng/doc/new_features/v117_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v117_features_doc.rst @@ -29,7 +29,8 @@ Headless API # {'op': 'key', 'key': 'delete'}, # {'op': 'type', 'text': 'hi'}] -``clear`` is ``"select_all"`` (the ``modifier``+A then Delete clear) or +``clear`` is ``"select_all"`` (the ``modifier``+A then Delete clear; Backspace on +macOS, whose key table has no Delete) or ``"none"``. ``paste=True`` enters the text through the clipboard (``modifier``+V) — the reliable path for Unicode / emoji / CJK that ``write`` cannot type — rather than typing key by key. ``modifier`` is the platform command key (``"ctrl"``; use diff --git a/docs/source/Eng/doc/new_features/v11_features_doc.rst b/docs/source/Eng/doc/new_features/v11_features_doc.rst index 892b475cb..634c06ffe 100644 --- a/docs/source/Eng/doc/new_features/v11_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v11_features_doc.rst @@ -68,6 +68,8 @@ store, then run the riskiest first (or only the top-k):: risky = select_flows(["login", "checkout", "report"], k=2) The score is ``0.5*failure_rate + 0.2*last_failed + 0.2*flakiness + -0.1*staleness``; a never-run flow scores ``0.8`` (untested is risky). +0.1*staleness`` over the newest ``window`` finished runs (runs still marked +running are not counted); a never-run flow scores ``0.8`` (untested is risky), +and a flow listed twice is ranked once. Exposed as ``AC_rank_tests`` / ``AC_select_tests`` and the ``ac_rank_tests`` / ``ac_select_tests`` MCP tools. diff --git a/docs/source/Eng/doc/new_features/v125_features_doc.rst b/docs/source/Eng/doc/new_features/v125_features_doc.rst index ccfa8ee9b..8c8dcb6c2 100644 --- a/docs/source/Eng/doc/new_features/v125_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v125_features_doc.rst @@ -35,7 +35,9 @@ cell, or ``{found: False, reason}`` when the index is out of range. Executor commands ----------------- -``AC_grid_cell`` takes ``boxes`` (a JSON ``[[x, y, w, h], ...]`` list, e.g. from a -prior ``AC_locate_all_image`` step) plus ``row`` / ``col`` / ``row_tolerance`` and -returns the cell dict. It is exposed as the MCP tool ``ac_grid_cell`` and as a +``AC_grid_cell`` takes ``boxes`` (a JSON ``[[x, y, w, h], ...]`` list) plus ``row`` / +``col`` / ``row_tolerance`` and returns the cell dict. ``AC_locate_all_image`` answers +``[left, top, right, bottom]`` boxes, so pass its output with ``"box_format": "ltrb"`` +(``cluster_grid`` / ``locate_cell`` take the same ``box_format``; dicts and OCR match +objects are read by their fields). It is exposed as the MCP tool ``ac_grid_cell`` and as a Script Builder command under **Mouse**. diff --git a/docs/source/Eng/doc/new_features/v128_features_doc.rst b/docs/source/Eng/doc/new_features/v128_features_doc.rst index efe84a211..d409b4865 100644 --- a/docs/source/Eng/doc/new_features/v128_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v128_features_doc.rst @@ -30,7 +30,10 @@ Headless API ``find_color_regions`` returns ``{x, y, width, height, area, center}`` for each blob within ``tolerance`` (per channel) of ``rgb`` and at least ``min_area`` pixels, largest first; ``find_color_region`` returns just the largest (or -``None``). ``haystack`` defaults to a screen grab of the optional ``region``. +``None``). ``haystack`` defaults to a screen grab of the optional ``region``, +``[left, top, right, bottom]`` on any monitor. Blobs found in a grabbed region +are in screen coordinates, so ``center`` can be clicked as it is; blobs in a +supplied ``haystack`` are in that image's pixels. Executor commands ----------------- diff --git a/docs/source/Eng/doc/new_features/v12_features_doc.rst b/docs/source/Eng/doc/new_features/v12_features_doc.rst index bbc473c53..a70471f53 100644 --- a/docs/source/Eng/doc/new_features/v12_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v12_features_doc.rst @@ -30,7 +30,7 @@ at every call site, and a UI change is fixed in one place:: info = repo.find_info("login.user") # {found, name, role, center} A locator is a small set of accessibility filters (``name`` / ``role`` / -``app_name``); resolving finds the live element through the accessibility +``app_name``; a blank value filters nothing and is refused); resolving finds the live element through the accessibility backend. Storage is a JSON file and works on any platform; resolution needs a platform accessibility backend. diff --git a/docs/source/Eng/doc/new_features/v139_features_doc.rst b/docs/source/Eng/doc/new_features/v139_features_doc.rst index 877ed9907..0ec8ca4f4 100644 --- a/docs/source/Eng/doc/new_features/v139_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v139_features_doc.rst @@ -31,7 +31,9 @@ Headless API ``val_min`` floor to skip greys, returning ``{x, y, width, height, area, center}`` per blob largest first — so it finds a colour at any brightness, unlike the RGB box. ``segment_hsv`` takes an explicit ``lower_hsv`` / ``upper_hsv`` band; ``color_mask`` -returns the raw uint8 mask. +returns the raw uint8 mask. ``region`` is ``[left, top, right, bottom]`` on any +monitor, and blobs found in it are in screen coordinates; blobs in a supplied +``haystack`` are in that image's pixels. Executor commands ----------------- diff --git a/docs/source/Eng/doc/new_features/v13_features_doc.rst b/docs/source/Eng/doc/new_features/v13_features_doc.rst index 61b3c2f71..f674c1099 100644 --- a/docs/source/Eng/doc/new_features/v13_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v13_features_doc.rst @@ -28,6 +28,9 @@ runs, instead of re-deriving the steps every time:: lib.search("auth") # find skills by name / description / tags lib.run("login") # replay through the executor +A skill that runs skills is bounded like a macro: nesting deeper than 50 fails +the outermost ``AC_skill_run`` instead of recursing. + Executor / MCP commands: ``AC_skill_save`` / ``AC_skill_run`` / ``AC_skill_list`` / ``AC_skill_remove`` / ``AC_skill_search`` (and the matching ``ac_skill_*`` MCP tools). This is the durable counterpart to the diff --git a/docs/source/Eng/doc/new_features/v141_features_doc.rst b/docs/source/Eng/doc/new_features/v141_features_doc.rst index 8bf7b2f2e..741db1207 100644 --- a/docs/source/Eng/doc/new_features/v141_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v141_features_doc.rst @@ -35,7 +35,9 @@ longest first; pass ``orientation`` other than ``any`` to keep only that kind. ``find_grid`` clusters the horizontal rules into row coordinates and the vertical rules into columns, returning ``{rows, cols, cells}`` (cells are the rectangles between consecutive rules). ``find_separators`` returns the merged coordinates of long divider -lines along ``axis``. A blank screen yields no lines / cells. +lines along ``axis``. A blank screen yields no lines / cells. Lines down to +``min_length`` are found (the Hough vote threshold follows it), and 16-bit or +float images are read as 8-bit. Executor commands ----------------- diff --git a/docs/source/Eng/doc/new_features/v152_features_doc.rst b/docs/source/Eng/doc/new_features/v152_features_doc.rst index e44af5182..f0b689abd 100644 --- a/docs/source/Eng/doc/new_features/v152_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v152_features_doc.rst @@ -31,7 +31,9 @@ Headless API click(*[target["x"] + target["width"] // 2, target["y"] + target["height"] // 2]) ``flatten_tree`` flattens a nested element tree, keeping only interactive roles by -default. ``observation_index`` clips to the ``viewport``, orders top-to-bottom / +default (the same roles, in any platform's spelling, as ``focus_order``). Elements +are placed by ``x`` / ``y`` / ``width`` / ``height`` or ``bounds``. ``observation_index`` +clips to the ``viewport`` (an element centred on its right or bottom edge is outside), orders top-to-bottom / left-to-right, caps at ``max_elements`` and assigns a stable ``index``. ``serialize_observation`` renders those as ``[i] role "name" @(cx,cy)`` lines. diff --git a/docs/source/Eng/doc/new_features/v167_features_doc.rst b/docs/source/Eng/doc/new_features/v167_features_doc.rst index 8939ab539..1ebd63804 100644 --- a/docs/source/Eng/doc/new_features/v167_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v167_features_doc.rst @@ -36,7 +36,7 @@ Headless API ``classify_effect`` returns an ``EffectVerdict`` (``effect`` / ``changed_near_target`` / ``changed_count`` / ``changed_centers`` / ``reason``). ``effect_near_point`` answers whether any -change landed within ``radius`` of an arbitrary point; ``is_no_op`` is the boolean shortcut. +change landed within ``radius`` (a circle) of an arbitrary point; ``is_no_op`` is the boolean shortcut. Executor commands ----------------- diff --git a/docs/source/Eng/doc/new_features/v171_features_doc.rst b/docs/source/Eng/doc/new_features/v171_features_doc.rst index f22e58bd2..5811b5dd9 100644 --- a/docs/source/Eng/doc/new_features/v171_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v171_features_doc.rst @@ -29,7 +29,8 @@ Headless API ``match_subpixel`` returns a ``SubPixelMatch`` (integer ``x`` / ``y`` / ``width`` / ``height`` / ``score`` plus float ``cx`` / ``cy`` and the ``offset_x`` / ``offset_y`` applied), or ``None`` -below ``min_score``. ``refine_peak`` returns the ``[-0.5, 0.5]`` quadratic-fit offset of a peak +below ``min_score``. ``cx`` is the matched box's centre in pixel indices, ``x + (width - 1) / 2``, +plus the offset. 16-bit and float images are matched in 8 bits. ``refine_peak`` returns the ``[-0.5, 0.5]`` quadratic-fit offset of a peak from its neighbours — usable on any correlation surface. Executor command diff --git a/docs/source/Eng/doc/new_features/v177_features_doc.rst b/docs/source/Eng/doc/new_features/v177_features_doc.rst index f1c074c02..ae787644b 100644 --- a/docs/source/Eng/doc/new_features/v177_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v177_features_doc.rst @@ -34,7 +34,8 @@ Headless API report when a spec is given. ``score_step_rule_based`` returns ``{outcome, process_score, reasons}`` — ``outcome`` is a binary success (the action did something *and* any postcondition held), ``process_score`` is a 0..1 quality from the effect class (halved if the postcondition -failed). ``to_judge_prompt`` renders the record for an external judge. +failed). An effect other than ``no_op`` / ``changed`` / ``changed_near_target`` / +``changed_elsewhere`` raises ``ValueError``. ``to_judge_prompt`` renders the record for an external judge. Executor commands ----------------- diff --git a/docs/source/Eng/doc/new_features/v184_features_doc.rst b/docs/source/Eng/doc/new_features/v184_features_doc.rst index eca801dc7..34f8a2ce3 100644 --- a/docs/source/Eng/doc/new_features/v184_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v184_features_doc.rst @@ -38,7 +38,10 @@ Headless API Focusability is role-based (the interactive roles: Button, Edit, CheckBox, ComboBox, RadioButton, Hyperlink, ListItem, MenuItem, Slider, Tab/TabItem, -TreeItem, …). ``focus_control`` locates by ``name`` / ``role`` / ``app_name`` / +TreeItem, …), read in any platform's spelling: UIA (``CheckBox``, or the raw +``ControlType_50002``), AT-SPI (``push button``), macOS AX (``AXTextField``) and +ARIA (``textbox``). A disabled control is left out of the Tab order, as ``Tab`` +skips it. ``focus_control`` locates by ``name`` / ``role`` / ``app_name`` / ``automation_id`` like the other native-control actions and returns ``bool``. Executor commands diff --git a/docs/source/Eng/doc/new_features/v192_features_doc.rst b/docs/source/Eng/doc/new_features/v192_features_doc.rst index d717e2e1f..08f555eca 100644 --- a/docs/source/Eng/doc/new_features/v192_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v192_features_doc.rst @@ -10,7 +10,9 @@ instead of mis-pairing everything — and classifies the differences: * **added** / **removed** — steps present in only one run, * **status_flips** — an aligned step whose status changed, with the new failure's :func:`failure_signature` when it carries an ``error``, -* **timing_regressions** — an aligned step that got ``regress_factor`` x slower. +* **timing_regressions** — an aligned step that got ``regress_factor`` x slower. A + step recorded at 0 s is compared as 0.1 s and reported with ``ratio`` ``None``; + non-finite or negative durations are not compared. A step is any dict with a name key (default ``"name"``) and optional ``status`` / ``duration`` / ``error``. Pure standard library; no device, no ``PySide6``. diff --git a/docs/source/Eng/doc/new_features/v193_features_doc.rst b/docs/source/Eng/doc/new_features/v193_features_doc.rst index 3abad25ec..071195b7e 100644 --- a/docs/source/Eng/doc/new_features/v193_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v193_features_doc.rst @@ -34,7 +34,8 @@ Headless API cofailure_pairs(runs, threshold=0.6) # [{"tests": ["test_a", "test_b"], "jaccard": 1.0, "co_failures": 3}] -``threshold`` is the minimum co-failure Jaccard to link two tests; ``min_size`` +``threshold`` is the minimum co-failure Jaccard to link two tests, which must also +have failed together at least once; a run given as one string is one test. ``min_size`` (default ``2``) drops singletons so only genuine clusters surface. Clusters come back largest / most cohesive first. diff --git a/docs/source/Eng/doc/new_features/v194_features_doc.rst b/docs/source/Eng/doc/new_features/v194_features_doc.rst index f8568eb60..7cc37ba02 100644 --- a/docs/source/Eng/doc/new_features/v194_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v194_features_doc.rst @@ -14,7 +14,8 @@ bottleneck steps, so you can read a single slow run instead of an average. A step is any dict with a name (default ``"name"``) and a ``duration``; an optional ``start`` places it on an absolute timeline (overlapping / parallel -steps), else steps are laid out back-to-back. Pure standard library; no device, +steps), else steps are laid out back-to-back. A duration must be finite and not +negative and a start finite, or ``ValueError`` names the step. Pure standard library; no device, no ``PySide6``. Headless API diff --git a/docs/source/Eng/doc/new_features/v209_features_doc.rst b/docs/source/Eng/doc/new_features/v209_features_doc.rst index 94f2c369c..a00538e39 100644 --- a/docs/source/Eng/doc/new_features/v209_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v209_features_doc.rst @@ -13,7 +13,8 @@ contended UI automation needs: ``retry_budget`` adds both. :class:`RetryBudget` is bounded by ``max_attempts`` *and / or* ``deadline_s``; :func:`run_with_budget` honours whichever is hit first and never sleeps past the deadline. Delays use capped exponential backoff -with a selectable jitter strategy (``full`` / ``equal`` / ``none``). The +with a selectable jitter strategy (``full`` / ``equal`` / ``none``, any case; another +value raises ``ValueError``, as does a delay that is not finite and >= 0). The randomness source (``uniform``), the clock and the sleeper are all injectable, so every delay and decision is deterministic in tests. Imports no ``PySide6``. diff --git a/docs/source/Eng/doc/new_features/v24_features_doc.rst b/docs/source/Eng/doc/new_features/v24_features_doc.rst index 1f7a95fd2..dc6826670 100644 --- a/docs/source/Eng/doc/new_features/v24_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v24_features_doc.rst @@ -47,5 +47,7 @@ Input-sequence DSL A declarative mini-language for press-hold-release chords and repeated input: action ops (``press`` / ``release`` / ``key`` / ``click`` / ``move`` / ``scroll``) plus control ops ``{op: wait, ms}`` and -``{op: repeat, times, steps:[...]}``. Returns the flattened executed log. +``{op: repeat, times, steps:[...]}``. A ``click`` without ``x`` / ``y`` clicks where +the pointer is; a ``move`` needs both. A negative ``wait`` is 0 and a non-finite +one raises ``ValueError``. Returns the flattened executed log. Exposed as ``AC_input_sequence`` / ``ac_input_sequence``. diff --git a/docs/source/Eng/doc/new_features/v35_features_doc.rst b/docs/source/Eng/doc/new_features/v35_features_doc.rst index 42557b8e4..ee9ca171a 100644 --- a/docs/source/Eng/doc/new_features/v35_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v35_features_doc.rst @@ -30,8 +30,9 @@ Headless API # .received file, then bless it: approve_artifact("invoice_render", approvals_dir="tests/.approvals") -``content`` may be ``str`` or ``bytes`` (pass ``extension="png"`` for binary -snapshots). A verified run clears any stale received file. +``content`` may be ``bytes`` (pass ``extension="png"`` for binary snapshots), +``str`` (stored as UTF-8) or any other JSON value (stored as sorted, indented +JSON). A verified run clears any stale received file. ``pending_artifacts(dir)`` lists names still awaiting approval. ``ApprovalResult`` carries ``status`` (``verified`` / ``mismatch`` / ``new``), ``match``, and both file paths. diff --git a/docs/source/Eng/doc/new_features/v36_features_doc.rst b/docs/source/Eng/doc/new_features/v36_features_doc.rst index 35b41373a..3044d0b50 100644 --- a/docs/source/Eng/doc/new_features/v36_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v36_features_doc.rst @@ -43,7 +43,9 @@ Headless API print(result["score"], result["checks"]) ``score`` is the fraction of applicable checks that passed; ``passed`` is true -only when all pass; an empty rubric trivially passes. Each entry in ``checks`` +only when all pass; an empty rubric trivially passes. A rubric that is not an +object, or that has an unknown key (``"forbiden_actions"``), raises ``ValueError`` +rather than passing with no checks. Each entry in ``checks`` is ``{name, passed, detail}`` so a failure pinpoints the violated expectation. Executor command diff --git a/docs/source/Eng/doc/new_features/v3_features_doc.rst b/docs/source/Eng/doc/new_features/v3_features_doc.rst index 465ec3751..f62b05191 100644 --- a/docs/source/Eng/doc/new_features/v3_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v3_features_doc.rst @@ -193,7 +193,9 @@ Quarantine (closing the loop) A quarantined case name is *skipped* by the suite runner (recorded as ``skipped`` with reason ``quarantined``) so a known-flaky case stops poisoning the suite's red/green status until it is fixed. The store is a -small JSON file (mode 0600 on POSIX) that persists across restarts:: +small JSON file (mode 0600 on POSIX) that persists across restarts; every change +takes the file's lock and re-reads it, so a runner, the CLI and the GUI can share +one file without overwriting each other:: from je_auto_control import ( default_quarantine_store, auto_quarantine_from_flakiness, diff --git a/docs/source/Eng/doc/new_features/v47_features_doc.rst b/docs/source/Eng/doc/new_features/v47_features_doc.rst index 5acddfde9..31c180f6e 100644 --- a/docs/source/Eng/doc/new_features/v47_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v47_features_doc.rst @@ -35,6 +35,7 @@ Executor command ---------------- ``AC_mine_actions`` takes ``actions`` (a list, or a JSON-string list from the -visual builder) plus ``min_len`` / ``max_len`` / ``min_count`` and returns +visual builder) plus ``min_len`` / ``max_len`` / ``min_count`` (``min_len`` and +``min_count`` at least 1, ``max_len`` at least ``min_len``, else ``ValueError``) and returns ``{total_actions, patterns, candidates}``. The same operation is exposed as the MCP tool ``ac_mine_actions`` and as a Script Builder command under **Report**. diff --git a/docs/source/Eng/doc/new_features/v48_features_doc.rst b/docs/source/Eng/doc/new_features/v48_features_doc.rst index 41456c233..1c1225925 100644 --- a/docs/source/Eng/doc/new_features/v48_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v48_features_doc.rst @@ -36,7 +36,9 @@ Types are ``text`` / ``int`` / ``bool`` / ``credential``; ``get`` coerces to the declared type and falls back to the ``default`` environment unless disabled. ``active_environment()`` reads ``JE_AUTOCONTROL_ENV``. ``list`` / ``delete`` round out the store. ``set`` refuses an unknown type and a value the type cannot read -(``"eighty"`` as an ``int``) at write time. +(``"eighty"`` as an ``int``) at write time. An ``int`` takes whole numbers only (not +``3.7`` or ``True``), and a ``bool`` string must be one of true / false / yes / no / +on / off / 1 / 0. Executor commands ----------------- diff --git a/docs/source/Eng/doc/new_features/v4_features_doc.rst b/docs/source/Eng/doc/new_features/v4_features_doc.rst index c1b868985..378ba0571 100644 --- a/docs/source/Eng/doc/new_features/v4_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v4_features_doc.rst @@ -74,7 +74,8 @@ Flow control & variables assertion DSL. * **Read into a variable** — bind external data into the flow scope for later ``${var}`` use: ``AC_ocr_to_var`` (region text), ``AC_shell_to_var`` - (command stdout, decoded with ``encoding`` -- default the locale's; a + (command stdout, decoded with ``encoding`` -- default the locale's code + page, also under Python 3.15's UTF-8 mode; a timeout ends the command and everything it started, and a ``.bat`` / ``.cmd`` argument holding cmd syntax is refused), ``AC_read_file_to_var`` (file text; UTF-8 with or without a byte-order mark unless ``encoding`` diff --git a/docs/source/Eng/doc/new_features/v51_features_doc.rst b/docs/source/Eng/doc/new_features/v51_features_doc.rst index cf966623f..52c975774 100644 --- a/docs/source/Eng/doc/new_features/v51_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v51_features_doc.rst @@ -23,7 +23,11 @@ compared value is a JSON number, a quoted string, ``true``, ``false`` or ``null``. Values of different types never compare equal (``true != 1``), and ``<`` / ``>`` order only two numbers or two strings (``<=`` is ``<`` or ``==``, so ``null <= null``). Quoted names and strings decode RFC 9535 -escapes (``['a\'b']`` is the key ``a'b``). +escapes (``['a\'b']`` is the key ``a'b``, and ``["\uD83D\uDE00"]`` the +one-character key it spells); an unpaired surrogate, a raw control character +or the other kind of quote escaped raises. An index is an RFC 9535 integer: +``[01]``, ``[-0]`` and indices past 2**53-1 raise, and ``..`` must be followed +by a name, ``*`` or ``[...]``. A path the subset cannot read -- an unsupported filter or value, a slice (``[0:2]``) or union (``[0,1]``, ``['a','b']``), an empty ``[]``, an unterminated ``[``, a stray character -- raises ``ValueError`` instead of matching something else. diff --git a/docs/source/Eng/doc/new_features/v54_features_doc.rst b/docs/source/Eng/doc/new_features/v54_features_doc.rst index 74d56fdde..c92e1b986 100644 --- a/docs/source/Eng/doc/new_features/v54_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v54_features_doc.rst @@ -51,5 +51,7 @@ Command Effect ``AC_repair_approve`` Approve a pending suggestion. ================================ =================================================== -The same operations are exposed as MCP tools (``ac_repair_*``) and as Script -Builder commands under **Tools**. +Each takes an optional ``db`` (a JSON path); without one, the commands share one +in-memory store per process (``repair_store()``), so a suggestion recorded by +``AC_repair_record`` is found by the others. The same operations are exposed as MCP +tools (``ac_repair_*``) and as Script Builder commands under **Tools**. diff --git a/docs/source/Eng/doc/new_features/v57_features_doc.rst b/docs/source/Eng/doc/new_features/v57_features_doc.rst index d946b8672..b26d1183d 100644 --- a/docs/source/Eng/doc/new_features/v57_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v57_features_doc.rst @@ -21,7 +21,16 @@ Supported keywords depth). * numbers — ``minimum`` / ``maximum`` / ``exclusiveMinimum`` / ``exclusiveMaximum`` / ``multipleOf``. -* strings — ``minLength`` / ``maxLength`` / ``pattern``. +* strings — ``minLength`` / ``maxLength`` / ``pattern``. ``pattern`` and + ``patternProperties`` match as ECMA-262 does, which is what the + specification says: ``$`` does not match before a trailing newline; + ``\d``, ``\w`` and ``\b`` are ASCII, so ``^\d+$`` rejects Arabic-Indic + digits; ``.`` stops at every line terminator; ``\s`` is ECMA-262 + whitespace. ``\cX``, ``\u{...}``, ``[]`` / ``[^]``, ``(?...)`` / + ``\k`` and ``\p{...}`` / ``\P{...}`` for the General_Category + values (``L``, ``Letter``, ``Nd``, ``digit``, ``gc=Lu``...) plus ``Any``, + ``ASCII`` and ``Assigned`` are understood; script properties raise + ``AutoControlJsonException``. * arrays — ``minItems`` / ``maxItems`` / ``uniqueItems`` / ``items`` / ``prefixItems`` / ``contains``. * objects — ``required`` / ``minProperties`` / ``maxProperties`` / diff --git a/docs/source/Eng/doc/new_features/v61_features_doc.rst b/docs/source/Eng/doc/new_features/v61_features_doc.rst index 7b9d2d31c..2e6e03764 100644 --- a/docs/source/Eng/doc/new_features/v61_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v61_features_doc.rst @@ -34,10 +34,10 @@ Headless API token = encode_jwt({"sub": "user1", "aud": "api", "exp": 1893456000}, secret) # -> "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...." - # default policy: HS256 only, verify exp/nbf, no audience/issuer check - claims = decode_jwt(token, secret) + # the token carries "aud", so the policy must name the audience to accept + claims = decode_jwt(token, secret, ClaimsPolicy(audience="api")) - # tighten the policy for audience / issuer / leeway / algorithms + # tighten the policy for issuer / leeway / algorithms as well policy = ClaimsPolicy(algorithms=("HS256",), audience="api", issuer="my-service", leeway=30) claims = decode_jwt(token, secret, policy) @@ -47,7 +47,11 @@ Headless API validates the standard claims against a :class:`ClaimsPolicy` (``exp`` / ``nbf`` with ``leeway``, ``aud`` membership, ``iss`` match) using an injectable ``now``; it raises ``ExpiredTokenError`` / ``InvalidSignatureError`` / ``JwtError`` on -failure. The minted token drops straight into the HTTP client: +failure. The default policy is HS256 only with ``exp`` / ``nbf`` checked; a +token that carries ``aud`` is refused unless ``ClaimsPolicy.audience`` names +one of its values (RFC 7519 4.1.3), so a token minted for one service does not +verify at another that shares the key. An empty or non-string key, and claims +or headers that are not JSON (a ``datetime``, ``NaN``), raise ``JwtError``. The minted token drops straight into the HTTP client: .. code-block:: python diff --git a/docs/source/Eng/doc/new_features/v62_features_doc.rst b/docs/source/Eng/doc/new_features/v62_features_doc.rst index a112bc005..bb59cb863 100644 --- a/docs/source/Eng/doc/new_features/v62_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v62_features_doc.rst @@ -48,6 +48,7 @@ Executor command ``AC_rate_limit`` takes a limiter ``name`` plus ``rate`` / ``capacity`` / ``n`` and tries to take ``n`` tokens from that named token bucket (created on first -use), returning ``{acquired, tokens, wait}`` so a flow can gate or defer an +use, and rebuilt when a call names a different ``rate`` or ``capacity``; the +executor and MCP share one set of buckets, ``named_bucket``), returning ``{acquired, tokens, wait}`` so a flow can gate or defer an action. The same operation is exposed as the MCP tool ``ac_rate_limit`` and as a Script Builder command under **Flow**. diff --git a/docs/source/Eng/doc/new_features/v64_features_doc.rst b/docs/source/Eng/doc/new_features/v64_features_doc.rst index ae004e9c6..64af30996 100644 --- a/docs/source/Eng/doc/new_features/v64_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v64_features_doc.rst @@ -34,12 +34,15 @@ Headless API ``SearchIndex.add`` / ``remove`` keep the index up to date incrementally; ``build`` indexes a ``{doc_id: text}`` map (or ``(id, text)`` pairs). ``search`` returns ranked ``SearchHit(doc_id, score)`` results — by default BM25 -(``k1=1.5``, ``b=0.75``), or ``mode="tfidf"``. The scoring is the standard +(``k1=1.5``, ``b=0.75``), or ``mode="tfidf"`` (log-scaled term frequency +times ``ln(N / df) + 1``, so a term in every document still counts). The BM25 scoring is the standard Okapi formula with ``IDF = ln(1 + (N − df + 0.5) / (df + 0.5))``, so a rare term out-ranks a common one, term-frequency saturates (``k1``), and long documents are normalized down (``b``). A ``stop_words`` set can be supplied to drop noise terms. Terms are case-folded runs of letters and digits in any script, so -``登入`` and ``café`` are indexed whole; stop words are folded the same way. Results are deterministic (ties broken by ``doc_id``). +``café`` is indexed whole; a run of kana, CJK ideographs or Hangul, written +without spaces, is indexed as its character bigrams, so ``登入`` is found inside +``請先登入系統``. Stop words are folded the same way. Results are deterministic (ties broken by ``doc_id``). Executor command ---------------- diff --git a/docs/source/Eng/doc/new_features/v66_features_doc.rst b/docs/source/Eng/doc/new_features/v66_features_doc.rst index 598a7d4e0..7097b858c 100644 --- a/docs/source/Eng/doc/new_features/v66_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v66_features_doc.rst @@ -10,7 +10,10 @@ Supported rule parts: ``FREQ`` (DAILY/WEEKLY/MONTHLY/YEARLY), ``INTERVAL``, ``COUNT``, ``UNTIL``, ``BYDAY`` (incl. ordinals like ``2MO`` / ``-1FR``), ``BYMONTHDAY`` (incl. negatives), ``BYMONTH``, ``BYSETPOS`` and ``WKST``. Time-level parts and BYWEEKNO/BYYEARDAY are out of scope: ``parse_rrule`` raises -``AutoControlException`` for them, and for a rule with both ``COUNT`` and ``UNTIL``. Pure standard library +``AutoControlException`` for them, for a rule with both ``COUNT`` and ``UNTIL``, +and for combinations RFC 5545 forbids (``BYMONTHDAY`` with ``WEEKLY``, a numbered +``BYDAY`` with ``DAILY`` / ``WEEKLY``, an unknown ``WKST``). Without ``BYMONTH`` +a ``YEARLY`` ``BYDAY`` ordinal counts within the year. Pure standard library (``datetime`` + ``calendar``); the clock is injectable so ``next_occurrence`` is deterministic. Imports no ``PySide6``. @@ -37,8 +40,11 @@ Headless API ``parse_rrule`` accepts the rule with or without the ``RRULE:`` prefix and returns a frozen ``Recurrence``. ``occurrences`` yields datetimes anchored at ``dtstart`` (its time-of-day and timezone are applied to every occurrence), -bounded by ``COUNT`` / ``UNTIL`` (or the ``count=`` / ``until=`` overrides) and -a safety cap. A date-only ``UNTIL`` bounds the whole day inclusively. +bounded by ``COUNT`` / ``UNTIL``; the ``count=`` / ``until=`` arguments narrow +them further (the smaller wins) and ``max_iter`` caps a rule without a count. +A series ends at 9999-12-31, or once 400 years (scaled up for longer +intervals) pass without an occurrence, so a rule that can never match still +ends. A date-only ``UNTIL`` bounds the whole day inclusively. ``next_occurrence`` returns the first occurrence at or after ``now``. Executor commands diff --git a/docs/source/Eng/doc/new_features/v76_features_doc.rst b/docs/source/Eng/doc/new_features/v76_features_doc.rst index c4c5d7f52..ac6a3fdd2 100644 --- a/docs/source/Eng/doc/new_features/v76_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v76_features_doc.rst @@ -32,11 +32,19 @@ Headless API ``SpanContext`` is the immutable (``trace_id``, ``span_id``, ``trace_flags``, ``tracestate``) tuple. ``new_root_context`` mints a fresh trace; ``child_context`` -keeps the trace id and inherited state but allocates a new span id. +keeps the trace id and inherited state but allocates a new span id, clearing +flag bits other than sampled and random. ``parse_traceparent`` / ``format_traceparent`` round-trip the version-``00`` header (a newer version is read as ``00`` with any extra fields ignored; version -``ff``, malformed or all-zero IDs raise ``TraceContextError``); ``parse_tracestate`` / ``format_tracestate`` handle the -vendor list. ``inject_context`` writes the headers; ``extract_context`` reads +``ff``, malformed or all-zero IDs raise ``TraceContextError``, and +``format_traceparent`` validates a hand-built context before writing it); +``parse_tracestate`` / ``format_tracestate`` handle the vendor list. Parsing +trims only the spaces and tabs around commas, keeps a value's leading spaces, +discards a member whose key or value breaks the grammar (an empty value, ``=`` +or ``,`` inside, over 256 characters, a control character), stops after 32 +members, and returns ``[]`` for a duplicated key; formatting raises +``TraceContextError`` for such a member instead of writing it out. +``inject_context`` writes the headers; ``extract_context`` reads them back (case-insensitively) and returns ``None`` for a missing or invalid ``traceparent``, so the receiver starts a new trace as W3C Trace Context says. diff --git a/docs/source/Eng/doc/new_features/v77_features_doc.rst b/docs/source/Eng/doc/new_features/v77_features_doc.rst index 5cf6ff9ca..a42fc0791 100644 --- a/docs/source/Eng/doc/new_features/v77_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v77_features_doc.rst @@ -28,11 +28,14 @@ Headless API ``profile_rows`` returns ``{row_count, columns}`` where each column carries its count, null count and fraction, distinct count, a uniqueness flag, the inferred -type (``int`` / ``number`` / ``bool`` / ``str``), the top values with counts, -and ``min`` / ``max`` / ``mean`` for numeric columns. ``infer_schema`` turns +type (``int`` / ``number`` / ``bool`` / ``str``, or ``mixed`` when no one type +holds), the top values with counts (``True`` and ``1`` count apart), and +``min`` / ``max`` / ``mean`` for numeric columns (exact for ``int`` columns, +over the finite values otherwise). ``infer_schema`` turns that profile into a schema the existing ``validate_rows`` understands: a column is ``required`` when it has no nulls, ``unique`` when every non-null value is -distinct, and carries numeric bounds. Pass an explicit ``columns`` list to +distinct, and carries numeric bounds; a ``mixed`` column gets no ``type`` rule. +Pass an explicit ``columns`` list to restrict either function to a subset. Executor commands diff --git a/docs/source/Eng/doc/new_features/v78_features_doc.rst b/docs/source/Eng/doc/new_features/v78_features_doc.rst index 3fcacf598..1c00c5ed2 100644 --- a/docs/source/Eng/doc/new_features/v78_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v78_features_doc.rst @@ -26,7 +26,8 @@ Headless API # or convert a problem response into an exception: raise_for_problem(response) # raises HttpProblemError -``is_problem`` checks the ``Content-Type`` (case-insensitively). +``is_problem`` checks that the ``Content-Type`` media type is +``application/problem+json`` (case-insensitively, parameters ignored). ``parse_problem`` returns a ``ProblemDetails`` (``type`` defaulting to ``about:blank``, an integer ``status`` when coercible, and all non-registered keys collected into ``extensions``) or ``None`` when the response is not a diff --git a/docs/source/Eng/doc/new_features/v79_features_doc.rst b/docs/source/Eng/doc/new_features/v79_features_doc.rst index 395470e3a..fc8f8cf5a 100644 --- a/docs/source/Eng/doc/new_features/v79_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v79_features_doc.rst @@ -28,11 +28,16 @@ Headless API ``export`` prefix, validates keys, and resolves values: single-quoted values are literal apart from ``\'`` and ``\\`` (as python-dotenv reads them), double-quoted values process ``\n`` / ``\t`` / ``\\`` / ``\"`` escapes, and unquoted values drop a trailing `` #`` comment and surrounding -whitespace. A quoted value ends at its closing quote, so a comment after it is -dropped, and it may span several lines. ``dotenv_values`` reads and parses a file; ``load_dotenv`` merges a +whitespace; ``#`` starts a comment only after whitespace, so ``COLOR=#ff0000`` +keeps its value while ``KEY= # note`` is empty. A quoted value ends at its +closing quote, so a comment after it is dropped, and it may span several lines, +keeping each line's trailing whitespace. A leading byte-order mark is skipped. +``dotenv_values`` reads and parses a file; ``load_dotenv`` merges a file into an explicit ``env`` mapping (keeping existing keys unless ``override``); ``dump_dotenv`` serialises a mapping back to ``.env`` text, -quoting values that need it. +quoting values that need it, and raises ``DotenvError`` (an +``AutoControlException`` and a ``ValueError``) for a key the parser would not +read back, such as one holding a line break or ``=``. Executor commands ----------------- diff --git a/docs/source/Eng/doc/new_features/v81_features_doc.rst b/docs/source/Eng/doc/new_features/v81_features_doc.rst index e5b820e65..92dcd9f9b 100644 --- a/docs/source/Eng/doc/new_features/v81_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v81_features_doc.rst @@ -34,7 +34,8 @@ defaults to insertion order, so later layers override earlier ones). merged recursively while scalars and lists are replaced. ``get`` reads a dotted key from the resolved config with a default; ``explain`` returns a ``SourceTrace`` naming the winning layer for a dotted key (raising ``KeyError`` -when absent). ``deep_merge`` is exposed as a standalone two-mapping helper. +when absent). ``deep_merge`` is exposed as a standalone two-mapping helper; its +result shares no nested value with either input. Executor commands ----------------- diff --git a/docs/source/Eng/doc/new_features/v82_features_doc.rst b/docs/source/Eng/doc/new_features/v82_features_doc.rst index e74a88c38..b11e2596c 100644 --- a/docs/source/Eng/doc/new_features/v82_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v82_features_doc.rst @@ -30,7 +30,8 @@ diverge). ``ks_two_sample`` returns the maximum empirical-CDF gap and a p-value from the Kolmogorov distribution. ``categorical_drift`` compares label frequencies via a chi-square statistic and the total-variation distance. ``detect_drift`` wraps the numeric path into one report with a ``drifted`` -verdict at ``threshold`` (default ``0.25``). +verdict at ``threshold`` (default ``0.25``). A NaN in either numeric sample raises +``ValueError`` naming its index: drop or impute missing values first. Executor commands ----------------- diff --git a/docs/source/Eng/doc/new_features/v83_features_doc.rst b/docs/source/Eng/doc/new_features/v83_features_doc.rst index 3240a0ae4..75eb48d52 100644 --- a/docs/source/Eng/doc/new_features/v83_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v83_features_doc.rst @@ -29,7 +29,8 @@ Headless API ``diff_rows`` keys both row-sets and returns ``{added, removed, changed, unchanged}``: ``added`` / ``removed`` / ``unchanged`` are row lists, while ``changed`` holds ``{key, old, new}`` entries (the key is a scalar for a single -column or a list for a composite key). On duplicate keys the last row wins. +column or a list for a composite key). On duplicate keys the last row wins; an empty +key, or a row without a key column, raises ``ValueError``. NaN cells equal NaN. ``cell_changes`` expands the changed rows into ``{key, column, old, new}`` records. ``summarize_diff`` counts each bucket. diff --git a/docs/source/Eng/doc/new_features/v84_features_doc.rst b/docs/source/Eng/doc/new_features/v84_features_doc.rst index 1a93fb172..d0c0dfa90 100644 --- a/docs/source/Eng/doc/new_features/v84_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v84_features_doc.rst @@ -27,7 +27,8 @@ Headless API ``Baggage`` wraps an immutable key-value map: ``get`` reads, ``set`` / ``remove`` return new instances, and ``to_dict`` exports the entries. ``parse_baggage`` reads the header (dropping optional ``;metadata`` and rejecting empty keys), -``format_baggage`` percent-encodes keys and values back into a header value, +``format_baggage`` percent-encodes keys and values back into a header value +(``Baggage`` refuses an empty key with ``ValueError``, since no parser reads it back), and ``inject_baggage`` / ``extract_baggage`` write and read the ``baggage`` header on a request dict (extraction is case-insensitive). Pairs naturally with ``trace_context`` to carry context alongside the trace. diff --git a/docs/source/Eng/doc/new_features/v85_features_doc.rst b/docs/source/Eng/doc/new_features/v85_features_doc.rst index 5eab9dcf5..fbedfae32 100644 --- a/docs/source/Eng/doc/new_features/v85_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v85_features_doc.rst @@ -43,3 +43,7 @@ Executor commands ``AC_resolve_refs`` resolves every reference inside ``obj`` and returns ``{resolved}``. Both are exposed as MCP tools (``ac_resolve_ref`` / ``ac_resolve_refs``) and as Script Builder commands under **Security**. +Their results are recorded (the executor record, the MCP result), so they +refuse ``secret://`` with ``SecretRefError``: secret values never enter those +records. Reference ``${secrets.NAME}`` in the step that needs the value, or call +``resolve_ref`` from Python. diff --git a/docs/source/Eng/doc/new_features/v87_features_doc.rst b/docs/source/Eng/doc/new_features/v87_features_doc.rst index ad0557300..a1dcfdbdb 100644 --- a/docs/source/Eng/doc/new_features/v87_features_doc.rst +++ b/docs/source/Eng/doc/new_features/v87_features_doc.rst @@ -26,8 +26,12 @@ Headless API pages = paginate(start_url, fetch, max_pages=50) ``parse_link_header`` returns a list of ``Link`` (``uri``, ``rel``, and all -``params``), tolerating quoted values that contain commas and multiple links in -one header. ``links_by_rel`` indexes by each (space-separated) relation, +``params``), reading the header with the RFC 8288 Appendix B algorithm: quoted +values keep their commas, semicolons and escapes, an unquoted value runs to the +next ``;`` or ``,`` (so ``title=x= 0 的 +延遲也一樣)。隨機來源(``uniform``)、時鐘與睡眠器 皆可注入,故每個延遲與決策在測試中都是確定的。不匯入 ``PySide6``。 無頭 API diff --git a/docs/source/Zh/doc/new_features/v24_features_doc.rst b/docs/source/Zh/doc/new_features/v24_features_doc.rst index fb865bf1f..f5beddde0 100644 --- a/docs/source/Zh/doc/new_features/v24_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v24_features_doc.rst @@ -45,5 +45,6 @@ 用於按住-放開組合鍵與重複輸入的宣告式迷你語言:動作 op(``press`` / ``release`` / ``key`` / ``click`` / ``move`` / ``scroll``)加上控制 op -``{op: wait, ms}`` 與 ``{op: repeat, times, steps:[...]}``。回傳攤平後的 +``{op: wait, ms}`` 與 ``{op: repeat, times, steps:[...]}``。沒有 ``x`` / ``y`` 的 ``click`` 在指標所在處點擊; +``move`` 兩者都要有。負的 ``wait`` 視為 0,非有限值丟出 ``ValueError``。回傳攤平後的 執行記錄。對應 ``AC_input_sequence`` / ``ac_input_sequence``。 diff --git a/docs/source/Zh/doc/new_features/v35_features_doc.rst b/docs/source/Zh/doc/new_features/v35_features_doc.rst index 348cfd67d..3b139f5f1 100644 --- a/docs/source/Zh/doc/new_features/v35_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v35_features_doc.rst @@ -26,7 +26,8 @@ # 首次執行為 "new",輸出變更為 "mismatch";審查 .received 檔後再核可: approve_artifact("invoice_render", approvals_dir="tests/.approvals") -``content`` 可為 ``str`` 或 ``bytes``(二進位快照請傳 ``extension="png"``)。相符的執 +``content`` 可為 ``bytes``(二進位快照請傳 ``extension="png"``)、``str``(以 UTF-8 儲存)或其他任何 JSON 值 +(以排序、縮排的 JSON 儲存)。相符的執 行會清除任何過期的 received 檔。``pending_artifacts(dir)`` 列出仍待核准的名稱。 ``ApprovalResult`` 帶有 ``status``(``verified`` / ``mismatch`` / ``new``)、 ``match`` 及兩個檔案路徑。 diff --git a/docs/source/Zh/doc/new_features/v36_features_doc.rst b/docs/source/Zh/doc/new_features/v36_features_doc.rst index 0fdf7ffbc..5df6802a3 100644 --- a/docs/source/Zh/doc/new_features/v36_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v36_features_doc.rst @@ -40,7 +40,8 @@ Rubric 鍵 意義 assert result["passed"] # 所有適用的檢查都通過 print(result["score"], result["checks"]) -``score`` 為通過的適用檢查佔比;``passed`` 僅在全部通過時為真;空 rubric 直接通過。 +``score`` 為通過的適用檢查佔比;``passed`` 僅在全部通過時為真;空 rubric 直接通過。rubric 不是物件,或含有未知的鍵(``"forbiden_actions"``) +時丟出 ``ValueError``,而不是在沒有任何檢查的情況下通過。 ``checks`` 中每個項目為 ``{name, passed, detail}``,因此失敗時可精準指出被違反的期望。 執行器指令 diff --git a/docs/source/Zh/doc/new_features/v3_features_doc.rst b/docs/source/Zh/doc/new_features/v3_features_doc.rst index c506fe255..994515499 100644 --- a/docs/source/Zh/doc/new_features/v3_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v3_features_doc.rst @@ -174,7 +174,8 @@ GUI:**Flaky Tests** 分頁。 被隔離的案例名稱會被套件執行器*跳過*(記為 ``skipped``,原因 ``quarantined``), 讓已知不穩定的案例在修好前不再污染套件的紅 / 綠狀態。隔離區是一個小型 JSON -檔(POSIX 上為 0600 權限),可跨重啟保存:: +檔(POSIX 上為 0600 權限),可跨重啟保存;每次變更都會取得檔案鎖並重新讀取,所以執行器、CLI 與 GUI 可以共用同一個 +檔案而不互相覆蓋:: from je_auto_control import ( default_quarantine_store, auto_quarantine_from_flakiness, diff --git a/docs/source/Zh/doc/new_features/v47_features_doc.rst b/docs/source/Zh/doc/new_features/v47_features_doc.rst index e3ee9b3a5..195f22c55 100644 --- a/docs/source/Zh/doc/new_features/v47_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v47_features_doc.rst @@ -31,6 +31,7 @@ AutoControl 一直錄製豐富的動作日誌卻從未分析;``mine_action_log`` ---------- ``AC_mine_actions`` 接受 ``actions``(清單,或視覺化建構器傳入的 JSON 字串清單)以及 -``min_len`` / ``max_len`` / ``min_count``,並回傳 ``{total_actions, patterns, +``min_len`` / ``max_len`` / ``min_count``(``min_len`` 與 ``min_count`` 至少為 1,``max_len`` 不小於 +``min_len``,否則丟出 ``ValueError``),並回傳 ``{total_actions, patterns, candidates}``。相同操作亦提供為 MCP 工具 ``ac_mine_actions``,以及 Script Builder 中 **Report** 分類下的指令。 diff --git a/docs/source/Zh/doc/new_features/v48_features_doc.rst b/docs/source/Zh/doc/new_features/v48_features_doc.rst index 2f7720d24..85b5bec6f 100644 --- a/docs/source/Zh/doc/new_features/v48_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v48_features_doc.rst @@ -32,7 +32,8 @@ JSON 後端(或記憶體內);純標準函式庫;不匯入 ``PySide6``。 型別為 ``text`` / ``int`` / ``bool`` / ``credential``;``get`` 會轉成宣告型別,並在未停用 時退回 ``default`` 環境。``active_environment()`` 讀取 ``JE_AUTOCONTROL_ENV``。``list`` / ``delete`` 補齊整個儲存體。``set`` 在寫入時就拒絕未知型別,以及該型別讀不懂的值(例如把 -``"eighty"`` 當 ``int``)。 +``"eighty"`` 當 ``int``)。``int`` 只接受整數(不接受 ``3.7`` 或 ``True``),``bool`` 字串必須是 true / false / yes / +no / on / off / 1 / 0 之一。 執行器指令 ---------- diff --git a/docs/source/Zh/doc/new_features/v4_features_doc.rst b/docs/source/Zh/doc/new_features/v4_features_doc.rst index 98e89d2df..2a010e192 100644 --- a/docs/source/Zh/doc/new_features/v4_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v4_features_doc.rst @@ -64,7 +64,7 @@ Builder 項目。視覺與視窗功能的 geometry / IO 操作皆可注入,因 ``AC_assert_duration`` 在區塊耗時超過預算時判失敗——銜接 profiler 與 斷言 DSL 的延遲回歸守門。 * **讀進變數** — 把外部資料綁進流程範圍供後續 ``${var}`` 使用: - ``AC_ocr_to_var``(區域文字)、``AC_shell_to_var``(命令 stdout,以 ``encoding`` 解碼,預設為系統地區設定的編碼; + ``AC_ocr_to_var``(區域文字)、``AC_shell_to_var``(命令 stdout,以 ``encoding`` 解碼,預設為系統地區設定的字碼頁,Python 3.15 的 UTF-8 模式下也是; 逾時會結束該命令及它啟動的所有程序,``.bat`` / ``.cmd`` 的參數含 cmd 語法時會拒絕)、 ``AC_read_file_to_var``(檔案文字;除非以 ``encoding`` 指定,否則讀 UTF-8,有無 BOM 皆可)、``AC_http_to_var``(GET body 或 dotted JSON path)、``AC_now_to_var``(strftime)、``AC_random_to_var`` diff --git a/docs/source/Zh/doc/new_features/v51_features_doc.rst b/docs/source/Zh/doc/new_features/v51_features_doc.rst index 709aea964..caed69521 100644 --- a/docs/source/Zh/doc/new_features/v51_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v51_features_doc.rst @@ -18,7 +18,9 @@ JSONPath 查詢 過濾條件的欄位可以是巢狀的(``@.a.b``),``[?(@.k)]`` 保留有 ``k`` 的元素;對物件套用過濾時,挑的是它的成員值。 比較的值是 JSON 數字、加引號的字串、``true``、``false`` 或 ``null``。 不同型別的值一律不相等(``true != 1``);``<`` / ``>`` 只比較兩個數字或兩個字串(``<=`` 是 ``<`` 或 ``==``, -所以 ``null <= null`` 成立)。加引號的名稱和字串會解碼 RFC 9535 的跳脫序列(``['a\'b']`` 是鍵 ``a'b``)。 +所以 ``null <= null`` 成立)。加引號的名稱和字串會解碼 RFC 9535 的跳脫序列(``['a\'b']`` 是鍵 ``a'b``, +``["\uD83D\uDE00"]`` 是它拼出的那一個字元的鍵);落單的代理字元、未跳脫的控制字元,或跳脫了另一種引號,都會拋錯。 +索引是 RFC 9535 的整數:``[01]``、``[-0]`` 與超過 2**53-1 的索引會拋錯,``..`` 後面必須接名稱、``*`` 或 ``[...]``。 這個子集讀不懂的路徑(不支援的過濾條件或值、切片 ``[0:2]``、 聯集 ``[0,1]``、``['a','b']``、空的 ``[]``、沒有收尾的 ``[``、多餘的字元)會拋 ``ValueError``,不會改成比對到別的東西。 diff --git a/docs/source/Zh/doc/new_features/v54_features_doc.rst b/docs/source/Zh/doc/new_features/v54_features_doc.rst index 54e79fe0f..1c4fc1fdc 100644 --- a/docs/source/Zh/doc/new_features/v54_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v54_features_doc.rst @@ -47,5 +47,6 @@ JSON 後端(透過共用 ``json_store`` 助手);純標準函式庫;信心與門 ``AC_repair_approve`` 核准一個待審建議。 ================================ =================================================== -相同操作亦提供為 MCP 工具(``ac_repair_*``),以及 Script Builder 中 **Tools** 分類下的指 -令。 +每個指令都可選填 ``db``(JSON 路徑);沒給時,同一個行程內的指令共用一個記憶體中的儲存區(``repair_store()``), +所以 ``AC_repair_record`` 記下的建議其他指令找得到。相同操作亦提供為 MCP 工具(``ac_repair_*``),以及 Script Builder 中 +**Tools** 分類下的指令。 diff --git a/docs/source/Zh/doc/new_features/v57_features_doc.rst b/docs/source/Zh/doc/new_features/v57_features_doc.rst index 3802ae388..d04445726 100644 --- a/docs/source/Zh/doc/new_features/v57_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v57_features_doc.rst @@ -18,7 +18,13 @@ JSON Schema 驗證 ``enum`` / ``const``(讓 ``True`` 與 ``1`` 保持相異,巢狀內也一樣)。 * 數字 —— ``minimum`` / ``maximum`` / ``exclusiveMinimum`` / ``exclusiveMaximum`` / ``multipleOf``。 -* 字串 —— ``minLength`` / ``maxLength`` / ``pattern``。 +* 字串 —— ``minLength`` / ``maxLength`` / ``pattern``。``pattern`` 與 + ``patternProperties`` 照規格用 ECMA-262 的語意比對:``$`` 不會在結尾換行之前成立; + ``\d``、``\w``、``\b`` 只認 ASCII,所以 ``^\d+$`` 不接受阿拉伯-印度數字;``.`` 遇到 + 任何行終止字元都停;``\s`` 是 ECMA-262 的空白。``\cX``、``\u{...}``、``[]`` / ``[^]``、 + ``(?...)`` / ``\k``,以及 General_Category 值(``L``、``Letter``、``Nd``、 + ``digit``、``gc=Lu``……)與 ``Any``、``ASCII``、``Assigned`` 的 ``\p{...}`` / + ``\P{...}`` 都看得懂;文字系統(script)屬性會丟出 ``AutoControlJsonException``。 * 陣列 —— ``minItems`` / ``maxItems`` / ``uniqueItems`` / ``items`` / ``prefixItems`` / ``contains``。 * 物件 —— ``required`` / ``minProperties`` / ``maxProperties`` / ``properties`` / diff --git a/docs/source/Zh/doc/new_features/v61_features_doc.rst b/docs/source/Zh/doc/new_features/v61_features_doc.rst index ba2ff05df..a9d1ee3d4 100644 --- a/docs/source/Zh/doc/new_features/v61_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v61_features_doc.rst @@ -29,10 +29,10 @@ bearer JWT。本功能補上一個聚焦、純標準函式庫的 JWT 編解碼 token = encode_jwt({"sub": "user1", "aud": "api", "exp": 1893456000}, secret) # -> "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...." - # 預設政策:僅 HS256、驗證 exp/nbf、不檢查 audience/issuer - claims = decode_jwt(token, secret) + # token 帶有 "aud",所以政策必須指名要接受的 audience + claims = decode_jwt(token, secret, ClaimsPolicy(audience="api")) - # 以 ClaimsPolicy 收緊 audience / issuer / leeway / algorithms + # 再以 ClaimsPolicy 收緊 issuer / leeway / algorithms policy = ClaimsPolicy(algorithms=("HS256",), audience="api", issuer="my-service", leeway=30) claims = decode_jwt(token, secret, policy) @@ -40,7 +40,10 @@ bearer JWT。本功能補上一個聚焦、純標準函式庫的 JWT 編解碼 ``encode_jwt`` 以 ``HS256`` / ``HS384`` / ``HS512`` 簽出精簡的 ``header.payload.signature`` token。``decode_jwt`` 先驗證簽章,再以一份 :class:`ClaimsPolicy` (含 ``leeway`` 的 ``exp`` / ``nbf``、``aud`` 成員資格、``iss`` 比對)使用可注入的 ``now`` 驗證 -標準宣告;失敗時拋出 ``ExpiredTokenError`` / ``InvalidSignatureError`` / ``JwtError``。簽出的 +標準宣告;失敗時拋出 ``ExpiredTokenError`` / ``InvalidSignatureError`` / ``JwtError``。預設政策只接受 +HS256 並檢查 ``exp`` / ``nbf``;帶有 ``aud`` 的 token,除非 ``ClaimsPolicy.audience`` 指名其中一個值,否則一律拒絕 +(RFC 7519 4.1.3),所以發給某個服務的 token 不會在共用同一把金鑰的另一個服務上驗證通過。空的或非字串的金鑰, +以及不是 JSON 的宣告或標頭(``datetime``、``NaN``),都會拋出 ``JwtError``。簽出的 token 可直接接上 HTTP 用戶端: .. code-block:: python diff --git a/docs/source/Zh/doc/new_features/v62_features_doc.rst b/docs/source/Zh/doc/new_features/v62_features_doc.rst index 8c3d9ced5..497962a76 100644 --- a/docs/source/Zh/doc/new_features/v62_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v62_features_doc.rst @@ -43,6 +43,7 @@ ---------- ``AC_rate_limit`` 接受限制器 ``name`` 以及 ``rate`` / ``capacity`` / ``n``,嘗試從該具名 token -bucket(首次使用時建立)取用 ``n`` 個 token,回傳 ``{acquired, tokens, wait}``,讓流程可閘控或 +bucket(首次使用時建立,呼叫給出不同的 ``rate`` 或 ``capacity`` 時重建;executor 與 MCP 共用同一組 bucket, +即 ``named_bucket``)取用 ``n`` 個 token,回傳 ``{acquired, tokens, wait}``,讓流程可閘控或 延後某個動作。同一操作亦以 MCP 工具 ``ac_rate_limit`` 以及 Script Builder 中 **Flow** 分類下的 命令提供。 diff --git a/docs/source/Zh/doc/new_features/v64_features_doc.rst b/docs/source/Zh/doc/new_features/v64_features_doc.rst index f5c2e2fd8..b484c8d56 100644 --- a/docs/source/Zh/doc/new_features/v64_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v64_features_doc.rst @@ -29,10 +29,12 @@ ``SearchIndex.add`` / ``remove`` 以增量方式維護索引;``build`` 索引一個 ``{doc_id: text}`` 對映 (或 ``(id, text)`` 配對)。``search`` 回傳排名後的 ``SearchHit(doc_id, score)`` 結果 —— 預設 -為 BM25(``k1=1.5``、``b=0.75``),或 ``mode="tfidf"``。評分採標準 Okapi 公式, +為 BM25(``k1=1.5``、``b=0.75``),或 ``mode="tfidf"``(對數化詞頻乘上 ``ln(N / df) + 1``, +所以出現在每份文件的詞仍會計分)。BM25 評分採標準 Okapi 公式, ``IDF = ln(1 + (N − df + 0.5) / (df + 0.5))``,因此罕見詞勝過常見詞、詞頻會飽和(``k1``)、長 文件被正規化下調(``b``)。可提供 ``stop_words`` 集合以濾除雜訊詞。詞是任何文字系統中連續的字母與數字(做 casefold),所以 -``登入``、``café`` 會完整索引;``stop_words`` 也以相同方式轉換。結果具決定性(平手以 +``café`` 會完整索引;不以空格分詞的假名、CJK 漢字與諺文連續字串則以相鄰兩字(bigram)索引, +因此能在 ``請先登入系統`` 中找到 ``登入``。``stop_words`` 也以相同方式轉換。結果具決定性(平手以 ``doc_id`` 決定)。 執行器命令 diff --git a/docs/source/Zh/doc/new_features/v66_features_doc.rst b/docs/source/Zh/doc/new_features/v66_features_doc.rst index 87475a22e..646c1d323 100644 --- a/docs/source/Zh/doc/new_features/v66_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v66_features_doc.rst @@ -8,7 +8,9 @@ 支援的規則部分:``FREQ``(DAILY/WEEKLY/MONTHLY/YEARLY)、``INTERVAL``、``COUNT``、``UNTIL``、 ``BYDAY``(含序數如 ``2MO`` / ``-1FR``)、``BYMONTHDAY``(含負數)、``BYMONTH``、``BYSETPOS`` 與 ``WKST``。時間層級部分以及 BYWEEKNO/BYYEARDAY 不在範圍內:``parse_rrule`` 遇到它們、 -或同時給了 ``COUNT`` 與 ``UNTIL`` 時拋出 ``AutoControlException``。純標準函式庫(``datetime`` + +同時給了 ``COUNT`` 與 ``UNTIL``,或出現 RFC 5545 禁止的組合(``WEEKLY`` 搭配 ``BYMONTHDAY``、``DAILY`` / ``WEEKLY`` +搭配有序數的 ``BYDAY``、未知的 ``WKST``)時拋出 ``AutoControlException``。沒有 ``BYMONTH`` 時,``YEARLY`` 的 ``BYDAY`` +序數以整年計算。純標準函式庫(``datetime`` + ``calendar``);時鐘可注入,因此 ``next_occurrence`` 具決定性。不匯入 ``PySide6``。 無頭 API @@ -33,7 +35,9 @@ ``parse_rrule`` 接受帶或不帶 ``RRULE:`` 前綴的規則,回傳凍結的 ``Recurrence``。``occurrences`` 產生以 ``dtstart`` 為錨點的 datetime(其時刻與時區會套用到每一次發生),受 ``COUNT`` / ``UNTIL`` -(或 ``count=`` / ``until=`` 覆寫)及安全上限約束。僅含日期的 ``UNTIL`` 會包含整天。 +約束;``count=`` / ``until=`` 參數再進一步收窄(取較小者),沒有次數限制的規則由 ``max_iter`` 設上限。序列在 +9999-12-31 結束,或在連續 400 年(間隔較長時等比放大)沒有任何發生時結束,所以永遠不會成立的規則也會停止。 +僅含日期的 ``UNTIL`` 會包含整天。 ``next_occurrence`` 回傳在 ``now`` 當下或之後的第一次發生。 執行器命令 diff --git a/docs/source/Zh/doc/new_features/v76_features_doc.rst b/docs/source/Zh/doc/new_features/v76_features_doc.rst index 82ed4a31d..9c082f2c9 100644 --- a/docs/source/Zh/doc/new_features/v76_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v76_features_doc.rst @@ -29,9 +29,13 @@ W3C Trace Context 傳播 span = child_context(parent) # 相同 trace_id,新的 span_id ``SpanContext`` 是不可變的(``trace_id``、``span_id``、``trace_flags``、``tracestate``)組合。 -``new_root_context`` 鑄造新 trace;``child_context`` 保留 trace id 與繼承狀態但配置新的 span id。 +``new_root_context`` 鑄造新 trace;``child_context`` 保留 trace id 與繼承狀態但配置新的 span id,並清掉 sampled 與 random +以外的旗標位元。 ``parse_traceparent`` / ``format_traceparent`` 來回轉換 version-``00`` 標頭(較新的版本當作 ``00`` 讀取、忽略多出的欄位; -版本 ``ff``、格式不符或全零 ID 拋出 ``TraceContextError``);``parse_tracestate`` / ``format_tracestate`` 處理 vendor 清單。 +版本 ``ff``、格式不符或全零 ID 拋出 ``TraceContextError``,``format_traceparent`` 寫出前也會驗證手工建立的 context); +``parse_tracestate`` / ``format_tracestate`` 處理 vendor 清單。解析時只去掉逗號兩側的空格與 tab、保留值開頭的空格,捨棄鍵或值 +不合文法的成員(空值、含 ``=`` 或 ``,``、超過 256 字元、控制字元),讀滿 32 個成員就停,鍵重複時回傳 ``[]``;格式化遇到這種成員會拋出 +``TraceContextError``,不會寫出去。 ``inject_context`` 寫入標頭;``extract_context`` 將其讀回(不分大小寫),``traceparent`` 缺少或無效時回傳 ``None``,讓接收端依 W3C Trace Context 開一條新的 trace。 diff --git a/docs/source/Zh/doc/new_features/v77_features_doc.rst b/docs/source/Zh/doc/new_features/v77_features_doc.rst index a7be93e61..c8dac6c35 100644 --- a/docs/source/Zh/doc/new_features/v77_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v77_features_doc.rst @@ -24,9 +24,9 @@ dict),因此在 CI 中完全具決定性。 report = validate_rows(rows, schema) ``profile_rows`` 回傳 ``{row_count, columns}``,每欄帶有其筆數、空值數與比例、相異值數、唯一性旗標、 -推斷型別(``int`` / ``number`` / ``bool`` / ``str``)、最常見值與其次數,以及數值欄的 ``min`` / -``max`` / ``mean``。``infer_schema`` 把該剖析轉成既有 ``validate_rows`` 能理解的結構:無空值的欄位 -標為 ``required``,每個非空值皆相異則標為 ``unique``,並帶上數值邊界。傳入明確的 ``columns`` 清單可將 +推斷型別(``int`` / ``number`` / ``bool`` / ``str``,沒有單一型別時為 ``mixed``)、最常見值與其次數 +(``True`` 與 ``1`` 分開計),以及數值欄的 ``min`` / ``max`` / ``mean``(``int`` 欄為精確值,其他以有限值計算)。``infer_schema`` 把該剖析轉成既有 ``validate_rows`` 能理解的結構:無空值的欄位 +標為 ``required``,每個非空值皆相異則標為 ``unique``,並帶上數值邊界;``mixed`` 欄不加 ``type`` 規則。傳入明確的 ``columns`` 清單可將 兩個函式限制在子集。 執行器命令 diff --git a/docs/source/Zh/doc/new_features/v78_features_doc.rst b/docs/source/Zh/doc/new_features/v78_features_doc.rst index bfde1432f..04ed00dbf 100644 --- a/docs/source/Zh/doc/new_features/v78_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v78_features_doc.rst @@ -24,7 +24,7 @@ RFC 9457 Problem Details 解析 # 或把 problem 回應轉成例外: raise_for_problem(response) # 拋出 HttpProblemError -``is_problem`` 檢查 ``Content-Type``(不分大小寫)。``parse_problem`` 回傳 ``ProblemDetails`` +``is_problem`` 檢查 ``Content-Type`` 的媒體類型是否為 ``application/problem+json``(不分大小寫,忽略參數)。``parse_problem`` 回傳 ``ProblemDetails`` (``type`` 預設 ``about:blank``,可轉換時 ``status`` 為整數,所有非註冊鍵收進 ``extensions``), 回應非 problem 文件時回傳 ``None``;當 ``json`` 缺席時會回退去解析 ``text``。``ProblemDetails.summary`` 給出一行描述,``to_dict`` 把文件攤平並併回擴充欄位。``raise_for_problem`` 對 problem 回應拋出 diff --git a/docs/source/Zh/doc/new_features/v79_features_doc.rst b/docs/source/Zh/doc/new_features/v79_features_doc.rst index c90eb9d86..715f478c0 100644 --- a/docs/source/Zh/doc/new_features/v79_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v79_features_doc.rst @@ -24,9 +24,10 @@ Dotenv(.env)解析 ``parse_dotenv`` 略過空白與 ``#`` 註解行,去除選用的 ``export`` 前綴,驗證鍵,並解析值:單引號值為 字面值(僅 ``\'`` 與 ``\\`` 會轉義,與 python-dotenv 相同),雙引號值處理 ``\n`` / ``\t`` / ``\\`` / ``\"`` 轉義,未加引號的值會去除結尾 `` #`` 註解與 -前後空白。加引號的值到收尾的引號為止,後面的註解會略過,值也可以跨多行。``dotenv_values`` 讀取並解析檔案;``load_dotenv`` 把檔案合併進明確的 ``env`` mapping +前後空白;``#`` 只有在空白之後才算註解開頭,所以 ``COLOR=#ff0000`` 保留它的值,``KEY= # note`` 則是空值。加引號的值到收尾的引號為止, +後面的註解會略過,值也可以跨多行,並保留每一行結尾的空白。開頭的 BOM 會略過。``dotenv_values`` 讀取並解析檔案;``load_dotenv`` 把檔案合併進明確的 ``env`` mapping (預設保留既有鍵,除非 ``override``);``dump_dotenv`` 把 mapping 序列化回 ``.env`` 文字,並為需要的值 -加上引號。 +加上引號;遇到解析器讀不回來的鍵(例如含換行或 ``=``)會拋出 ``DotenvError``(同時是 ``AutoControlException`` 與 ``ValueError``)。 執行器命令 ---------- diff --git a/docs/source/Zh/doc/new_features/v81_features_doc.rst b/docs/source/Zh/doc/new_features/v81_features_doc.rst index d60789424..e03ed8239 100644 --- a/docs/source/Zh/doc/new_features/v81_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v81_features_doc.rst @@ -27,7 +27,7 @@ ``add_layer`` 註冊一個具名層;``priority`` 越高越勝出(預設為插入順序,因此後加的層覆蓋先前的)。 ``resolve`` 依優先序由低到高深度合併每一層 —— 巢狀 dict 遞迴合併,而純量與 list 直接取代。``get`` 以 點分鍵從解析後設定讀取並帶預設值;``explain`` 回傳 ``SourceTrace``,標明點分鍵的勝出層(不存在時拋 -``KeyError``)。``deep_merge`` 另以獨立的雙 mapping 輔助函式提供。 +``KeyError``)。``deep_merge`` 另以獨立的雙 mapping 輔助函式提供,結果不與任一輸入共用巢狀值。 執行器命令 ---------- diff --git a/docs/source/Zh/doc/new_features/v82_features_doc.rst b/docs/source/Zh/doc/new_features/v82_features_doc.rst index 9f06841b9..4d86b55da 100644 --- a/docs/source/Zh/doc/new_features/v82_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v82_features_doc.rst @@ -25,7 +25,8 @@ Kolmogorov-Smirnov 雙樣本檢定來做經典的「今天的資料形狀是否 ``psi`` 以 ``reference`` 的分位邊界將 ``current`` 分箱,並加總每箱的 log-ratio 貢獻(分布相同為 0, 分歧越大值越大)。``ks_two_sample`` 回傳最大經驗 CDF 差距與 Kolmogorov 分布的 p 值。``categorical_drift`` 以卡方統計量與 total-variation 距離比較類別頻率。``detect_drift`` 把數值路徑包成一份報告,並以 ``threshold`` -(預設 ``0.25``)給出 ``drifted`` 判定。 +(預設 ``0.25``)給出 ``drifted`` 判定。任一數值樣本含 NaN 時丟出指明索引的 ``ValueError``: +請先移除或補值。 執行器命令 ---------- diff --git a/docs/source/Zh/doc/new_features/v83_features_doc.rst b/docs/source/Zh/doc/new_features/v83_features_doc.rst index eed721466..3f57eae55 100644 --- a/docs/source/Zh/doc/new_features/v83_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v83_features_doc.rst @@ -26,7 +26,7 @@ ``diff_rows`` 為兩個資料列集合建立鍵索引,回傳 ``{added, removed, changed, unchanged}``:``added`` / ``removed`` / ``unchanged`` 是資料列清單,而 ``changed`` 收錄 ``{key, old, new}``(單欄鍵為純量,複合鍵 -為 list)。鍵重複時以最後一列為準。``cell_changes`` 把變更的列展開成 ``{key, column, old, new}`` 記錄。 +為 list)。鍵重複時以最後一列為準;鍵為空或某列缺少鍵欄位時丟出 ``ValueError``。NaN 儲存格視為等於 NaN。``cell_changes`` 把變更的列展開成 ``{key, column, old, new}`` 記錄。 ``summarize_diff`` 統計每個分類的數量。 執行器命令 diff --git a/docs/source/Zh/doc/new_features/v84_features_doc.rst b/docs/source/Zh/doc/new_features/v84_features_doc.rst index ec5c5b9d6..2baf32bf7 100644 --- a/docs/source/Zh/doc/new_features/v84_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v84_features_doc.rst @@ -24,7 +24,7 @@ W3C Baggage 傳播 ``Baggage`` 包裝一個不可變的鍵值對應:``get`` 讀取,``set`` / ``remove`` 回傳新實例,``to_dict`` 匯出 條目。``parse_baggage`` 解析標頭(去除選用的 ``;metadata`` 並拒絕空鍵),``format_baggage`` 將鍵與值 -percent-encode 回標頭值,``inject_baggage`` / ``extract_baggage`` 在請求 dict 上寫入與讀取 ``baggage`` +percent-encode 回標頭值(``Baggage`` 以 ``ValueError`` 拒絕空鍵,因為沒有解析器讀得回來),``inject_baggage`` / ``extract_baggage`` 在請求 dict 上寫入與讀取 ``baggage`` 標頭(讀取不分大小寫)。與 ``trace_context`` 自然搭配,在 trace 之外攜帶脈絡。 執行器命令 diff --git a/docs/source/Zh/doc/new_features/v85_features_doc.rst b/docs/source/Zh/doc/new_features/v85_features_doc.rst index 9a434dee7..c9c547c6a 100644 --- a/docs/source/Zh/doc/new_features/v85_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v85_features_doc.rst @@ -34,4 +34,6 @@ governance 憑證 broker。``resolve_refs_in`` 走訪巢狀 dict/list 並就地 ``AC_resolve_ref`` 把單一 ``ref`` 解析成 ``{value}``;``AC_resolve_refs`` 解析 ``obj`` 內每個參照並回傳 ``{resolved}``。兩者皆以 MCP 工具(``ac_resolve_ref`` / ``ac_resolve_refs``)以及 Script Builder 中 -**Security** 分類下的命令提供。 +**Security** 分類下的命令提供。它們的結果會被記錄(executor 紀錄、MCP 回應),所以遇到 ``secret://`` +會丟 ``SecretRefError``:祕密的值不進這些紀錄。需要值的步驟請引用 ``${secrets.NAME}``,或在 Python +裡呼叫 ``resolve_ref``。 diff --git a/docs/source/Zh/doc/new_features/v87_features_doc.rst b/docs/source/Zh/doc/new_features/v87_features_doc.rst index cc92ff1f0..f27a81e15 100644 --- a/docs/source/Zh/doc/new_features/v87_features_doc.rst +++ b/docs/source/Zh/doc/new_features/v87_features_doc.rst @@ -22,8 +22,10 @@ RFC 8288 Link 標頭與分頁 # 透過注入的 fetch(傳輸 / 卡帶)走訪每一頁: pages = paginate(start_url, fetch, max_pages=50) -``parse_link_header`` 回傳 ``Link`` 清單(``uri``、``rel`` 與所有 ``params``),容許含逗號的引號值與單一 -標頭中的多個連結。``links_by_rel`` 依每個(以空白分隔的)關係索引,``next_url`` 是 ``rel="next"`` 的便利 +``parse_link_header`` 回傳 ``Link`` 清單(``uri``、``rel`` 與所有 ``params``),以 RFC 8288 附錄 B 的演算法讀取標頭: +引號值保留其中的逗號、分號與跳脫字元,未加引號的值一直到下一個 ``;`` 或 ``,`` 為止(所以 ``title=x