跳到內容

Session 狀態與 Events

PedelecSession 維護 browser-side status,並提供 status、error 與 end lifecycle callbacks。

type PedelecSessionStatus =
| "idle"
| "running"
| "waiting_tool_result"
| "ended"
| "error";
Status 意義 可呼叫 sendText()?
idle 沒有 active turn,可接收下一個 prompt 可以
running Preparation 或 user turn active 不可以
waiting_tool_result Agent 已要求 frontend tool,正在等待 不可以
ended Session 已結束;只要 Core 與記錄的 workspace 仍可恢復,同一個 attached handle 可明確呼叫 resume() resume() 成功前不可以
error Runtime 回報 session error 不可以;依產品策略建立或 resume session

新 PedelecSession object 的 local initial status 是 idle。resumeSession() 回傳的 handle 會在 promise resolve 前先和 Core 同步,因此初始也可能是 running、waiting_tool_result、error 或 ended。

Pedelec.resumeSession(sessionId) 只會重新 attach 到既有 thread,因此 ended thread 仍保持 ended。PedelecSession.resume() 才是同一個 handle 的明確 ended → idle transition;它保留 handle handlers 與 session state,provider runtime work 仍會等到下一個正常 operation 才 lazy 執行。

沒有 tool 的 turn:

idle → running → idle

包含 frontend tool:

idle
→ running
→ waiting_tool_result
→ running
→ idle

Active state 也可能直接 error 或 ended:

running → error
running → ended
waiting_tool_result → error
waiting_tool_result → ended

不要假設每個理論 intermediate status 都一定會在 UI 出現。Event 可能被 normalize、de-duplicate,或非常接近地到達。idle 是可觀察的 thread state,單獨不代表特定的 prepare() 或 sendText() promise 已完成。

const offStatus = session.onStatus((status, ctx) => {
console.log(ctx.previousStatus, "→", status);
console.log(ctx.type, ctx.source);
});

只有 value 改變時 callback 才執行。Registration 不會立即 emit initial status。

renderStatus(session.getStatus());
const offStatus = session.onStatus(renderStatus);

為了 UI 即時反應,SDK 可在 Core response 前先 emit:

  • sendText() 與 prepare() local set running;
  • tool event 處理時 set waiting_tool_result;
  • end() request 成功後 local mark ended。
  • session.resume() 不會 optimistic set idle;這個狀態由 authoritative Core snapshot 與 operation-less status_changed event 設定。

Context 可辨識來源:

ctx.source // "sdk" | "core"
ctx.type // "sdk_status_changed" | "status_changed"

Core event 使用 source: "core";SDK 內部主動變更使用 source: "sdk"。

Status handler 會去重。SDK 已設為 running,Core 後續也回報 running 時,不會再 callback 一次。

const offError = session.onError((error, ctx) => {
console.error(error.code, error.message, error.details);
console.debug(ctx.type, ctx.source, ctx.sessionId);
});
type ErrorEventContext = PedelecEventContext & {
type: "error" | "sdk_error";
};

onError() 適合 session-wide observation、logging 與 UI;發起 action 的 promise 仍要 catch。

Error callback 不一定代表 status 變成 error。例如 tool result submission 失敗會 emit SDK SUBMIT_TOOL_RESULT_FAILED,最終 session state 仍由 Runtime 決定。Core operation error 是 diagnostic;語意上的 rejection 由相符的 operation completion 提供。

const offEnded = session.onEnded((ctx) => {
console.log("ended by", ctx.source);
});
type EndedEventContext = PedelecEventContext & {
type: "ended" | "sdk_ended";
};

onEnded() 會在每次 transition 進入 ended 時執行一次,無論原因是 runtime event 或成功的 local end()。Handle 已經是 ended 時,重複的 ended notification 不會再次執行 callback。相同 handle 成功透過 resume() 由 ended 回到 idle 後,之後再次 transition 回 ended 會視為新的 lifecycle transition,再執行一次 callback。

Session end 時,pending sendText() 或 prepare() 會 reject SESSION_ENDED。

type PedelecEventContext = {
sessionId: string;
provider: string;
effortLevel?: "default" | "low" | "high";
sessionCreatedAt: number;
eventReceivedAt?: number;
eventEmittedAt: number;
turnId?: string;
turnStartedAt?: number;
turnKind?: "user" | "prepare";
source: "core" | "sdk";
};

不是每個 event 都有所有欄位:

  • Core chat/tool callback 有 received time 與 active turn metadata。
  • Local status change 可能發生在 Core event 前,沒有 eventReceivedAt。
  • Active turn 之外的 end/error 可能沒有 turn fields。
  • 新的 resumed handle 若應用程式沒自行保存,可能有空 provider metadata。

Tool handler 還會收到:

ctx.type; // "tool_call"
ctx.toolRequestId; // Core tool request identifier
ctx.tool; // tool name
ctx.turnId;

toolRequestId 可用於 diagnostics 與 application bookkeeping,不需要手動 submit result;SDK 會自動送出 handler return value。

Core event 可包含 per-session sequence number。Client 會忽略 sequence 不比上一個更新的 event,且 operation-scoped event 只有在 operation ID 對應目前 active operation 時才會採用。Application 自己 handler 內的 async work 仍要能處理 rapid transition 與 late completion。

Subscription 重建時,SDK 會先收到 authoritative lifecycle snapshot,再接收 live event。Resume 或 recovery handle 會依 snapshot 採用 Core status 與 active operation;若 Core 回報 pending frontend tool,請在 resumeSession() 後註冊 handler,SDK 會先保留 recovered request,等相符 handler 可用後才 submit。同一個 handle 的 session.resume() 也會在 promise resolve 前 reconciliation reactivation 後的 snapshot,因此 await 後 getStatus() 一定是 idle。

session.onArtifact((artifact, ctx) => ...) 會在 Core 將 provider 產生的檔案寫入 Workspace assets 後,回傳檔案 metadata。這是綁定目前 user operation 的 Core-originated event;context 包含 type: "provider_artifact"、source: "core" 與 turnKind: "user"。註冊 handler 不會重播已完成 turn 的 artifacts。

session.onArtifact((artifact, ctx) => {
console.log(ctx.turnId, artifact.path, artifact.kind);
});

Artifact event 到達時 session status 仍可能是 running。Callback 表示檔案已可讀取,不表示 turn 已完成。sendText() 只會在收到相符的 operation_completed event 後 resolve;resolve 時該 turn 的 artifacts 都已完成匯入。要探索歷史檔案請使用 listAssets(),不要預期 onArtifact() 會重播舊 event。

Reconnect 期間漏掉的 live artifact event 會沿用一般 event sequence 與 recovery 機制。Sequence deduplication 會避免重複派送已收到的 artifact event。

session.onStatus((status) => {
setSendEnabled(status === "idle");
setBusy(status === "running" || status === "waiting_tool_result");
setToolWaiting(status === "waiting_tool_result");
setSessionClosed(status === "ended");
});

Interactive tool 正在等待使用者時,不要把 waiting_tool_result 只顯示成 frozen。應呈現對應 modal 或操作提示。

下一頁:Session Lifecycle 與 Resume。