クローズ
作業を終えたセッションを閉じ、止めるべき実行を terminate し、結果が不明なターンを復旧する
セッションを終える方法は 3 つある。作業を終えたセッションは close で閉じる。今すぐ止める必要があるときは terminate する。terminate によって結果が不明なターンが残ったら、復旧判断をする。開始で作ったセッション(SDK は sessionId、HTTP API は SID)を引き続き使う。
| close | terminate | 復旧判断 close | |
|---|---|---|---|
| SDK | sessions.close | sessions.terminate | sessions.recoveryDecisions.create |
| HTTP API | POST …/close | POST …/terminate | POST …/recovery-decisions |
| scope | sessions:control | sessions:control | sessions:recover |
| 進行中のターン | 最後まで実行し、checkpoint を残す | 実行を強制終了する。ターンが outcome_unknown になることがある | すでに停止したセッションが対象 |
| 待機中の入力 | 閉じるときにキャンセル | 即座にキャンセル | 即座にキャンセル |
| 結果 | closed(元に戻せない) | stopped(resume 可能) | closed(元に戻せない) |
close
close は、実行中のターンが終わるまで待つ。まだ開始していないターンはキャンセルされる(cancelled_turn_count)。下の例では、最初のターンが終わった後に閉じる。
import { closeResult, waitForReceipt, waitForTurn } from "@werft-ai/sdk";
await waitForTurn(werft, sessionId, created.turn_id);
const { revision } = await werft.sessions.get(sessionId);
const accepted = await werft.sessions.close(sessionId, {
expected_revision: revision,
reason: "work is done",
});
const receipt = await waitForReceipt(werft, accepted.receipt_id);
console.log(receipt.operation, receipt.status, closeResult(receipt));close succeeded {
checkpoint_revision: 0,
cancelled_turn_count: 0,
resulting_admission_state: 'closed'
}closeResult は close の receipt の result を型付きで取り出す。開いているリクエストのせいで止まっているかどうかは closeBlocked(session) で見る。
閉じている間、セッションは closing である。開いている権限リクエストや質問があると close が止まる。応答するか interrupt すれば解除される。60 秒を超えると attention が CLOSE_BLOCKED になる。閉じた後のメッセージと pause・resume・close・terminate は 409 SESSION_CLOSED になる。すでに終了したターンを指す interrupt は、閉じた後でも何もしない成功(no_op: true)になる。
terminate
terminate は実行を即座に停止する。ツールがすでに外部に対して行ったことは元に戻さない。待機中のセッションを terminate するとすぐ stopped になり、そのまま resume できる。実行中だったターンがあれば、そのターンは outcome_unknown になり、セッションは復旧判断を待つ。
const runaway = await werft.sessions.create({
profile_id: "claude-coding-local",
repository_id: "sample-app",
message:
'GATE-SPEC {"id":"runaway","steps":[],"final":"never","finalDelayMs":300000}',
});
const id = runaway.session_id;
while ((await werft.sessions.turns.get(id, runaway.turn_id)).status !== "running") {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
const current = await werft.sessions.get(id);
const terminated = await werft.sessions.terminate(id, {
expected_revision: current.revision,
reason: "runaway turn",
});
console.log(terminated.receipt_status, terminated.external_effects_reverted);
while ((await werft.sessions.get(id)).admission_state !== "recovery_required") {
await new Promise((resolve) => setTimeout(resolve, 2000));
}
console.log((await werft.sessions.turns.get(id, runaway.turn_id)).status);accepted false
outcome_unknown復旧判断
outcome_unknown は成功でも失敗でもない。そのターンが外部に何をしたかを確認した人が判断する。
| 判断 | 内容 |
|---|---|
abandon | そのターンを cancelled にして、最後の checkpoint から続行できるようにする |
confirm_completed | checkpoint がそのターンまで含んでいる場合に、完了したものと認める。evidence_ref が必要 |
start_fresh | 以前の会話を破棄し、新しい Claude Code セッションで待機中の入力を実行する |
retry_restore | checkpoint の復元失敗の原因を直したうえで、同じ checkpoint からもう一度復元する |
close | セッションを終了する。元に戻せない |
const stopped = await werft.sessions.get(id);
const decided = await werft.sessions.recoveryDecisions.create(id, {
decision: "close",
expected_revision: stopped.revision,
reason: "nothing to keep",
});
const decision = await waitForReceipt(werft, decided.receipt_id);
console.log(decision.operation, decision.result);recovery_decision {
resumable: false,
checkpoint_revision: null,
resulting_admission_state: 'closed'
}キーに sessions:recover が必要である。
abandon と confirm_completed には、対象のターンを target_turn_id として一緒に送る。他の判断では受け付けない(400)。abandon の receipt の result.resumable が true なら、resume で続行する。
次のステップ
- receipt: 制御コマンドの結果を読む
- リポジトリなしのセッション
session-close.tsサンプル: セッション作成、イベントを受信しながらのターン待機、権限リクエストの許可、close、閉じた後のメッセージ拒否(409SESSION_CLOSED)までを一度に実行する SDK スクリプト