---
title: "Agent 主控台權限能力點化 — 定版交付 (FR-190)"
brand: "Guidant AI · **FR-190** 定版交付"
eyebrow: "FR-190 · 定版交付"
h1: "定版交付：Agent 主控台權限能力點化"
subtitle: "已合回 feature/FR-172，未 push；套件 jedi-agent-protocol 0.6.1、jedi-remote-agent 1.18.3 已在 Nexus；出貨基線已含本案 migration；STG／POC 未上"
lede: "Agent 主控台的權限從 7 個大範圔改成 26 顆能力點：11 個主控台功能，每個功能依「檢視、設定、執行、下載」分開給。管理員在主系統角色設定頁「Agent」組的「Agent 主控台」子頁逐顆勾，或按「比照主系統功能帶入」一次帶入。使用者進主控台時主系統把他持有的顆寫進入場券，Agent 照票開頁面、擋 API；斷線用的離線操作者名單每次心跳依當下持有的顆重算。主系統、jedi-remote-agent、Agent 三邊都讀 `jedi-agent-protocol` 的同一份清單。"
chips: [
  {text: "四 repo 已合回 feature/FR-172", kind: ok},
  {text: "出貨基線已重產並乾淨裝驗", kind: ok},
  {text: "畫面未實機手測", kind: warn},
  {text: "STG／POC 未上", kind: warn},
]
footer: "FR-190 定版交付 · 設計見 design.md · 沿革見 handoff/2026-11-10-FR-190-SUMMARY.md 與 git log"
---

## 一句話 {#summary nav="一句話"}

Agent 主控台權限變成 26 顆主系統能力點 `agent.<功能>.<動作>`，角色頁逐顆給；簽票與離線名單照既有管道把持有的顆帶到 Agent；Agent 版本舊時丟掉不認得的顆、照常登入並提示；主系統派下來的掃描紀錄歸「掃描 · 檢視」。

---

## 1. 交付物 {#deliverables nav="交付物"}

本案動到四個程式 repo 與兩類文件，另外重產了出貨基線。契約（清單本身）在 jedi-agent-protocol；主系統這邊是資料遷移、端點守門與角色頁；Agent 這邊是驗票與路由守門；前端分主控台與主站兩塊。下表列每個 repo 交付了什麼、落在哪些檔。

| Repo | 內容 | 位置 |
|---|---|---|
| jedi-agent-protocol 0.6.1 | `console_scopes` 模組：26 顆清單（資源、動作、中英顯示名、離線可下放旗標、對應主系統功能）、`OFFLINE_GRANTABLE`（24 顆）、`MIRRORS`（15 條）、`scope_of()`／`capability_of()` 互轉、`CAPABILITY_MODULE='agent'`、`LICENSE_MODULE='remote-agent-manage'`、`HOME_ROUTE='remote-agent-console'`；錯誤碼 `TICKET_SCOPE_EMPTY`；三邊鏡像測試 | `jedi_agent_protocol/console_scopes.py`、`error_codes.py`、`tests/test_console_scopes.py`、`tests/test_mirror.py` |
| jedi-remote-agent 1.18.3 | `CAPABILITIES`（4 顆管理用＋26 顆主控台）、簽票範圍表、離線名單範圍表三張表由 `console_scopes` 推導；舊 7 顆退場，新顆 `legacy_name` 記舊名 | `jedi_remote_agent/plugin/contract.py` |
| 主系統 BE | 主線 migration：`ui_routes` 子頁 `remote-agent-console`、舊 7 顆字面持有者展開到新顆（含 pattern 角色與客戶自建角色）、刪舊顆與三張關聯表的列、角色範本「稽核員」預帶 `agent.scan.read`／`agent.scan.download`；`_ADMIN_CAPABILITIES` 全部換 `agent.*`，執行紀錄下載依紀錄種類細判；權限矩陣產生器把 protocol 清單內的顆視為「Agent 端有守」；唯讀端點 `GET /roles/<uid>/agent-console-mirror`；守衛測試「出貨 seed 的萬用字元 pattern 不得命中 `agent.*`」 | `scripts/sql/2026-11-10-fr190-agent-console-capabilities.sql`、`core/plugins/remote_agent.py`、`core/plugins/_agent_local_runs.py`、`scripts/deliverables/gen_permission_matrix.py`、`api/access/routes/agent_console_mirror_route.py`（19 行）、`app/access/service/agent_console_mirror_service.py`（24 行）、`test/test_fr190_seed_patterns_exclude_agent_module.py` |
| Agent | `CONSOLE_SCOPES` 改 import 契約；驗票把不認得的範圔丟掉並回數量，全部不認得才 403；`/api/me` 多回 `dropped_scopes`；73 條主控台路由守門一對一換成 26 顆短名；執行紀錄不分來源歸 `scan.read`；全 blueprint 單一 scope 對表守衛 | `guidant_agent/shared/constants.py`、`sync/domain/agent_auth.py`、`identity/app/session.py`、`console/api/*.py`、`test/test_fr190_scope_table.py` |
| 主系統 FE | 主控台：`router.js` 每頁 scope 與 13 個 view 的按鈕判斷換 26 顆短名、頂部舊版 Agent 橫幅；主站：角色樹新增 `agent` 模組 26 顆標籤與說明、動作順序加 `download`、「Agent 主控台」子頁列的「比照主系統功能帶入」鈕、離線操作者對話框權限標籤、Agent 管理頁按鈕換顆、權限常數檔重產 | `console/router.js`、`console/Shell.vue`、`src/config/locales/i18n/*/permission-tree.json`、`src/composables/usePermissionGrouping.js`、`src/components/access/PermissionMenuTree.vue`、`src/composables/useAgentConsoleMirror.js`、`src/config/capabilities.js` |
| 文件 | 頁面 SPEC 三頁、Agent 使用手冊權限節 | `docs/spec-site/current/evidence/agent-console.md` §3、`evidence/remote-agent-manage.md`、`system-admin/access-role-detail.md` §4.2；`docs/user-manual/agent/13-standalone-operation.md`「角色權限：26 項」 |
| 出貨基線 | 188 基線庫已套本案 migration；`02-schema.sql`、`04-seed-core.sql`、`99-stamp.sql` 重產；本機乾淨 `postgres:16` 完整 `init.sh` 裝驗通過 | `scripts/init/` |

---

## 2. 26 顆能力點 {#capabilities nav="26 顆"}

能力點全名 `agent.<功能>.<動作>`；入場券與 Agent 守門用去掉 `agent.` 的 `<功能>.<動作>`。模組 `agent` 同時是角色樹第一層的分組名。清單的單一來源是 `jedi_agent_protocol.console_scopes`，三邊都從那裡讀，不各自硬寫。

| 功能 | read（檢視） | update（設定） | execute（執行） | download（下載） | 離線可下放 |
|---|---|---|---|---|---|
| `files` 檔案 | 清單、預覽、上傳者清單 | | | 證據檔原檔 | 是 |
| `scan` 掃描 | 工具、目標、基準、紀錄（含主系統派工） | | 執行、取消、重送結果 | 報告原檔、工具 log | 是 |
| `netdiag` 網路除錯 | 目標、紀錄、預估、報告預覽 | | 執行、重送 | 除錯報告 | 是 |
| `logs` 日誌 | 查日誌 | | | 匯出 | 是 |
| `tools` 工具設定 | 看已設定（不回密碼） | 改帳密、測試連線、清除 | | | 是 |
| `ldap` LDAP | 看設定 | 改來源、改本機、測試、重新信任 | | | 是 |
| `keys` 金鑰 | 看狀態 | 換發 | | | 是 |
| `license` 授權 | 看狀態 | 上傳、線上啟用 | | | 是 |
| `ops` 維運 | 服務狀態、離線登入設定、儲存設定 | 改名稱、主系統位址、儲存、離線登入；補更新弱點庫／基準／目標 | 重啟 | 診斷包 | 是（含重啟） |
| `update` 更新 | 版本、檢查新版 | | 線上換版、離線上傳換版 | | **否** |
| `setup` 連線設定 | 註冊狀態 | 註冊、探測、換主系統 | | | 是 |

共 26 顆；離線操作者可下放 24 顆，只有 `update.read`、`update.execute` 不下放（換版不可逆，斷線時不該有人換版；重啟可逆、斷線卡住要能現場重啟，所以下放）。

**模組為什麼是 `agent` 而不是 `detection`**：出貨 seed 的系統角色帶萬用字元 pattern——稽核主管 `detection.*.*`，稽核員、一般成員、唯讀 `detection.*.read`。能力點若放在 `detection` 底下，新裝客戶的稽核主管升級後自動持有全部 26 顆（含檔案下載、重啟、換版），唯讀角色自動持有 11 顆檢視。`agent` 是新模組，沒有任何既有 pattern 命中。DEV 的角色沒有這些 pattern，在 DEV 驗不出來，所以守衛測試掃 seed 檔本身。

---

## 3. 權限怎麼到 Agent {#flow nav="流程"}

權限從角色設定頁到 Agent 主控台走兩條路：使用者在線時點「進入主控台」拿入場券（即時路），斷線時用離線操作者名單登入（離線路）。兩條路都讀同一份角色持有，本案沒有新增任何同步機制，只把寫進票與名單的範圍從 7 個換成 26 個。下圖是完整順序。

```{.mermaid cap="圖 1　權限從主系統角色到 Agent 主控台的兩條路。第 4～6 步是即時路（使用者點進主控台），第 9～11 步是離線路（每次心跳）；兩條路讀同一份角色持有，改角色不必重新授權。"}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','sequenceNumberColor':'#FFFFFF'}}}%%
sequenceDiagram
  autonumber
  actor M as 管理員
  participant H as 主系統 API
  actor U as 使用者（瀏覽器）
  participant A as Agent（:8444 主控台）
  M->>H: 角色頁「Agent 主控台」子頁勾顆，或按「比照主系統功能帶入」
  H->>H: GET /roles/{uid}/agent-console-mirror 依 MIRRORS 算出該加的顆（只算不寫）
  M->>H: 儲存角色
  U->>H: POST /remote-agents/{uid}/console-ticket
  H->>H: 逐顆查使用者持有的 26 顆，寫進票 scope；一顆都沒有回 403
  H-->>U: 五分鐘入場券（RS256，scope 原樣 26 個短名字串）
  U->>A: 帶票登入
  A->>A: 範圍過濾：認得的留下、不認得的丟掉並計數；全部不認得回 403 TICKET_SCOPE_EMPTY
  A-->>U: session；/api/me 回 scope 與 dropped_scopes，前端依 *.read 開分頁、依 update／execute／download 開按鈕
  A->>H: 心跳
  H->>H: 離線操作者名單：每人依當下持有的顆算可下放範圍（24 顆內）
  H-->>A: 整份名單；縮權的人踢掉已登入 session
```

第 5 步的「持有」由 jedi-iam 展開服務算，含萬用字元 pattern（`*.*.*` 的系統管理員自然持有 26 顆）。第 8 步丟掉不認得的範圍只會讓人少拿權限、不會多拿，所以主系統可以先升、Agent 慢慢跟；舊 Agent 收到新顆名會全部丟掉，等同登入失敗。反向不相容：新 Agent 不認舊的 7 個範圍名，舊主系統發的票在新 Agent 上全丟、回 403，升級順序必須主系統先。第 11 步的名單只決定「能不能離線登入這台」，範圔永遠跟著角色，主系統改角色下一次心跳就生效。

---

## 4. 規則 {#rules nav="規則"}

- **守門只收緊不放寬**。每條 Agent 路由與主系統端點換顆後，新顆持有者集合是舊顆持有者集合的子集；主系統執行紀錄的三個下載端點守 `scan.download`／`netdiag.download` 任一，route 內再依紀錄種類細判一次。
- **主系統端點對應**：進入主控台（簽票）與 Agent 清單可見→26 顆任一；重啟→`ops.execute`；換發憑證→`keys.update`；代設工具帳密→`tools.update`；補更新基準→`ops.update`。Agent 管理頁本身的新增、檢視、修改、刪除仍是 `detection.remote-agent.*` 四顆，不動。
- **執行紀錄不分來源**。本機手動與主系統派工的掃描紀錄都歸 `scan.read` 看、`scan.download` 帶走；網路除錯紀錄歸 `netdiag.*`。
- **「比照主系統功能帶入」**是一次性動作，不是連動。依 `MIRRORS` 15 條把對應顆勾起來，管理員可再取消，要按儲存才寫入。對照：檢測工具的檢視／修改→工具設定的檢視／設定；掃描設定檔檢視→掃描檢視、修改→掃描執行；LDAP 設定→LDAP；平台授權→授權；儲存設定→維運檢視／設定；產品更新→更新；Agent 管理的修改→金鑰設定＋維運執行；Agent 管理的新增→連線設定；主系統證據下載→檔案下載。日誌與網路除錯沒有對應的主系統功能，不會被帶入。
- **既有角色的升級**。migration 把字面持有舊 7 顆的角色（含客戶自建）展開到新顆：`configure`→工具、LDAP、金鑰、授權、維運、連線六組的檢視＋設定共 14 顆再加掃描檢視；`browse-files`→檔案檢視、日誌檢視；`run-local-scan`→掃描檢視＋執行、日誌檢視；`net-diag`→除錯檢視＋執行、日誌檢視；`download-files`→檔案、掃描、除錯、日誌四顆下載，`ops.download`（診斷包）只給同時持 `configure` 者；`restart`→`ops.execute`；`upgrade`→更新兩顆。萬用字元持有者不展開。展開後刪舊 7 顆與 `role_capabilities`、`route_capabilities`、`role_capability_patterns` 的對應列。
- **出貨預帶**：系統管理員靠 `*.*.*`；稽核員 `agent.scan.read`、`agent.scan.download`；檔案下載四顆與更新兩顆不預帶給任何非管理員角色。
- **票上寫法**：`scope` 是 26 個短名的字串陣列，原樣寫，約 2.5 KB；Agent 登入入口 body 上限 8 KB。
- **錯誤碼**：Agent 路由沒權限 403 `AGENT_CONSOLE_SCOPE_DENIED`；票上範圍全部不認得 403 `TICKET_SCOPE_EMPTY`；主系統一顆都沒有簽不到票 403 `AGENT_CONSOLE_NO_SCOPE`。
- **前端只決定看得到、點得進**，真正的牆是 Agent 端 `require_session(<短名>)` 與主系統 `_ADMIN_CAPABILITIES`；直接打 API 一樣被擋。

---

## 5. 驗證結果 {#verify nav="驗證結果"}

**自動測試**（四 repo 工作區，合回前；protocol 鏡像測試在合回後對主線再跑一次）：

| 範圍 | 結果 |
|---|---|
| jedi-agent-protocol | 96 條綠，含三邊鏡像（remote-agent 不再硬寫舊名、Agent `CONSOLE_SCOPES` 改 import、主系統 `_ADMIN_CAPABILITIES` 無舊名） |
| jedi-remote-agent | 606 條綠 |
| Agent | 全套 2542 條綠；`test_fr190_scope_table.py` 對 26 顆各建單一 scope session 逐支打全部主控台路由，放行集合等於對照表 |
| 主系統 BE | 守衛 `test_fr190_seed_patterns_exclude_agent_module`、`test_legacy_capability_name_references`、`test_module_boundaries` 綠；基線重產後 `test_plugin_capabilities_are_seeded`、`test_seed_capabilities_are_all_accounted_for` 綠 |
| 主系統 FE | 權限相關 5 檔 74 條綠；主控台與主站 `vite build` 過；改動檔無 `border-left` |

**DEV 資料庫**（migration 套用後）：`capabilities` 中 `module='agent'` 26 顆、舊 7 顆 0；`ui_routes` 有 `remote-agent-console`（`tree_page`、非選單）綁 26 顆；`role_capabilities`、`route_capabilities` 無孤兒；原持 `configure` 等舊顆的角色已展開（例如 IT 執行人員持 21 顆、系統管理員 26 顆）。

**主系統端點實打**（HEAD 起在 8123）：系統管理員簽票 200、票帶 26 個範圍；只持 `agent.scan.download`＋`detection.remote-agent.read` 的角色對掃描報告過守門、對除錯報告 403。

**出貨基線**：188 基線庫套本案 migration 後重產三份產物；本機乾淨 `postgres:16` 跑完整 `init.sh`（198 表、271 條 RLS policy、`cm_app` 缺權限 0），`agent` 顆 26、舊顆 0、子頁綁 26、基線 marker 在、稽核員預帶 2、非系統角色萬用字元命中 `agent.*` 為 0。

**未實機驗證**：Agent 的新票登入 200、舊票 403 是用 Flask 測試 client 打的，沒另起 Agent 程序；掃描報告下載沒拿到 200（DEV 的 Agent 離線，過守門後連不上）；角色頁子頁、帶入鈕、主控台側欄與橫幅沒有人在瀏覽器實機點過。

---

## 6. 這一輪沒做完的 {#todo nav="沒做完的"}

三件事還沒做，讀本頁時不要把它們當成已完成。

**畫面手測**。角色頁「Agent」組底下的子頁 26 顆與「比照主系統功能帶入」鈕、主控台側欄依權限顯示、舊版 Agent 的頂部橫幅，全部只有自動測試驗過，沒有人在瀏覽器實機點過。DEV 的 Agent 要重起到含本案的版本才看得到。由決策者接。

**push 與上版**。四個 repo 的 `feature/FR-172` 都有本案的 commit，尚未 push；STG 與 POC 都沒上。上版順序必須主系統先、Agent 後，因為新 Agent 不認舊的 7 個範圍名。由決策者接。

**主線本來就失敗的一條守衛**。`test_fr132_capability_home_route` 的 `test_dev_every_capability_has_a_home` 列出 13 顆 `proj.*` 專案角色能力點沒有 home_route，與本案無關，由 FR-194 處理。

---

## 7. 文件定位 {#docs nav="文件定位"}

本頁只寫交付了什麼與現在長什麼樣。為什麼這樣決定、過程中發生什麼、怎麼操作，分別在設計、歷史、手冊三類文件裡，照下表找。

| 你想知道 | 看哪份 |
|---|---|
| 為什麼拆 26 顆、為什麼模組是 `agent`、被排除了哪些做法 | [design.md](design.md) §3、[docs/analysis/2026-11-10-agent-console-capability-granularity.md](../../analysis/2026-11-10-agent-console-capability-granularity.md) |
| 討論過程、對照表怎麼來的 | [discussion.md](discussion.md) |
| 畫面長什麼樣（審稿用解釋版，非產品文案） | [prototype/index.html](prototype/index.html) |
| 使用者操作怎麼做 | 手冊 `docs/user-manual/agent/13-standalone-operation.md`「角色權限：26 項」 |
| 頁面行為規格 | SPEC `evidence/agent-console.md` §3、`evidence/remote-agent-manage.md`、`system-admin/access-role-detail.md` §4.2 |
| 每張卡做了什麼、commits、驗收結果 | [handoff/2026-11-10-FR-190-SUMMARY.md](handoff/2026-11-10-FR-190-SUMMARY.md)、[README.md](README.md) |
| 拆卡怎麼排批 | [handoff/cards.py](handoff/cards.py) |
