Skip to Content
操作流程執行與取回結果

執行與取回結果

單一 run 操作以聊天室定址(跨房間歷史另有 GET /runs):/chatrooms/{chatroom_id}/...。受限 API key 可用 menu / 提交 / 查自己的 run / 下載自己的產出物 / 取消自己的 run;retry 對受限 key 在允許房間的 route allowlist 回 405,quick-run 在 auth 回 403(進到 router 則 404)。

1. 探索可執行清單

GET /chatrooms/{chatroom_id}/menu # query page_size (ge1 le100, 預設 50)

回 items[](SandboxMenuItemResponse):task_id、task_name、task_version_id、task_version_digest、requires_confirmation、secret_slot_names、必填的 source_visible=false、secret_slots[](逐 slot 的 borrower/owner 履行狀態)、timeout_seconds、update_available、input_instructions / input_example / input_schema、runtime_contract、secret_use_risk_warning?。只有已授予且已啟用。每個呼叫者同一份清單。 不含腳本原文——這個選單回應上的 runtime_contract.driver 永遠是預設 fallback ". /workspace/work.sh",任何呼叫者(包括 owner)都看不到真正的 work_command(真正的 driver 只會出現在 GET /tasks/{id}/versions/{vid} 或 POST .../input-preview)。page_size 預設 50;下一頁存在時,next_page_token 是不透明 cursor。房間管理者用 granted-jobs 改目錄。

提交前先在 client 端用 input_schema(若有)驗證 input——伺服器端也會強制檢查(422 input_schema_violation,在 dispatch 之前)。POST /tasks/{task_id}/versions/{vid}/input-preview 可以讓作者先試跑一個候選 input,看清楚 work script 實際會讀到的位元組。見 任務、發布與綁定。

2. 提交執行

POST /chatrooms/{chatroom_id}/runs Idempotency-Key: <UUID> # 必帶,否則 422 idempotency_key_required { "task_version_id": "...", "input": {...}, "timeout_seconds": 1800 }
  • input:canonical JSON(≤1 MiB、深度 ≤64、節點 ≤100k、無 NUL、無重複 key)。
  • body 過大先回 413 request_too_large(依 Content-Length)。
  • 版本非「published + content active」→ 409 version_not_runnable(content_state 為 scrubbed 時則是 409 content_scrubbed_irreversible;_refuse_not_runnable 只是後端內部函式名,不是回應碼)。
  • 回 SandboxRunDetailResponse,status: "queued"。
  • 手動 REST 需要 live share + 成員資格(can_execute_manually)。不要求啟用。產品 UI 仍應提交選單上的 task_version_id。Agent 提交需要啟用。
  • run 的 input 會先暫存為 owned object,但不再計入公司每分鐘 5 次上傳起始的額度(後端 ≥ #1140);連續提交會全部以 queued 接受。422 input_not_stageable 現在只代表 input JSON 無法 canonicalise 或寫入。
  • **佇列背壓(429,什麼都沒排入)。**OpenAPI 在提交、重試與 Quick Run 上宣告為 SandboxQueuedRunLimitErrorResponse/SandboxQueueFullErrorResponse,兩者都包在 detail 裡。sandbox_queued_run_limit 表示公司等待中的 run 已達上限(body 的 limit);退避到等待中的 run 變少再送。queue_full 只會出現在地端部署:整個 executor fleet 的佇列已滿,body 帶 limit 與 retry_after_seconds,同一個值(60 秒)也放在 Retry-After。等過這段時間後,用同一把 Idempotency-Key 重送。見雲端與地端部署。
  • 託管雲端上,提交後幾秒內就會派送(API 在 commit 後喚醒 controller;另有 3 秒一次的掃描當後備)。剛發布版本的第一次執行要在 Cloud Run 拉映像(wait_reason: container_starting,30–90 秒);之後每次約 10–30 秒到 running。

3. 輪詢狀態

GET /chatrooms/{chatroom_id}/runs/{run_id}

status 走 queued → starting → running → completed(或 customer_failed / platform_failed;取消 cancel_requested → cancelled)。終態看 terminal_at。

每個終態 run 都帶租戶可見的失敗來源(completed 時為空):

欄位意義
error_code固定 slug,例如 work_exit_nonzero、work_timeout、invalid_output、cancelled_by_request、binding_revoked、interrupted_by_platform、result_upload_failed(工作成功但平台無法儲存輸出——platform_failed,請重試)
exit_code工作指令真正的結束碼(exit 3 就是 3;124 逾時、130 取消、127 找不到指令)
failure_stagetask_bundle / secret_env / workspace / spawn / metering_* / work / output / timeout / cancel / egress_cap / wait
failure_sourcerunner / reconcile / dispatch / submit / policy / tenant
error_detail已消毒的簡短說明([A-Za-z0-9._@:/+=%;-],≤128)
failure_hint失敗終態附帶的修法建議,由 error_code/exit_code 推出(例如 exit 127 → 把工具鏈目錄加進 PATH、work_timeout → 調高 timeout_seconds)(後端 ≥ #1140)
measured_ingress_bytes、measured_egress_bytes工作階段計量的網路位元組

每一次非終態的輪詢都會說明正在發生什麼與該做什麼(後端 ≥ #1140),請直接呈現給等待中的使用者:

欄位意義
wait_reason封閉詞彙:awaiting_dispatch、company_saturated、no_active_region、missing_image_digest、capacity_snapshot_stale、provider_capacity_exhausted、provider_submit_pending、provider_submit_uncertain、container_starting、cancel_pending;running 與所有終態為空
next_action對應 wait_reason 的白話指引(要等還是要動手)
wait_since目前這段等待從何時開始(排隊中為 queued_at,啟動中為 started_at)
queue_position在 run 所等待佇列中的位置(從 1 起算);已被認領或派送、或根本沒在等待時為 null。雲端:在公司 queued run 中的位置。地端:跨公司的 executor 佇列位置,executor 接手前的 starting 也算
eta_seconds等待中的 run 大約還要幾秒開始,或 null。託管雲端一律 null。地端:ceil(queue_position ÷ 整個 fleet 的 run slot 數) × 同一 resource profile 最近 20 筆已完成 run 的平均耗時;沒有這些歷史時為 null。有值才顯示,絕不自行推算

給使用者看 error_code + exit_code,並提供 log(§5a)——log 裡有腳本的 stdout/stderr,例如 /workspace/work.sh: line 1: python3: not found。

secret_env_name_collision/secret_env_value_collision(platform_failed,stage secret_env)表示某個 ordinary_env 變數與已綁定的 secret slot 同名,或其值與已綁定的 secret 值相同。Runner 在工作開始前就拒絕,error_detail 為空,使用者只看得到 failure_hint:把變數或 slot 改名讓兩組名稱不重疊,或移除該變數、改從 slot 讀取 secret。

列表 + 篩選:

GET /chatrooms/{chatroom_id}/runs?status=queued&status=running # status 可重複

受限 key 和 department-manager 角色以下的一般使用者都只會看到自己提交的 run;整個房間的清單需要 department manager(或 company manager)以上的角色,且即使是管理者,每一列仍會經過 can_read_run 把關。

4. 看有哪些產出

GET /chatrooms/{chatroom_id}/runs/{run_id}/content # → { state, has_input, has_output, has_log, has_artifact_bundle, input_digest }

只有布林旗標,沒有內容本體(這是「c2 log/output」的 metadata 視圖)。state 可能是 "missing"(尚無內容列)。

Runner 的結果上傳不受作者上傳初始化次數預算限制。除了 work exit code,仍須分別檢查 has_output、has_log 與實際下載;result_upload_failed 是平台失敗,請依 failure_hint 處理。

5. 列出並下載產出物

GET /chatrooms/{chatroom_id}/runs/{run_id}/artifacts # 預設 lifecycle=active # → items[] { id, relative_path, byte_size, digest, deletion_state, declared_content_type } POST /chatrooms/{chatroom_id}/runs/{run_id}/artifacts/{artifact_id}/download # → { capability_token, expires_at, content_type, content_disposition, x_content_type_options: "nosniff" }
  • 怎麼產生產出物檔案(runner ≥ #1152,2026-09-04 已在 staging 實測:兩個檔案、列表、逐檔公開連結、撤銷):工作把檔案寫到 $TEAMSYNC_ARTIFACTS_DIR(/workspace/artifacts,一開始是空的;子目錄變成路徑前綴)。工作結束後 runner 把裡面每個一般檔案打成一個確定性的 bundle;之後 GET .../content 會回 has_artifact_bundle=true,GET .../artifacts 逐檔列出 relative_path、byte_size、digest。遇到 symlink/hard link/特殊檔、..、超過 1000 個檔案、路徑過長、或總量超過 standard profile 上限(workspace 上限 − 16 MiB,最多 1 GiB)時整包拒收——run 照樣 completed,log 會寫 stage=artifacts refused: <原因>。output.json 仍是結構化結果。
  • 回的是短效 capability_token(TTL 5 分鐘),不是原始 key / presigned URL。這個 POST 只做擁有權授權(產出物的 deletion_state 必須是 active)——沒有端點會兌換這個 token。要取單一檔案的位元組,用逐檔公開連結(後端 ≥ #1149):POST .../runs/{run_id}/artifacts/{artifact_id}/public-link → { url, token };GET <url> 驗過 sha256 後只串流該檔案在 bundle 裡的位元組區間,同路由 DELETE 撤銷,產出物或 bundle 退場後連結自動 404。只限 owner-manager(受限金鑰 404)。
  • deletion_state != active 的產出物,download 一律 404。
  • 要把一次執行的內容交出去,用 §5a 的 POST .../runs/{run_id}/{log|output}/public-link(GET /public/sandbox/artifacts/{token});Agent 工具 sandbox_job_status 另可回有界、不可信的預覽(output ≤64 KiB、log 頭尾 ≤16 KiB)。

5a. 把一次執行的 log/output 交給 TeamSync 之外的人(可選)

POST /chatrooms/{chatroom_id}/runs/{run_id}/{log|output}/public-link # → { run_id, object_kind, url, token, revoked, created_at }

這是永久的(沒有 TTL),連結一旦鑄出就不必登入——url 是 GET /public/sandbox/artifacts/{token},不需要登入。鑄造冪等;DELETE 同一條路撤銷(要新 token 再鑄一次)。只有 log/output,不含個別產出物。完整契約見 內容、Digest 與下載。

6. 取消 / 重試

POST /chatrooms/{chatroom_id}/runs/{run_id}/cancel # 取消你讀得到的 run(受限金鑰:只有自己的;kill switch 下仍可用) POST /chatrooms/{chatroom_id}/runs/{run_id}/retry # 以新身分重試終態 run;帶 Idempotency-Key;受限 key 在允許房間回 405(若到 router 才是 404)
  • retry 允許任何終態:completed/customer_failed/platform_failed/cancelled。永遠是新的 run_id。受限 key 在允許房間回 405(若到 router 才是 404)。同一把 Idempotency-Key + 不同 request hash → 409 idempotency_conflict。
  • Retry 保留精確的任務版本 pin,建立新的 run 身分。省略 body、送 {} 或 {"input": null} 會逐位元重放保留的來源 input;非 null 的 input 則覆寫參數,依一般輸入限制重新驗證與暫存。來源 input 不存在、已退役、無法讀取或 digest 不符時,重放回 409 retry_input_unavailable,請改傳明確的 input。必帶 Idempotency-Key;來源未終態回 409 retry_not_eligible。受限 Sandbox key 不可 retry:允許房間的請求會被 route allowlist 拒絕(405);不在 key scope 的房間可能更早回 403。
  • 來源 run 還不是終態 → 409 retry_not_eligible。
  • 未認領取消(queued):立刻變成 cancelled,釋放定價 hold,也立刻釋放排隊/並行容量 slot(2026-08-30 修正——之前一次競速中的並行 dispatch 可能讓已取消的 run 洩漏容量 slot)。已認領/執行中取消:cancel_requested,再由 runner 走到 cancelled;租戶取消圍欄贏了時 error_code=cancelled_by_request。cancel_requested_at 有持久化,不回傳。
  • 極少數情況下,若控制平面在 dispatch 途中掛掉,run 可能卡在 starting;背景的 reaper 會自我修復(重新驅動同一次 dispatch,且只會執行一次)——不需要租戶動作,取消在這期間仍然可用。
  • submission_source 是 rest(本頁)、quick_run、agent 或 custom_table_trigger。四條路的輪詢/下載契約相同。

Quick Run(manager-only)

一次原子建立隱藏的聊天室擁有父任務 + 版本 + 執行:POST /chatrooms/{chatroom_id}/quick-run(SandboxQuickRunRequest,帶 Idempotency-Key)。回 { task, version, run }。受限 key 在 auth 回 403(若到 router 才是 404)。不要把那個 owner_scope 抄到 POST /tasks。

Quick Run 跟手動提交一樣暫存 input(v5.10.11;在此之前工作讀到空的 /workspace/input.json,GET .../content 也回 has_input=false)。因此在 manager 檢查與冪等重放之後,可能回 422 input_not_stageable/503 input_staging_unavailable,此時尚未建立任何任務或 run。Quick Run 不受公司政策的 queued-run 上限檢查;地端部署的每公司與 fleet 佇列上限仍然適用(429 sandbox_queued_run_limit/queue_full)。

Company manager 每任務歷史

不是房間清單。只有 company manager:

GET /tasks/{task_id}/runs?offset=0&limit=10&order=desc GET /tasks/{task_id}/runs/numOfData

兩邊同一組篩選:status、method(agent|manual)、executor_kind(internal_user|external_user)、source_kind(chatroom|department|external_platform)、department_id、chatroom_id。列表回應是歷史列的 JSON 陣列(duration、已結算 cost_usd、執行者、房間、部門、method)— 不是 { items, page_token }。numOfData 是 { num }。受限金鑰在認證層直接 403(路徑在 restricted-key 拒絕清單上,也不在 Sandbox scope 允許清單內),永遠到不了 router 的 404。

常見錯誤速查

狀況回應
缺 Idempotency-Key422 idempotency_key_required
body 過大413 request_too_large
版本不可執行409 version_not_runnable
retry 來源不合格409 retry_not_eligible
未授權 / 受限 key 動了禁區 / 產出物非 active404
kill switch 下的變更503 sandbox_disabled(/cancel、/download 除外)
Envelope 塞不進配額403 budget_reservation_exceeded(不建列)
公司 queued-run 上限已滿429 sandbox_queued_run_limit(body 帶 limit;預設 100,可由公司政策覆寫;地端取它與主機每公司上限(預設 20)中較小者)——退避後再送,不要熱重試
地端 executor fleet 佇列已滿429 queue_full(body 帶 limit、retry_after_seconds;Retry-After: 60)——等過這段時間再重試
定價拒絕(no_active_region …)422
Last updated on