變更紀錄
這份手冊對準 teamsync-backend origin/master。
| 釘選 | 值 |
|---|---|
| Commit | 172a7f80bdf4dbdffdc018eb08bfa42c62bb485c — v5.21.0 版本提交(tag v5.21.0) |
| Subject | chore: bump version to 5.21.0 |
| Date | 2026-10-08 (Asia/Taipei) |
| 租戶 router | src/routers/private/modules/sandbox/ |
| 公開目錄 | src/routers/public/info/server.py(GET /public/info/sandbox/{regions,profiles})、src/routers/public/sandbox_artifacts.py(GET /public/sandbox/artifacts/{token}) |
| 共用 schema | src/schemas/sandbox.py、src/schemas/enums.py |
| 常數 | src/components/sandbox/constants.py、src/components/sandbox/storage.py、src/components/sandbox/input_schema.py、src/components/sandbox/secrets.py |
| Provider | src/components/sandbox/providers/(SANDBOX_PROVIDER 切換;onprem/ 的區域、計價 tier 與佇列上限)、地端 executor src/workers/sandbox_onprem/ |
| Operator root(僅供參考) | src/routers/root/sandbox.py |
| Agent 工具 | src/components/tools/custom/sandbox/ |
| Trigger | src/crud/custom_table_triggers.py(submit_sandbox_job) |
| Writeback | src/crud/sandbox/writeback.py |
| Command 輸出 | src/crud/sandbox/command_output.py;公開路由 POST /public/module/custom_tables/callback/command-output/{token_id} 在 src/routers/public/custom_tables_callback/server.py |
| 通知 | src/crud/sandbox/notifications.py |
| Runner/建置路徑 | sandbox/runner/、sandbox/build/(Go runner + Cloud Build compose/scan 路徑) |
| 控制面基礎設施 | infrastructure/sandbox/gcp/、infrastructure/app/gcp/k8s/base/sandbox-controller-*.yaml |
頁面與那棵樹不一致時,以後端為準。重新稽核就從這個 SHA diff 那些路徑。
2026-10-08 — release v5.21.0
**來源與上線:**後端 172a7f80bdf4dbdffdc018eb08bfa42c62bb485c 為 v5.21.0 版本提交(tag v5.21.0,chore: bump version to 5.21.0,2026-10-08 11:07 UTC = Asia/Taipei 19:07)。這是 v5.10.11 之後的第一支釘,所以本則檢視 70862ceae7..172a7f80bd 範圍內 1,848 個 commit 中的 111 個:碰到下方「如何更新這支釘」指令所列路徑的 71 個,加上碰到路徑含 sandbox 的檔案、或訊息提到 Sandbox 的另外 40 個。它們幾乎都屬於其他模組;有五個改變了 Sandbox 契約。下列內容都讀自已發布的樹。本則沒有呼叫任何已部署的 API,所以沒有可引用的 production info.version 讀回結果。日期用 Asia/Taipei。
任務輸出可以執行自訂表格 Command
output_policy 現在是以 kind 區分的聯集型別。除了表格 writeback(custom_table_writeback),部門擁有的任務版本現在可以宣告 { "kind": "custom_table_command", "command_id", "chatroom_id", "input_schema" }。該版本的每一次 run 會透過既有的 TEAMSYNC_CT_WRITEBACK_URL/TEAMSYNC_CT_WRITEBACK_TOKEN 變數收到一把在提交時鑄出的密封、限於單次 run 的憑證(若認領時無法揭露,run 會在沒有這兩個變數的情況下啟動),這兩個變數現在指向公開端點 POST /public/module/custom_tables/callback/command-output/{token_id}。work script 送出 { "inputs": { … } },平台就以 run 的提交者身分,在那一個聊天室裡,執行那一個 Command,每次 run 最多一次:第一份有效輸出勝出,端點會回 404/409 output_run_unavailable、409 output_command_stale、409 output_command_conflict、422 output_schema_violation、403 output_policy_author_denied,或 Commands API 自己的錯誤。
這個 policy 會在草稿建立/更新(422 output_policy_command_not_found、422 output_policy_schema_mismatch、403 output_policy_author_denied)、發布(任何一項 Command 檢查失敗都是 409 output_policy_unsatisfiable;對發布者與已記錄撰寫者的身分、grant 與範圍檢查仍是 403 output_policy_author_denied),以及每一次提交 run 時檢查。和表格 writeback 不同,不合格的 run——borrowed 的房間、政策指定房間以外的房間、API key/受限金鑰/社群 client 的提交者,或撰寫者或提交者沒有該 Command 的 grant——會被拒絕,回 403 output_policy_author_denied,什麼都不會排隊。自訂表格 trigger 現在可以對準這種版本(run 由 trigger 的撰寫者提交,任務的輸入檔是渲染後的 input 物件本身,而不是 source/trigger/record/row 信封)。requires_confirmation 為 false 時,Agent 直接提交,不經確認回合;表格 writeback 的版本仍需要兩回合確認。這些檢查用到的版本撰寫者歸屬是內部的,不會出現在任何回應。
**前端調整:**讓 owner 能撰寫新的種類並顯示撰寫時的錯誤碼;borrower 看到的 output_policy 仍是 null(現在裡面還多了 Command 與聊天室的 id),不要預期有值;把提交時的 403 output_policy_author_denied 當作授權問題,而不是資源不存在。觀察 run 的方式不變,Sandbox 的 run 紀錄不帶 Command 的結果。見任務輸出交給自訂表格 Command、任務受眾、自訂表格 trigger、Agent toolkit、Schema與錯誤。
runner 憑證跟著 timeout_seconds
認領時,runner 用來讀取 manifest、回報進度與完成、以及上傳結果的兩把 runtime 憑證,有效期限現在是已儲存的 run timeout_seconds 加上 120 秒收尾包絡(最多 604800 秒)。在這一版之前,有效期限只有平台預設的 600 秒,之後控制面就拒絕它們,所以更長的 run 無法回報結果,會被 reconcile 判成 platform_failed/provider_terminal_without_callback;這個 release 的地端 E2E(scripts/e2e/custom_tables/sandbox_ttl)在針對舊程式碼的 RED 階段,就是要求一個 timeout_seconds=1800、跑 12 分鐘的 run 以這個結果結束。在託管雲端上,每個 Cloud Run execution 也會以同樣的總長度作為它自己的 timeoutSeconds 覆寫啟動(不超過 168 小時),與重複使用的 Job 範本無關。已儲存的 timeout 缺少或超出範圍,會讓認領 fail closed(僅控制面)。
**前端調整:**無。很長的 timeout_seconds 不必再為了回報結果而壓在 10 分鐘以內。見限制。
runner 會重試它的完成回報
Go runner 的終態 complete 呼叫,現在每次嘗試有 30 秒預算,當控制面回 408、429、500、502、503 或 504,或傳輸失敗(逾時、EOF、連線錯誤;找不到主機不重試)時,最多嘗試三次、每次相隔 500 毫秒。依這項變更自己的標題,重點是:那個呼叫的一次暫時性失敗,不會再讓已完成的 run 被 reconcile 判成 platform_failed,而不是帶著客戶的 exit code 或逾時結束。runner 的變更只有在環境版本由含該變更的 runner release 建置時才會到達它;版本的 runner_release 記錄它用的是哪個 release。
**前端調整:**無。
表格 writeback 與 commands_only 的表
自訂表格的表可以帶 write_policy: commands_only(它的紀錄只能透過 Command 變更)。表格 writeback 的 output_policy 不能指向這種表:草稿建立/更新回 422 output_policy_governed_table;發布時,以及提交 run 且真的鑄出憑證時,同樣的狀況是 409 output_policy_unsatisfiable;在該政策開啟之前就鑄出的、限於單次 run 的表格 token,會被 callback 以有型別的 403 governed_context_required 拒絕。
**前端調整:**顯示新的 422;必須寫入這種表的任務,改用 Command 輸出這一種。見任務、分享與啟用與錯誤。
營運面(僅供參考)
在地端部署上,provider 容量更新與 GET /root/sandbox/quota-snapshots/refreshes/{refresh_id} 現在把快照範圍限定在別名 onprem,不再要求 GCP project;託管雲端在 project 缺少或格式不對時,仍回 409 sandbox_gcp_project_unset/sandbox_gcp_project_invalid。營運路由表不變(27 條)。見營運與 Root。
這次稽核找到的更正
下列內容在這個 release 之前就是錯的或不完整,兩種語言都已修正:
- 錯誤碼頁寫「
Authorization與X-Api-Key都沒帶」是 401;實際是 418(Could not validate credentials),認證頁本來就是這樣寫。 - Agent toolkit 頁說
PATCH /private/chatrooms/setting/jobs/{chatroom_id}的 OpenAPI 說明只列 wms 與 booking;實際上它列有sandbox(v5.10.11 時就是如此)。 - Agent 相關頁面沒有寫明:表格 writeback 的
output_policy即使requires_confirmation是 false 也會強制走提案路徑;現在寫了。
釘選與匯出
README、兩個首頁與執行規劃頁現在寫 v5.21.0 與完整 SHA 172a7f80bdf4…,schema 頁寫 v5.21.0。下方「如何更新這支釘」指令從這個 SHA 開始,並且另外涵蓋 src/routers/public/custom_tables_callback、src/tasks/sandbox_*.py、src/dependencies/sandbox.py 與 src/database/models/sandbox.py。llms.txt、llms-full.txt 與 Markdown twin 由這些頁面重新產生,包括新頁面任務輸出交給自訂表格 Command。
稽核附註——commit 分類
搜尋方式:(A) 對下方「如何更新這支釘」指令的路徑做 git log 70862ceae7..172a7f80bd(71 個 commit:60 個非 merge、11 個 merge);(B) 碰到任何路徑含 sandbox 的檔案的非 merge commit;(C) commit 訊息提到 Sandbox 的 commit。聯集共 111 個 commit(99 個非 merge、12 個 merge),每個都恰好分類一次:
- 有文件記載/串接端看得到的契約變更(上面各節涵蓋;5 個 commit):
7c41cbc501(Command 輸出,#1853)、739894a878(憑證壽命,#1826)、433e22bd00(完成回報重試,#1819)、3fa893b56d(commands_only,#1788)、4b11540fa6(所選 provider 的容量更新,#1804)。 - 沒有額外契約變更的 merge commit(12 個):
c7e3d41426、aba63cb121、e65d71e298、a81511bb15、9a4d7b0660、028db39d42、a3c9665e10、8e80f29a49、ba3290fcb7、d65e9e5b26、62b4075d81、d77bcfa6e4。 - 開發環境、基礎設施、CI 與打包——對已發布產品沒有影響(9 個):
03d3f5d4df、3cb548cb43、9057cc086d(Sandbox 開發環境啟用、啟動期間維持關閉、在共用 project 內隔離;production 資源名稱不變)、69598743e9(較早把 Sandbox 從開發部署移除)、feba983f1e、4d7415d467(agent-workspace IaC)、1bea1ef4ec(CI workflow)、45242b8711(雜湊鎖定的 Python lock)、552609edbf(release 與已部署映像的佈局契約)。 - 內部變更,沒有契約變更(1 個):
37b79e2222(行程回收現在會等待有追蹤的行程內工作,包括 run 排入佇列後喚醒 controller 的執行緒;3 秒 sweep 仍是備援)。 - 地端安裝套件、營運腳本、教學與 compose add-on(26 個):
e19594605b、4c11ddea98、3d05af8193、690d574faa、415bd335d1、10a72f6c1f、11b49fd897、695e7bf761、200512a125、150a8fc8be、46e9051e6c、d4a6be835b、caa80868dd、16e82ca37f、840b0c7220、fd991177fb、d72b880df9、67c513e294、4891aab5e2、e62c9907eb、73e377d58c、bbabc707ac、22ca3e74d0、aafb03c9be、368713044a、ebb3e0c988。它們改的是引導式安裝程式、它的診斷、add-on 位址處理與打包,不是租戶 API,手冊記載的任何變數也沒有改變意義;手冊把安裝套件交給後端的docs/deployment/on-prem/。 - 測試工具與 Agent 工具選擇(3 個):
1d89736847(針對既有 Sandbox scope 拒絕行為的測試)、badb87d738(自訂表格驗收 runner)、9ec0fbae42(智慧工具選擇器;五個 Sandbox 工具仍被釘住)。 - 其他模組——共用被稽核的路徑、只在訊息裡順帶提到 Sandbox、或碰到檔名含
sandbox的檔案;都沒有改變 Sandbox 行為(55 個):7df284baa0、7a0a2b8a0d、dbdaeb4243、a21ba222ef、6bad38fbe9、737955fb69、6bf9760687、c9392060d1、c15a12f9b0、eaf7c84d76、cc5d2db9c8、1a607e52b8、bfb9ced050、cfb7457a4f、430d9aa23c、a510143539、9750eac491、74a6a2b13a、c48f763467、457140d0de、0f8985db78、2f548ae2a7、d48281f258、4ac973afae、2b78575d80、f0e17d4259、e5770ead73、ce26227b87、c6c21d4194、8ecc1f9a54、63636600ae、49103a6c5d、82042fed21、3b380b1a63、68f5beffc2、455fa388c6、e932ccdc0a、ae5229549e、9f7f93b7fd、2b0c952fef、1bc159f300、8cb89701ef、1eb1d3e517、68bc44c431、48577334a5、9587d94e93、8c0435fd3f、0e89a8032d、64843e7b66、19c720aad2、168e3d0da0、0de6458c1e、ba8ca8b3cd、c6d02fa6fa、78d30b5ff6。備註:新增的 OAuth access token(只在/mcp/*有效)與 Command service key(只在兩條 Command 路由有效)在 Sandbox 路由上都會被拒;7df284baa0讓自訂表格的請求處理離開事件迴圈(command-output 路由變成在 threadpool 執行的一般 handler,契約相同);455fa388c6移除 AI 呼叫上的價格閘門,並保留 Sandbox 雲端建置的價格閘門;4ac973afae修正表格 callback 的updatedata 說明(僅文件)。
2026-09-19 — production v5.10.11
來源與上線:後端 70862ceae7869470c14f4ed7cc7dfc3609e299f4 為 v5.10.11 版本提交(origin/master HEAD,tag v5.10.11)。Production release 由 「Release & Deploy to GCP (production)」run 35428530132 部署,該 run 於 2026-09-19 成功結束;production 讀回 GET https://api.cluster.scfg.io/openapi.json 的 info.version 為 5.10.11。本則稽核 9b8b95e59..70862ceae 之間、碰到下方「如何更新這支釘」指令所列路徑的每一個 commit(共 202 個:190 個非 merge commit 加 12 個 merge commit)。這段範圍大多是新的地端 provider(SANDBOX_PROVIDER=onprem);除非另外註明,託管雲端部署的行為不變。
Run 佇列:位置、預估時間與 429 背壓
SandboxRunDetailResponse 在 queue_position 旁新增可為 null 的 eta_seconds,所有回傳 run 的路由都有(提交、列表、明細、取消、重試、Quick Run)。託管雲端上 queue_position 意義不變(在公司 queued run 中從 1 起算),eta_seconds 一律為 null。地端部署兩者都來自 executor 佇列:位置也涵蓋 executor 尚未接手的 starting,預估時間為 ceil(位置 ÷ 整個 fleet 的 run slot 數) × 同一 profile 最近 20 筆已完成 run 的平均耗時,沒有歷史時為 null。OpenAPI 現在把 POST .../runs、POST .../runs/{run_id}/retry 與 POST .../quick-run 的 429 宣告為 SandboxQueuedRunLimitErrorResponse 或 SandboxQueueFullErrorResponse;queue_full(僅地端:整個 executor fleet 的佇列已滿)帶 limit、retry_after_seconds 與 60 秒的 Retry-After header。
前端調整:eta_seconds 非 null 才顯示,絕不自行推算;依兩個 429 元件重新產生 client。遇到 queue_full 先等 Retry-After;遇到 sandbox_queued_run_limit 則退避到等待中的 run 變少再送。見執行與結果、Schema、錯誤與限制。
Quick Run 的 input 終於送達工作
在這一版之前,Quick Run 從未暫存 input:工作讀到的是空的 /workspace/input.json,GET .../content 也回 has_input=false。Quick Run 現在跟手動提交一樣暫存 input,$TEAMSYNC_INPUTS_FILE 就是送出的 JSON。因此 Quick Run 也可能回 422 input_not_stageable/503 input_staging_unavailable(在 manager 檢查與冪等重放之後、建立任何任務或 run 之前)。
**前端調整:**拿掉針對 Quick Run 空 input 的權宜做法,兩個暫存錯誤比照手動提交處理。見 Quick Run 與錯誤。
依 provider 過濾的區域目錄與 onprem
SandboxRegionCode 新增 onprem,公開區域的 tier 範圍放寬為 1..3(3 = 固定/專屬容量,以名目會計費率計價)。GET /public/info/sandbox/regions 回傳部署所用 provider 的目錄:託管雲端永遠不列 onprem;地端部署只列 onprem。selectable_regions 是租戶 settings 使用的 active operator 列集合;正確 bootstrap 的地端部署只有 onprem,但 operator 額外建立的 tier 1/2 列可能出現在其中,而公開目錄與 build gate 仍依 provider 過濾。POST .../versions/{vid}/builds 若 build_region 不在該部署公開的目錄內,回 422 build_region_unavailable(地端在已有 active 區域列、但指定的不在其中時也回這個)。省略 build_region 時,地端預設 onprem,雲端仍是 asia-east1。
**前端調整:**區域選項維持由目錄驅動,接受 tier=3,建立建置的請求不要寫死 asia-east1。無 body 的 retry-build 端點在此釘選仍由後端固定使用 asia-east1。見環境與建置、端點、列舉與執行規劃。
地端部署
自架主機用 Docker executor 取代 Cloud Run,執行同一套租戶 API。串接端看得到的差異:只有一個 onprem 區域並以 tier 3 名目費率計價;有每公司與整個 fleet 的佇列上限(queue_full);submit/start 狀態的 next_action 用語改為不指名供應商(「the local runtime」;容量狀態則是「the on-prem sandbox」);沿用雲端 runner 的 error_code/failure_stage 詞彙,逾時 exit_code=124、取消 130;映像的其他 ENV 不再滲進工作 shell;執行期對外網路另需主機設 SANDBOX_ONPREM_EGRESS=internet;工作要回呼主機自己的 API,需要 operator 開放 API origin 並提供信任憑證庫。Agent 提交遇到任一種 429 時,工具錯誤為 provider_unavailable。
**前端調整:**依目錄與 GET /settings 建構介面,不要預設雲端行為;next_action 原文呈現。見雲端與地端部署、營運與 Agent 工具。
Runner:巢狀行程命名空間與計量修正
這一段 runner 說的是託管雲端;地端使用 Docker worker 的直接 shell 路徑。託管雲端開啟執行期對外網路的 run(預設)上,此釘的 runner 原始碼會嘗試讓工作 shell 成為巢狀 user+PID 命名空間的 PID 1;主機允許私有 /proc 設定時才會擁有自己的 /proc,若主機不允許命名空間則保留直接 shell fallback。關閉對外網路的 run 維持直接啟動 shell。短命的孤兒行程(例如 BusyBox wget https://… 分出的 ssl_client)在巢狀設定啟用時不再讓整個 run 被 SIGKILL 成 exit_code=137。命名空間內,主機允許 id 對應時 id -u 為 0,否則是溢位 uid 65534 且沒有任何 capability;/workspace 與 $TEAMSYNC_OUTPUT_FILE 仍可寫入。Runner 在 /workspace/.teamsync/ 下保留自己的紀錄。另一項修正:工作已 exit 0 的 run,不會再因網路介面拆除的時間差被結算成 platform_failed/metering_failed/stage metering_wait/exit_code=130。Runner 的變更要等環境版本由包含它的 runner release 建置後才生效;版本的 runner_release 記錄是哪一版。
**前端調整:**不需要調整;有背景行程時不要對 exit_code=137 做特例。見執行期須知與本機重現。
失敗提示與保留
secret_env_name_collision 的 failure_hint 改為說明真正的規則(某個一般環境變數與已綁定的 secret slot 同名),secret_env_value_collision 也有了自己的提示(某個一般環境變數的值與已綁定的 secret 值相同)。歸檔回收在仍有未歸檔的 curated 版本釘住同一 digest 時,保留租戶建置的 execution 套件。見執行與結果與保留。
營運面(僅供參考)
GET /root/sandbox/queue 以同一形狀在每種部署列出等待中的工作與 executor 容量。註冊 tier 3 區域只接受地端部署上的 onprem。營運路由表現在列出全部 27 條 root 路由,包括先前漏列的 GET/PUT /companies/{company_id}/policy。見營運。
釘選與匯出
README、兩個首頁、執行規劃頁與 Schema 頁改標 v5.10.11 與 70862ceae。下方「如何更新這支釘」指令改從這個 SHA 起算,並加入營運與控制面 router、地端 executor 與 controller、sandbox/onprem/、整個 infrastructure/sandbox/,以及這些頁面引用的其他後端路徑。llms.txt、llms-full.txt 與 Markdown twins 由這些頁面重新產生。
稽核附註——commit 分類
範圍內每個 commit 都已分類。先列出 29 個契約變更 commit;其餘 commit 沒有改變任何文件化或串接端可見的契約:
- 文件化/串接端可見的契約變更(由上方各節涵蓋;共 29 個):
3edebc843、3dd307ba0、fa423ad8c、03b3eca5f、1d806d00b、a08bd498e、ee8a6de1b、aecbe16d6、fe074dfe0、6d42c0d3a、0e8a4b584、c9b5894fe、4687c6553、f916c29fe、a3f50f512、ec39191f0、3205659d2、c6c55f12f、ee54b9584、dfd96f0ca、5c69c5917、d5edcffc5、d384d06a5、dd005d9fe、33eebf13d、e157b4093、e882217cc、eee7aff4b、a78a0c47a。 - Merge commit,除了合併進來的 commit 外沒有額外文件化契約變更:
bf5968970、6f7718d4b、54c2c1012、d331534b1、e773750a4、f93f24597、679147a5f、d9aac6f71、d615306d9、a70e4fe8f、37f3fd1f3、ae8783293。 - 地端 provider 內部(work lease、排程器、sweeper、executor、建置關卡、registry、dind 打包;僅供營運參考):
6491285ef、bcee513d9、90c7cb8f1、acd18b7c1、542defb74、f232d0852、ec3d10407、50f1373f8、b7e54b141、b9909ad18、ec9dedd7a、2b31cd051、810484db2、d12a69949、761092f43、bd8d951a2、56e695fad、6c435f033、5741e685d、bf9f8ce37、d0d872302、3e8f8d4fc、c8387f7bf、f3e3388ae、9a62fb64b、fb75f449a、9f9a299f4、cf8cd1ff0、51f334f33、af83f77df、302fd9542、c2cc9ec74、c2d75c8a3、7867f3971、8047773ee、0119b6a50、fa6a3dcfd、beb9996a4、14ba3ab85、b8f16252a、aa0e1f609、5faa8bf52、410b1f53b、1fe95018c、24ab14393、76b048f28、6e2f05e2b、87fb335e3、8a2c6f337、30c27f6d3、a78b86674、bf42d7e23、d7ed040ae、5ffa0eaa4、50c52d071、c3a495845、e3ed8437f、8197701e2、3000ac16e、e7f7d9f45、fbdd515b1、ff9a4ca3d、28b6b3312、2a7da3852、8daf2a81a、9c688487f、08c3fcf77、6c40e71d5、3b5780264、cece7e91b、da366aaca、73c3455e7、3917dc89b、7c0bd4de6、abbeb5bad、e140b4c42、dc05cd786、bbe709b1a、93951d7f1、fa4c6b5f5、c53f60d98、8fa0aec70、28e9fdd5e。 - 共用重構、效能與測試(雲端回應逐位元不變):
ecfeeca63、8cb94350e、f58826b93、f8795d46f、04d436164、c5cbb1011、275c43f31、d78342af0、106469961、543ad2b73、d926fd9f1、37b70caf3、e8a0f7327、0ea91eaf5、b0602e136、b14239834、bb043e755、544737775、dac6cd145、44f0b9d4a、c51dc3406。 - Runner subreaper 嘗試,已在同一範圍內被巢狀命名空間取代:
5d1acdf93、9847dc302。 - 共用稽核路徑的其他模組(
src/schemas/enums.py的非 sandbox 列舉、infrastructure/sandbox/gcp/**/agent-workspace、src/routers/private/modules/server.py的私有模組掛載):bbe49538b、ebf8c6d6e、c9c805528、d911258d8、cbae44462、6501074b0、b86990f66、3320fcacd、89677a14c、1206ffea9、68d62b320、f30db1557、50ff40109、79722e274、9819680b3、22a4e375f、8f3fd1757、ff1fdfa7b、3e238babc、037b51362、e1a955a22、a9d6127b6、71dc5749c、643bb249a、55998aecc、3cccd926a、7877bb6fd、c7c5f9fc2、90ad65f5d、e7b251ecc、27f9fe4be、49658aa62、92934f0c9、4aa4aea74、bfab0b7dc、e2f7d67d2、ae2254aa6、3e6e6e0e6、293488605、8df1ababe、b5ab0d2db、3916a119e、d38cca5a9、145c7f438、7ba15c58e、3a79fb629、4e4ecec5e、2649934fe、a9ecda8fb、2f7f9a7b6、c2a9d7dbe、f3f70735e、b8c8fe2b8、1b295eb93、a396ac72f。
2026-09-10 — production v5.10.0
**來源與上線:**後端 9b8b95e598a7b1f606c030d8dd0f27f26c748c83 為 v5.10.0 版本提交,執行程式碼與已驗收的 9393591ae 相同。Production release 與部署讀回 已完成;本頁日期採 Asia/Taipei。以下描述目前契約,舊紀錄保留當時的上線範圍。
環境 owner 與釘選受眾
環境編輯不再只限公司管理員。POST /environments 接受 owner_scope=company|department 與 owner_id;預設 scope 是 company,因此部門管理員必須明確選自己的部門。PATCH、context 上傳、版本/build 操作與 archive 都依環境 owner scope 授權,兩種 owner 共用公司的 active-environment 上限。
**前端調整:**保留 owner_kind 以及獨立的 owner_scope/owner_id,依目前授權顯示控制。部門環境可給同部門任務或公司擁有任務釘選,不可給其他部門任務或 Quick Run;Quick Run 需要公司擁有或 curated 環境。見環境建立及權限矩陣。
能力提示與 source 可見性
GET /me 提供當前管理能力與可建立的 owner scopes。manageable_chatroom_scope=company 配合空房間 id 陣列,表示所有現有/未來房間。Owner 目錄(GET /tasks、detail、versions)依 live audience grant 顯示;同公司身分不等於能看全部任務。
**前端調整:**顯示 source 編輯功能前先讀必填的 source_visible。False 表示原始內容被隱藏,owner view 也須看 content_state 才能區分已清除的內容。Room menu 即使由 owner 呼叫仍使用 borrower view。見能力與管理查詢。
跨房間歷史與管理清單
GET /runs 提供依可見範圍篩選的跨聊天室歷史,支援可重複的 room/task/status、submitted_by_me、queued-time 界線、total 與 keyset 分頁。Task/room binding、task secret、task approval、consumer secret 清單提供管理既有物件所需的 metadata;consumer 外層回應也有 consumer_name。
**前端調整:**原樣傳回 /runs cursor,使用其授權範圍內的 total。管理清單上限 2000 列並回 truncated,能力 id 清單上限 5000;binding 歷史預設 active,需要 retired/all 時明確傳入。task_visible=false 時隱藏不可用的任務身分資訊;secret/approval 清單仍縮限到可管理的 consumer scope,不回 secret 值。見管理查詢。
分享、重疊 grant 與 Enable 同意
部門任務可直接分享到同公司任一存活房間,舊的「先分享整個部門」步驟已不需要。Grant 依 room、department、company 順序解析;剩餘等價授權可保留 binding,不會悄悄接受不同的憑證來源政策。既有 run 仍固定在當時的 grant/generation。
**前端調整:**Share 與 Enable 仍是兩個操作。Enable、明確建立 binding、accept-version,可替操作者有管理權且已解析的 borrower-secret scope 記錄精確版本同意。缺少 slot 或他人管理的 scope,仍須設定/核准;GET、發布、分享、一般 run 不會核准。接受版本保留已接受的政策。見任務受眾與Secret 同意(後端 #1260/#1262)。
忠實重試與可判讀的失敗狀態
POST .../runs/{run_id}/retry 在省略 body/input 或 input 為 null 時,逐位元重放保留的來源 input;非 null input 可在相同版本 pin 上以新 run 身分執行新參數。保留的 input 不存在、已退役、不可讀或 digest 不符時回 409 retry_input_unavailable,不再改用空 payload。
**前端調整:**新的重試意圖使用新的 Idempotency-Key;保留期限阻止重放時,請傳明確 input。呈現有型別的等待/失敗欄位與保留的 provider 拒絕來源,除了 work exit code,也分別檢查儲存內容與實際下載。受限 key 在允許房間呼叫 retry 回 405;管理/Quick Run 則可能更早在 auth 回 403。見執行/重試及錯誤。
Secret 驗證與作者 metadata
Create/rotate 解密後最少 4 個 UTF-8 bytes,不足時回 422 detail[].type=sandbox_value_too_short。明確核准會檢查已發布版本、相符 digest、risk_accepted=true 與非空、正規化、合法的 slot 名稱;claim 仍要求精確 borrower 子集。
**前端調整:**驗證位元組數而非字數,將過短值與信封/解密錯誤分開處理。新的 slot declarations 採具名物件,保留可選 metadata;讀取側仍支援歷史宣告。請更新產生的型別,不要把宣告攤平成失去欄位的任意 map。見Secret及Schema。
執行區域與費用規劃
公司 settings 提供 live selectable_regions、可為 null 的有型別 pricing,以及唯讀 operator egress 政策;region-readiness map 保留 live codes。費率含已加價的 profile 單價、最低計費/finalization 時間與網路預留界線。
**前端調整:**從目前需授權的 settings 取得區域,分別保留已儲存選擇,依文件的預估上限公式計算且不要再加價。提交時仍會重新檢查費率/政策/預算;runtime placement 不代表資料落地保證。見執行規劃。
手冊與 Agent 匯出
英文與繁體中文教學、endpoint/schema 表、首頁 pin、權限矩陣一起同步;更正了目前章節裡 retry 遺失 input、menu cursor 永遠為 null、結果上傳初始化限制、環境只限公司管理員等舊描述。llms.txt、llms-full.txt 與各語言 Markdown twins 由相同頁面重新產生。驗證包含 typecheck、靜態匯出與全部內部連結;來源/production 讀回及歷史 live E2E 證據仍各自註明範圍。
2026-09-05(四)— runner release 核准 + 歸檔版本回收(後端 #1162、SBW-08)
- Runner release 核准(後端 #1162):task-bundle 的 runner 允許清單從環境變數搬進 operator 表,每次請求即時讀取。
GET/POST /root/sandbox/runner-releases、DELETE /root/sandbox/runner-releases/{sha};版本回應新增runner_release_approved+runner_release_next_action;補上409 task_bundle_runner_unsupported。stage image 發佈會自動核准自己的 commit,新的 runner release 不再讓新建環境卡住。 - 歸檔版本回收(SBW-08):歸檔環境版本後幾分鐘內即回收其各區 Cloud Run Job、quarantine/execution 映像套件、attestation 與建置報告;孤兒 Job/套件也一併清掃。歸檔是終態。
- 建置線:提早離開共用 release Build 的 attempt(取消、逾時、步驟失敗)不再卡在
cleanup_pending。
2026-09-04(三)— 環境建置約快 2 倍(後端 #1154)
- 六個驗證 gate(static scan ∥ smoke → sign → attest → promote → final verify)現在是同一個 Cloud Build 的步驟,跑在
E2_HIGHCPU_8,接在 customer build 之後。輕量映像約 5–6 分鐘就ready(原約 12),重的約 7 分鐘(原約 19)。進度欄位不變;build_expected_seconds約 360。
2026-09-04(二)— 任務管理 + 產出物檔案(後端 #1151,runner #1152)
PATCH /tasks/{id}(name/description/agent_enabled)與POST /tasks/{id}/archive(整個任務歸檔:版本歸檔、binding 撤銷、排隊中的 run 取消、分享紀錄保留)。GET /environments/{id}/context-uploads:唯讀列出已完成的建置 context 上傳,附used_by_version_ids。- 任務可以產出檔案:放在
$TEAMSYNC_ARTIFACTS_DIR(/workspace/artifacts)底下的東西會變成該次執行的產出物 bundle——GET .../artifacts逐檔列出,POST .../artifacts/{artifact_id}/public-link逐檔分享。整包拒收規則寫在 runtime 契約的artifacts。 - context-uploads 列表補正(#1156):已被版本吃掉的上傳也會列出,並以
used_by_version_ids連回版本。
2026-09-04 — 前端追蹤項(後端 #1149)
GET /chatrooms/{id}/runs(游標(queued_at, id),最新在前)、GET .../menu(依task_id)、GET .../runs/{rid}/artifacts(依relative_path)都收page_token;三條的next_page_token現在是真的游標。POST /bindings/{id}/accept-version重新計算update_available,不再寫死false。SandboxTaskVersionResponse新增environment_id/environment_name。SandboxBuildAttemptResponse新增retryable_until(版本在retryable_failed期間的 7 天重試視窗)。build_ready/build_failed/build_cancelled有通知 producer(環境 owner-scope + 公司管理者,繁中文案),並新增curated_environment_published事件(通知有釘該環境的公司)。- 逐檔產出物公開連結:
POST/DELETE .../artifacts/{artifact_id}/public-link;GET /public/sandbox/artifacts/{token}串流該檔案驗過 digest 的位元組區間。 - 手冊自我矛盾修正(執行期網路、
script-uploads)——見下方 09-03 條目。
2026-09-01 — FE 疑問修正 + 全站 docs↔implementation 稽核 vs 上一支釘 7011b318d
前端 2026-08-31 的沙盒疑問清單觸發了這一輪:先修 OpenAPI 契約(後端 PR #1100,待合——SandboxRuntimeContract 新增 working_directory/network 欄位、work_command 欄位描述的 python3 work.py 壞例子改成 python3 /workspace/work.sh),再對全站 21 頁跑一輪 docs↔implementation 稽核:133 個經對抗驗證確認的修正全數套用(64 遺漏、50 矛盾、17 過時、2 雙語分歧)。影響最大的幾個:
- 認證:缺 credential 是 418(
Could not validate credentials),不是 401;受限金鑰打/tasks路徑是認證層 403,不是 404。 - runtime 契約:補
working_directory(cwd 名義上/workspace但不保證——一律絕對路徑)與network(執行期無對外網路;建置期RUN可連公網)。 - 建置:
source_digest單獨送不會比對已完成物件(錯誤晚到建置期才爆);Dockerfile 必須是封存根層的一般檔案;任何# syntax=行都拒(不只遠端);解壓上限 5 GiB/100k entries;retry-build不吃 body、不沿用 region/profile;503build_admission_fenced;complete 未被版本採用會佔住 staging slot。 - 執行:run retry 不重放 input(新 run 拿到空
{},input_digest卻對齊原 run——要重放請重新POST /runs);產出物下載回的是 5 分鐘 capability token。
新頁:本機重現與除錯——用本機 docker harness 重現執行契約(固定檔名、同一個 sh session、兩個環境變數)、Node node_modules 陷阱、與真沙盒的差異表。
2026-08-31 — job contract、共享 secret slot 政策、永久公開連結、傳輸加密 vs 上一支釘 e94b54c3d
Master 自上一支釘之後上了五波租戶面功能,加一波大型建置路徑穩定性修正(PR #1033–#1087,總共 41 個 sandbox 範圍的 PR——完整清單見下方)。e94b54c3 手冊裡以下句子現在是錯的:
- 建置 context 裡的 Dockerfile 必須
FROMalpine:3.20/busybox:1.36/debian:bookworm-slim之一;scratch在 compose 會被拒。 - Base 映像裡的 CVE(例如 Alpine:3.20)仍可能讓掃描失敗,
error_code=vulnerability_reject。 - 沒有辦法宣告 work script 怎麼被呼叫、預期什麼 JSON input 形狀,也無法在提交 run 之前先驗證那個形狀。
- 分享 grant 除了讓借用方自己提供值之外,沒有任何 secret 管理機制。
- run 的 log/output 永遠只能透過已驗證租戶走 5 分鐘的產出物下載 capability 取得。
POST /secrets/POST /secrets/rotate接受明文value。
正確替代:任務、發布與綁定(job contract)、Secret 與授權(slot 政策 + 傳輸加密)、內容、Digest 與下載(公開連結)、建立環境並建置(base 映像與 CVE 立場)、任務受眾(分享受眾 fail-fast)。
Job contract(PR #1065)
- #1065 —
work_command(自由格式的驅動指令,materialise 進/workspace/driver.sh,在startup.sh之後被 source;留空 →. /workspace/work.sh,跟舊行為位元組相同)、input_schema(JSON Schema draft 2020-12;提交的input違反它就是 422input_schema_violation,在 dispatch 之前擋下;input_example在這次草稿建立/更新呼叫時必須通過它,否則 422job_contract_invalid——發布永遠不會重新驗證)、一個runtime_contract區塊(固定呼叫方式 +TEAMSYNC_INPUTS_FILE/TEAMSYNC_OUTPUT_FILE環境變數名稱),出現在房間選單(SandboxMenuItemResponse——不論呼叫者是誰都是遮蔽過的預設值)、版本詳情回應(owner 看得到真正的work_command)與 input-preview 回應上,一條 multipart 的POST .../versions/{vid}/script-file上傳路由(owner-manager、只限草稿),以及一個POST .../versions/{vid}/input-preview試跑端點(精確位元組 + digest + schema 判定,不會 dispatch 任何東西——公司裡任何非受限成員都能對草稿或已發布版本呼叫,不是 owner-manager 專屬)。work_command對 borrower 禁止(遮蔽成預設值,就連選單上 owner 自己看到的也是遮蔽值);input_schemaborrower 看得到。Agent 工具選單項目帶input_schema與secret_slots,但不帶runtime_contract。
Dispatch 出包鏈(PR #1066–#1072)
- #1066 —
sandbox_cloud_run_quota_leases.weight從INT加寬成BIGINT。記憶體維度的 admission 權重是位元組數(標準 profile 的 2 GiB 租約會溢位有號 INT);自 ~08-26 起每個 dispatch 都卡在starting。 - #1067 — 給
sandbox_job_executor自訂 IAM role 加上run.jobs.runWithOverrides——跟run.jobs.run是不同的權限,每個帶 env override 的 dispatch 都需要它;之前每個 live dispatch 都 403provider_forbidden。 - #1068 — 執行中 job 的網路對外連線現在無條件停用。 Cloud Run Sandboxes launcher 的
--allow-egress要建立 veth pair,需要 root;runner 依設計拒絕以 root 執行。目前沒有逐任務的 opt-in。 - #1069 — REST 手動提交現在會在建立 run 之前,把 input JSON staging 進 owned-object store。之前從來沒做過——每個 REST 提交的 run,work script 都會默默看到空 input。
- #1070 — 修掉 #1069 造成的自我 deadlock:staging 現在發生在拿公司 policy row lock 之前,不是在 lock 底下。
- #1071 — 手動 run 的 input staging 拒絕現在會記錄完整例外鏈,並在 422 回應裡點名根本原因。
- #1072 — 手動 run 的 input staging 現在用 scoped idempotency key 的確定性
uuid5當 subject,而不是原始(無界限)的 key——後者曾經溢位SandboxOwnedObject.subject_id(String(36))。
永久公開連結 + 共享 secret 管理(PR #1074–#1081)
- #1074 — 永久公開結果連結:
POST/DELETE .../chatrooms/{id}/runs/{run_id}/{log|output}/public-link為一次 run 的 log 或 output 鑄造/撤銷一個沒有 TTL、不需驗證的GET /public/sandbox/artifacts/{token}下載連結。 - #1075 — 修掉一個
NameError(_reject_restricted定義錯了模組)——之前在 staging 上每次 mint/revoke 呼叫都會 500。 - #1076 — 共享 secret slot 政策:分享 grant 可以設定
secret_slot_policies({slot_name: "borrower"|"owner"|"owner_overridable"}),只能在建立時設,要改就重新分享。 - #1077 — Secret 傳輸加密:
POST /secrets與POST /secrets/rotate現在只接受混合 AES-256-GCM + RSA-OAEP-SHA256 的encrypted_value信封({encrypted_key, iv, ciphertext},都是 base64,金鑰來自GET /public/info/model_key/public_key)。明文value一律拒絕——四種可分辨的 422 錯誤代碼(sandbox_plaintext_value_rejected/sandbox_encrypted_value_required/sandbox_transit_keypair_missing/sandbox_encrypted_value_undecryptable)。 - #1078 — 修正了
GET /public/info/model_key/public_key的描述,之前還宣稱明文「可以當備援接受」。 - #1079 — 把部門擁有的任務直接分享給受眾之外的聊天室,現在在分享建立時就 fail-fast 409
share_target_not_in_audience,不會是一個悄悄永遠用不到的 grant。 - #1080 — Provider secret 路徑現在按任務分段(加上
.../tasks/{task_id})——兩個不同任務共用同一個 consumer scope 與 slot 名稱,不再會撞上同一條未分段路徑。 - #1081 — Manifest 揭露現在會重新檢查 owner 出借 pin 的 grant 存活狀態。一筆 grant 若在認領與之後的 manifest 讀取之間被撤銷,現在會被拒絕(控制面 409
share_grant_revoked),而不是仍然解析出那個過期的 pin。
建置路徑穩定性(PR #1033–#1055)
一條 20 個 PR 的鏈,全部是在 2026-08-21 到 2026-08-25 之間於 live 環境發現的,重新打通了租戶自訂建置的快樂路徑,並縮短了 rollout/診斷時間。兩個對租戶可見的結果已在上方點名(base 映像允許清單移除、CVE 閘門移除);其餘都是維運/CI 穩定性修正。
- #1033 — 打通自訂建置:Dockerfile base 允許清單自 08-21 起卡住每次建置(例如
node:20-alpine被拒),且沒寫回任何原因——只有一個乾巴巴的wrapper_fault。 - #1035 — Compose 接受 Node 系映像;每個 base 映像都會帶的一個無害
/etc/mtabsymlink,之前被誤判成保留路徑逃逸。每個 builder 拒絕現在都會點名自己。 - #1037 — Stage 映像發布從四個手動步驟改成一個 CI job,背後有一個真的 admission 圍欄(之前的「圍欄」只是一個沒有強制力的貼上字串)。
- #1038 — 依對抗性 review 加固了那個 CI 圍欄:每次長等待前都重新取得 re-entrant hold(之前的 TTL 可能在 rollout 途中過期、悄悄開了圍欄),並修正 rollback 的 annotation 處理。
- #1040 / #1041 — Compose 直接接受任何公開 base 映像(
node、golang、python-slim、debian、ubuntu、busybox、nginx全部 live 驗證過);CI admission 圍欄現在以 Job 執行。 - #1042 — CVE 掃描結果重新歸類為只是參考——
HARD_FAILURE_SEVERITIES現在是空的,OPA policy 也沒有嚴重度規則了,所以沒有 CVE 能擋建置。live 掃描階段現在一律把拒絕回報成policy_reject;vulnerability_reject仍留在封閉的 receipt 集合與 terminal-class 對照表裡(程式碼沒刪),但 live 路徑已經到不了那裡。SBOM/漏洞報告仍會產生並附掛。 - #1043 — 把 stage rollout 從約 22 分鐘砍到約 6 分鐘:一旦資料庫已經證明沒有進行中工作,就跳過一個 600 秒的 pod-grace 等待。
- #1044 — 一個沒留下 receipt 就死掉的 stage,現在會印出
stage_error gate=<gate> code=<code>,而不是靜靜地 exit 2。 - #1045 — 修好
attest閘門:Container Analysis v1 不接受 client 提供的 occurrence id;這個閘門自從上線就一直失敗。 - #1046 — Rollout 現在會強制刪除已排空的 controller pod,而不是用一個縮短過的等待去跟 deployment 自己的 600 秒 termination grace 賽跑。
- #1047 — 修掉一個洩漏的 context 上傳 staging slot(
complete_upload從沒釋放過它)——之前卡住每個租戶發布 E2E,撞staging_slot_exhausted。 - #1048 — Settled-lease 的 staging 上限檢查改寫成 correlated
EXISTS(之前是 O(公司終身上傳數量) 的IN清單)。 - #1049 — Smoke 閘門不再要求
bin/sh(usrmerge 系發行版如 Debian/Ubuntu/Fedora 只有usr/bin/sh);區域 Job 建立現在指向 promote 實際寫入的東西。 - #1050 — 一個重試中的
regional_readback閘門現在會記下真正的原因(例如provider_forbidden),而不是用一個只點名計時器的代碼結案。 - #1051 — 一個因期限觸發的取消,現在會保留閘門已經記下的原因,而不是拿一個乾巴巴的取消標記蓋掉它。
- #1052 — Buildkit scratch 目錄的收尾,不再因為 Python
TemporaryDirectory清理時的EPERM而讓一個原本已完成的建置失敗。 - #1053 — Promote 的 receipt 現在回報真正的搬移位元組計數,不只是邏輯/manifest 大小。
- #1054 — 把 EPERM 收尾修正做得更持久(純
mkdtemp+ best-effortrmtree);跨套件的 blob 複製現在會重新雜湊並登記到目標套件下,而不是相信一個僅限套件範圍的存在性檢查。 - #1055 — 給
sandbox-job-provisioner加上sandbox-execution的 Artifact Registry 讀取權——Cloud Run 建立時的映像可讀性檢查之前對每個租戶版本都失敗封閉。
控制面穩定性(PR #1082–#1087)
- #1082 — 每個控制面拒絕(claim/bootstrap/progress/manifest/……)現在都會記錄 stage、subject、拒絕代碼與狀態——之前只有裸的 HTTP 狀態碼會進 access log。
- #1083 — 把 sandbox-controller pod 釘住不讓 GKE Autopilot autoscaler 驅逐(
cluster-autoscaler.kubernetes.io/safe-to-evict: "false")——整併曾經每 5–7 分鐘就殺掉這個單副本 controller,悄悄讓已 staging 的 dispatch 卡死。 - #1085 — Sandbox-controller 的 KEDA scaler 從
gcp-pubsub換成gcp-stackdriver——pubsub scaler 已棄用,且它固定的回看視窗對這個低流量的訂閱回傳空值。 - #1086 — 一個背景 reaper 現在會救回卡在
starting/provider_submit_staged的 run(controller 在 dispatch 途中掛掉時,重新驅動同一條恰好一次的送出路徑)。取消一個尚未認領/正在競速的 run,現在會可靠地立刻釋放它的排隊與並行 lease,而不是偶爾洩漏。 - #1087 — Capacity-keepalive 刷新現在會在快照過期前約 60 秒觸發,而不是剛好卡在過期門檻上。Dispatch 拒絕與 sweeper 執行緒當機現在都會被記錄,不再靜默;controller 自己的 log 現在真的會送到 stdout(之前打到一個沒設定的 root logger 就消失了)。
仍不是前端面
/sandbox-control/*、/root/sandbox/*請求本體。- Cloud Build tags、stage 映像 digest、CI admission 圍欄內部、GKE Deployment/ScaledObject YAML、KEDA trigger 設定。
- 原始 R2 key/presigned URL/secret 值/writeback HMAC key/provider secret 路徑。
2026-08-23 — 實作建置路徑 vs 上一支釘 d99b4cad6
#975 之後 master 上了租戶 compose/scan 實作路徑與可持久化的建置取消(到 #1023)。租戶路由清單與列舉沒變。#975 手冊裡以下句子現在是錯的:
- 取消仍在排隊的建置 attempt 當下就結算。
- Writeback 把
TEAMSYNC_CT_WRITEBACK_URL注入 ordinary env(只有 token 走密封通道)。 - 任意公開
FROM映像都能 compose。(scratch不在允許清單;alpine:3.20 掃描常失敗。)(已於 2026-08-25 被取代——見上方 2026-08-31 的條目。) - 模組能用之前,公司必須先打開
SANDBOX_ENABLED。
實作路徑補上的(#1012–#1025 時期,釘在 #1023)
- 租戶自訂建置是真的 Cloud Build compose + scan。Dockerfile
FROM必須是alpine:3.20、busybox:1.36、debian:bookworm-slim(ECR public library 標籤)。其他 base 會 compose 失敗。掃描仍可能customer_failed,error_code=vulnerability_reject/policy_reject。(已於 2026-08-25 被取代——見上方 2026-08-31 的條目。) POST .../builds/{id}/cancel一律圍欄 stage capability 並寫入可持久化的 provider-cancel intent。輪詢到terminal_class=cancelled。排隊中取消不是同一請求內結算。- Writeback 的 URL 與 token 都走密封 secret 通道。兩者都不能進
ordinary_env(TEAMSYNC_是保留前綴)。 SANDBOX_ENABLED預設 true。false才是 kill switch(503sandbox_disabled),不是啟用單。- 快路徑:釘 live
system_curated目錄 SCFG Standard(每個公開區域都是state=ready)。除非你需要自訂映像,否則跳過租戶上傳/建置。 - 未完成的 context 上傳會漏 staging lease。撞上 20 個 live slot 會 429
staging_slot_exhausted,連 scan report 寫入也會卡住。Abort 未完成 session;不要對 init 空轉重試。
仍不是前端面
- Cloud Build tags、stage 映像 digest、
/sandbox-control/*、/root/sandbox/*請求本體。
2026-08-19 — PR #975 + #964 vs 上一支釘 076c1278
上一支手冊釘是 #956 的 merge。之後 master 進了自訂表格 writeback(#964)以及部門任務/上傳/歷史面(#975)。
不要再留舊故事。 以下句子現在是錯的:
- 目錄任務可以用
owner_scope=chatroom建立。 - 建立版本必填
source_digest;前端永遠不上傳位元組。 - 分享就會把任務放進 Agent 選單。
output_policy是不透明 JSON/只有聊天室擁有才能 writeback。- Staging 20/20GiB/每分鐘 5 次只在控制面。
- 租戶面除了 queued-run 沒有 429(上傳 429 存在)。
task.agent_enabled是 Agent 開關。
正確替代:任務受眾。
#975 補上的
- 建立只有 department/company;
POST /tasks送chatroom是 422。隱藏 Quick Run 父任務仍在。 - 分享對象:整間公司(含未來部門)、任一部門、擁有部門或已有 grant 的部門裡的房間。
- 房間
GET/POST .../granted-jobs.../enable|disable。選單 + Agent = 已授予 且 已啟用,每個呼叫者同一份清單。手動 REST 仍走can_execute_manually(share + 成員資格;不要求啟用)。 - Context 上傳:init → PUT
X-Sandbox-Upload-Capability→ complete →owned_object_id。單一部分、1 GiB、15 分鐘 capability。 GET /public/info/sandbox/regions與/profiles。xlarge租戶不可選。- Company manager
GET /tasks/{id}/runs+/numOfData(offset/limit)。 - 擁有部門的 writeback 即使房間是透過 share 進來,只要在擁有部門裡就會鑄憑證。公司擁有的任務不能發布
output_policy。
#964 補上的(以 #975 之後的狀態為準)
- 有型別的
SandboxOutputPolicy。執行期把TEAMSYNC_CT_WRITEBACK_URL/TEAMSYNC_CT_WRITEBACK_TOKEN注入密封 secret 通道(不在 REST,也不在ordinary_env)。 - 只有 JWT 使用者;API key/受限金鑰/社交客戶端不鑄。
- Trigger 路徑拒絕
output_policy版本。Agent 仍走確認。 - 403
output_policy_author_denied/writeback_authority_denied;409output_policy_owner_scope_unsupported/output_policy_unsatisfiable;503sandbox_writeback_key_unavailable。
仍不是前端面
/sandbox-control/*、/root/sandbox/*請求本體。- 原始 R2 key/presigned URL/secret 值/writeback HMAC key。
2026-08-19 — 更早一次相對 2026-08-13 的缺口補齊
2026-08-13 的手冊已有租戶路由清單。那次補了 schemas、agent 工具、通知、自訂表格 trigger、下載 TTL、取消來源,並修了若干內部矛盾。那些頁還在;這支釘改寫被 #975 證偽的頁。
如何更新這支釘
git -C ../teamsync-backend fetch origin master
git -C ../teamsync-backend log -1 --format='%H %s %ci' origin/master
git -C ../teamsync-backend diff 172a7f80bdf4dbdffdc018eb08bfa42c62bb485c..origin/master -- \
src/routers/private/modules/sandbox \
src/routers/private/modules/server.py \
src/routers/public/info/server.py \
src/routers/public/sandbox_artifacts.py \
src/routers/public/custom_tables_callback \
src/routers/root/sandbox.py \
src/routers/sandbox_control \
src/schemas/sandbox.py src/schemas/enums.py \
src/components/sandbox \
src/components/tools/custom/sandbox \
src/crud/sandbox \
src/crud/custom_table_triggers.py \
src/database/models/sandbox.py src/dependencies/sandbox.py \
'src/tasks/sandbox_*.py' \
src/workers/sandbox_onprem src/workers/sandbox_controller.py \
sandbox/runner sandbox/build sandbox/onprem sandbox/e2e-js \
docs/runbooks/sandbox-operations.md \
infrastructure/sandbox \
infrastructure/app/gcp/k8s/base/sandbox-controller-deployment.yaml \
infrastructure/app/gcp/k8s/base/sandbox-controller-scaledobject.yaml要分類的 commit 清單用 git log --oneline <pin>..origin/master -- <同一組路徑>,再加上碰到路徑含 sandbox 的任何檔案的非 merge commit(例如 scripts/sandbox/ 底下的地端安裝套件與 docker-compose.sandbox.yaml),以及訊息提到 Sandbox 的 commit(git log -i --grep=sandbox)。src/schemas/enums.py、src/crud/custom_table_triggers.py 與 infrastructure/sandbox/gcp/ 也放了其他模組(自訂表格、Edge Workers、agent workspace);這些 commit 請歸類為本模組以外,不要略過不記。
2026-09-03 —— 對 master 0203b987 的實機驗證
由 staging 實機執行(GCP log + 9 次執行的 JS 套件)補上的事實:
- 任務包:
POST /tasks/{id}/bundle-uploads(zip / tar / tar.gz,≤20 MiB),解壓到/workspace;runner 保留的根目錄檔名 → 422bundle_path_forbidden。 - runtime 契約修正:
runtime_egress_enabled開啟時有對外網路(pip / npm / go get 在工作階段可用);只有/workspace與/tmp可寫。——已被 #1145 取代:work shell 沿用映像的PATH極簡;python -m venv會失敗PATH(沒設時採慣用預設),官方python:*映像上venv可用。 SandboxRunDetailResponse的失敗來源:error_code、error_detail、exit_code(精確,含 launcher 壓縮過的結束碼)、failure_stage、failure_source、計量位元組。每分鐘 5 次上傳額度下提交 → 422——已被 #1140/#1145/#1146 取代:runinput_not_stageable;新版本第一次執行 1–5 分鐘input不再計入額度(連續提交全部queued),第一次執行約 10–35 秒到running。環境建置仍約 11–19 分鐘(七個循序驗證階段)。已知限制:runner 上傳結果可能在該額度下被略過(——#1140 已修(結果上傳不計入額度),#1145 再補強(輸出遺失時以completed+has_output=false)platform_failed/result_upload_failed結束,不會是completed)。- 參考實作:
teamsync-backend的sandbox/e2e-js/。