13 KiB
13 KiB
Gateway Functional Contract
目標:把 Hermes gateway 在 identity 綁定、長任務交付、LINE quota fallback、email 檔案交付、shared drive 下載工作流 的可接受行為定義成明確契約,未來更新或重構時可直接逐條核對。
實際程式碼再確認
本文件依下列 live 模組與測試整理:
gateway/user_verification.pygateway/run.pygateway/platforms/base.pyplugins/platforms/line/adapter.pyplugins/platforms/email/adapter.pyhermes_cli/managed_downloads.pyhermes_cli/web_server.pytests/gateway/test_line_plugin.pytests/gateway/test_email.pytests/gateway/test_approval_prompt_redaction.pytests/gateway/test_display_config.pytests/hermes_cli/test_managed_downloads.pytests/hermes_cli/test_web_verification_admin.pytests/gateway/test_principal_profile_routing_phase1.pytests/gateway/test_verified_email_handoff_guard_helpers.py
這份文件只記錄目前已在程式或測試中有明確落點的行為,不把尚未實作的理想狀態假裝成既有功能。
1. Principal / verified email 綁定契約
1.1 authoritative source
- verified email 的 live 來源是
GatewayUserStore。 - 主要查詢入口:
GatewayUserStore.get_verified_email_for_source(source)。 - 系統不得只靠對話上下文、記憶摘要、舊 prompt、舊 session 欄位決定要寄到哪個 email。
1.2 handoff 前必做 live confirm
- 任何 系統主動 把結果寄到使用者綁定信箱的流程,在寄出前都必須重新確認:
- active source 的綁定 email
- 與即將寄送的 target email
- 必須完全一致
- 目前這條 hard guard 已有明確函式:
GatewayUserStore.bound_email_matches_source(...)- LINE 側經
LineAdapter._verified_email_target_if_bound(...)使用
1.3 例外規則
- 若是 使用者明確指定其他 email,可用
explicit_user_requested=True略過 bound-email equality check。 - 這個例外只適用使用者明講的目的地,不適用系統自行 fallback 或自動推論。
1.4 不可接受行為
- 把別的 principal 的 verified email 當成目前 source 的綁定信箱
- 用 stale session state / context compression 結果直接寄信
- 無 live confirm 就直接把 LINE / Telegram / OpenWebUI 的結果轉寄到 email
1.5 principal context 與 profile routing
- verified source 除了 email handoff guard,還會產生 principal context:
GatewayUserStore.get_principal_context(source)gateway/principal_profiles.py
- verified principal 若啟用
principal_profile_routing,應穩定解析到同一 principal profile。 - 不可因 update / restart 導致同一 principal 突然丟失 profile mapping,否則會讓該 principal 的後續權限、工具、長任務脈絡一起漂移。
2. LINE 長任務契約
2.1 pending / postback state machine
- LINE 慢回覆流程使用
RequestCache狀態機:PENDINGREADYDELIVEREDERROR
show_responsepostback 只在該 cache 路徑中取回結果。- 若任務還沒完成,
show_response必須回 pending 狀態,不得假裝 final 已送達。
2.2 pending button 的語意
- pending button 是「稍後取回答案」機制,不是 final delivery 本身。
- 若 final 結果已轉到 email surface,後續 postback replay 必須保留「已改寄 email」事實,不能回 generic delivered copy。
2.3 長任務 heartbeat
- gateway 允許 long-running heartbeat,但 LINE 預設應是 安靜、泛化、對客版。
tests/gateway/test_display_config.py已明確固定:tool_progress == offbusy_ack_detail == falselong_running_notifications == generic
- 不應把 raw tool names、iteration counter、工程英文直接丟給 LINE 使用者,除非明確 opt-in。
2.4 non_conversational 與 bypass
- system busy-acks / approval / update 類通知必須以
non_conversationalmetadata 保護,不可被誤收進最終交付內容。 - heartbeat 與 background/system notices 不得誤變成使用者的 final email。
2.5 不可接受行為
- heartbeat 冒充 final answer
查看答案重播時遺失 fallback surface 狀態- 長任務提示持續噴工程實作細節,讓使用者以為系統異常
3. LINE quota fallback → email 契約
3.1 fallback 觸發條件
- LINE push quota / push send 失敗時,應嘗試改走 verified email fallback。
- fallback payload 必須明確標記為:
email_fallback_pending- 或
email_fallback
3.2 pending 與 final 要分開
- pending 階段:
- 可寄一次「仍在處理中,完成後會寄到驗證 email」通知
- 應避免重複寄一堆同樣 pending 通知
- final 階段:
- 應寄出完整內容
- 並明確告知已改由 email 交付
3.3 fallback ownership transfer
- 一旦 final answer 已由 email fallback 接手,後續 post-stream 附件交付也必須沿用同一 email surface。
- 不得又回頭走 LINE 原生檔案送出,造成:
- 使用者已收到 email,卻又看到 LINE 附件送失敗
- 或 delivery state 自相矛盾
3.4 replay contract
DELIVERED狀態若 payload 為email_fallback,replay / postback 必須重播 fallback notice,而不是 genericAlready replied類文字。
3.5 不可接受行為
- LINE quota 滿了卻靜默失敗
- fallback email 只寄文字,不帶原本該帶的檔案資訊或下載連結
- 使用者之後查詢結果時,看不到「其實已改寄 email」這個事實
4. Email 檔案交付契約
4.1 MIME attachment contract
- 若有附件且未超限:
- email 應為 multipart
- 要附真正 MIME attachment
- 檔名要保留副檔名
Content-Disposition/Content-Typefilename 參數要正確
tests/gateway/test_email.py已固定這個契約
4.2 HTML + plain text contract
- email 不只 plain text,也要帶 HTML body
- 裸 URL 在 HTML 中應轉成可點擊的
<a href=...>
4.3 large attachment contract
- 若附件總大小超過 email 限制:
- 不應狂寄多封
- 應改成單封通知 + 下載連結
- payload 應保留:
attachments_omitted_reason = size_limitomitted_filenamesdownload_links
4.4 download link contract
- 對客下載連結應優先使用 managed downloads 正式網域
- 目前由:
hermes_cli/managed_downloads.pyhermes_cli/web_server.py的/downloads/{token}
- token 必須可 resolve,且 route 必須真的能回檔案
tests/hermes_cli/test_managed_downloads.py已固定:- URL shape
- route 可下載
Content-Disposition帶原始副檔名
4.5 不可接受行為
- 說有附件,實際沒有 MIME attachment
- 附件有帶出但沒有副檔名
- 下載連結指到錯誤網域或重啟後失效 token
- 附件太大寄失敗後,沒有自動切成 link-only
5. Approval / 對客通知契約
5.1 approval 文案
- approval fallback 文案應為對客繁中,不應把 raw command / security scan 原文直接外露。
- 需要保留「可批准 / 拒絕」操作語意,但不洩漏敏感內容。
5.2 approval redaction seam
_redact_approval_command(...)是 approval prompt 的硬性 secret-egress boundary。- chat platform path 與 SSE/API path 都必須在送出前實際使用 redacted command。
tests/gateway/test_approval_prompt_redaction.py已釘住這條契約。
6. Shared drive workflow 契約
6.1 查找與選檔契約
- shared drive 對話式工作流目前落在:
~/.hermes/scripts/shared_drive_index_query.py~/.hermes/scripts/shared_drive_lookup.py~/.hermes/scripts/shared_drive_chat_workflow.py
search必須回傳可對話使用的候選清單,且每個候選都帶file_id。- 若命中資料夾,
children <folder_file_id>必須能展開子檔案清單,供使用者最多選 10 檔。
6.2 打包與敏感檔契約
bundle --file-ids ... --requester <email>必須:- 以
file_id解析實際檔案 - 先複製到本機 staging
- 產生 24h managed download link
- 以
- 若命中個資 / 財務或無法可靠判讀內容,必須自動改走加密 zip,並保留第二通道密碼通知資訊。
6.3 驗證契約
- bundle 結果必須帶
verification欄位。 - 成功判準以 真實 GET 為準,不以 HEAD 為準。
- 不可把
urllib/ Cloudflare1010這類 bot-like 驗證誤判直接當成下載路徑壞掉。
6.4 不可接受行為
- search 找得到候選,但沒有
file_id,導致無法進入選檔/打包流程 - children 展開不到資料夾內容,卻沒說明是 index 或 parent mapping 問題
- 敏感檔仍走未加密原檔
- 連結其實可用,卻因 HEAD/urllib 驗證誤判為壞掉
7. 額度與權限治理契約
6.1 governance store 是 live source of truth
- principal、external identity、quota policy、model policy、dashboard role、admin audit log 的 authoritative store 都在
GatewayUserStore/gateway/user_verification.py。 - schema 至少包含:
principalsexternal_identitiesidentity_enforcementquota_policiesmodel_policiesdashboard_rolesprincipal_usage_dailyadmin_audit_logs
6.2 dashboard role contract
- dashboard verification/admin 角色目前分三級:
vieweroperatoradmin
- 權限邏輯由
hermes_cli/web_server.py::_require_verification_role(...)控制。 - 在 gated auth 模式下:
viewer:可讀 identities / principals / audit / quota / model / usageoperator:可 force-bind、unbind、block、unblock、resend verification、寫 quota/model policyadmin:可管理 dashboard role 本身
- 在 loopback / 非 auth_required 模式下,server 目前視為
admin;這是便利行為,不是未來所有部署都該仰賴的安全模型。
6.3 quota policy contract
- quota policy 目前已落地的資料欄位是:
daily_message_limitdaily_token_limitnotes
- principal usage 目前已落地的統計欄位是:
message_countinput_tokensoutput_tokenstotal_tokenslast_model
gateway/run.py在對話完成後會呼叫GatewayUserStore.record_usage_for_source(...)寫入 usage。
6.4 quota 的目前邊界
- 目前 repo 內已明確存在的是:
- quota policy CRUD
- usage record / usage list
- API / admin 測試 round-trip
- 目前這一版文件 不把尚未在 live code 看到的硬性 runtime deny / throttle 假裝成已完整存在。
- 換句話說:目前 governance 比較像「可觀測 + 可設定 + 可對帳」,不是已全面接管所有 gateway runtime 拒絕邏輯。
6.5 model policy contract
- 每個 principal 可有:
allowed_modelsdefault_model
- 這些資料目前已可經 admin API round-trip 保存與查詢。
- 若未來要主張「特定 principal 只能用指定模型」,必須再補 runtime enforcement 驗證;沒有測到前,不可把它寫成既成事實。
6.6 admin audit contract
- 任何人工治理動作至少應留下 audit:
- force bind
- unbind
- block / unblock
- role assignment
- quota / model policy 變更(若後續擴充,應一併補 audit)
- 目前已確認
force_bind_identity等身份治理動作可經/api/admin/verification/audit查回。
6.7 不可接受行為
- principal 還在,但 quota / model policy / role 因 update 被洗掉或查不到
- dashboard 可看 identities,卻看不到 usage / quota 對帳
- 系統把「有 quota policy 紀錄」誤當成「runtime 一定已 enforce」
7. 目前管理平台功能契約(current admin surface)
7.1 已確認存在的管理後台能力
依 hermes_cli/web_server.py 與 tests/hermes_cli/test_web_verification_admin.py,目前至少已有這些 admin API:
GET /api/admin/verification/roleGET/POST /api/admin/verification/rolesGET /api/admin/verification/identitiesGET /api/admin/verification/principalsPOST /api/admin/verification/force-bindPOST /api/admin/verification/unbindPOST /api/admin/verification/blockPOST /api/admin/verification/unblockPOST /api/admin/verification/resendGET /api/admin/verification/auditGET/POST /api/admin/quota-policiesGET/POST /api/admin/model-policiesGET /api/admin/principal-usage
7.2 這代表「目前管理平台」最少能做什麼
- 查誰綁了哪個平台 identity
- 查哪些 identities 被綁到同一 principal
- 人工 force-bind / unbind / block / unblock
- resend verification email
- 指派 dashboard role
- 設 quota / model policy
- 查每日 principal usage
- 查 admin audit log
7.3 目前不要過度聲稱的部分
- 我這次沒在 live code 裡看到一個已完成、明確對應上述所有治理 API 的前端 SPA 頁面證據。
- 所以目前比較穩的說法是:
- 治理後台後端能力已存在並有測試覆蓋
- 前端顯示/操作面是否完整暴露全部能力,仍需額外做 UI 對照驗證
- 這可以避免未來有人誤以為「API 有 = UI 一定完整可用」。
8. 這份契約如何維護
每次修改以下任一區塊,都要同步檢查本文件是否要更新:
gateway/user_verification.pygateway/run.pygateway/platforms/base.pyplugins/platforms/line/adapter.pyplugins/platforms/email/adapter.pyhermes_cli/managed_downloads.pyhermes_cli/web_server.py- 對應 tests
若改動造成契約變更,應同時更新:
- 本文件
gateway-state-and-sequence.mdgateway-regression-matrix.md- 對應 regression tests