準備與傳送 Turn
一個 session 同時只處理一個 turn。prepare() 是 optional optimization;sendText() 會啟動 user turn,並在 Core 回報該 operation 完成後 resolve。
操作 session assets
Section titled “操作 session assets”App 與 Agent 共用 session 實體上的 .pedelec-runtime/assets/ 目錄。SDK 以 public /... path 提供這個 store,隱含根目錄不會出現在回傳 path。Session 結束前,App 可以 upload input file、遞迴列出所有已完成檔案,並讀取任一方產生的檔案。
Upload 檔案
Section titled “Upload 檔案”使用 uploadAsset() 傳送一個 browser File。單檔上限為 100 MiB;實體根目錄是 .pedelec-runtime/assets/,公開 path 使用 /...,並以 assets/ 為 SDK 隱含根目錄。Upload 可與 prepare 和 Agent execution 並行,但同一 session 一次只能有一個 upload。
const file = input.files?.[0];if (!file) return;
const path = await session.uploadAsset(file);await session.sendText(`請處理這個檔案:${path}`);File body 直接送往 127.0.0.1 loopback server,不經過 Extension、Native Messaging host 或 Core IPC。Upload 成功只表示檔案位於 workspace,不能保證 provider 或 model 能理解其格式。
列出 assets
Section titled “列出 assets”const assets = await session.listAssets();for (const asset of assets) { console.log(asset.path, asset.sizeBytes, asset.modifiedAt);}listAssets() 會以 flat array 遞迴回傳 assets/ 所有層級的一般檔案,依 filesystem modification time 由新到舊排序,時間相同時依 name 排序。nested file 的 name 仍是 basename,path 則是完整 public path,例如 /results/report.json。資料夾項目與 symlink 不會列出;所有層級只排除 .pedelec-* entry,其他 dotfile 仍會列出。此 method 不支援 pagination,Agent 執行期間仍可呼叫。可使用任一回傳 path 搭配 readAsset()。
讀取 asset
Section titled “讀取 asset”const text = await session.readAsset("/report.txt", "text");
const result = await session.readAsset<{ ok: boolean; score: number }>( "/results/result.json", "json",);
const modelFile = await session.readAsset("/model.glb", "file");"text" 要求 valid UTF-8。"json" 會再將文字 parse 成 JSON;generic type 只提供 TypeScript annotation,使用不可信資料前仍應自行驗證。"file" 會將 binary bytes 保留在 browser File。Read 支援已知 nested /... path,單檔上限同樣為 100 MiB。
Listing 與 reading 可和 provider execution 同時進行。若 Agent 可能仍在寫入 target path,請先等待明確的 tool result、message 或其他 workflow signal,再開始讀取,避免取得不穩定內容。Session shutdown 後,所有 asset methods 都會 reject SESSION_ENDED。
Optional preparation
Section titled “Optional preparation”await session.prepare();prepare() 讓 Desktop Runtime 在第一個真實 prompt 前先準備 provider session,可將部分 startup work 提前到使用者開啟 assistant panel 的時刻。
重要行為:
- 非必要;不呼叫也能直接
sendText()。 - 成功一次後,後續呼叫立即 resolve。
- Concurrent calls 共用同一個 in-flight preparation promise。
- User turn active 時呼叫會收到
SESSION_BUSY。 - Session ended 後呼叫會收到
SESSION_ENDED。 - Preparation turn 的 assistant output 不會送進
onChat()或onChatDelta()。 - Status event 仍可能顯示 preparation lifecycle。
async function warmUpAssistant() { try { await session.prepare(); } catch (error) { // Preparation 只是 optimization,保留一般 send flow。 console.warn("Pedelec preparation failed", error); }}Preparation 進行中呼叫 sendText() 時,會先等待該 promise。Preparation 失敗後,sendText() 會 fallback 到一般 first-run path,不會永久阻擋 user turn。
傳送 user turn
Section titled “傳送 user turn”await session.sendText("Summarize the selected document.");SDK 依序:
- 等待 in-flight preparation;
- session ended 或已有 active turn 時 reject;
- 建立 SDK-local turn metadata;
- 以
ctx.source === "sdk"將 status 設為running; - 經 bridge 傳送
send_text; - dispatch completed chat message、optional chat delta、status、tool events;
- 只有收到與本次 operation ID 相符的 completion 才 resolve 或 reject;
idle與errorstatus event 只是可觀察狀態,不是 promise terminal; - turn error、session ended 或 transport setup 失敗時 reject。
Completion semantics
Section titled “Completion semantics”sendText() 不回傳 assistant text。完成的 logical message 使用 onChat();只有需要 live incremental rendering 時才另外使用 onChatDelta()。
const completedMessages: string[] = [];const offChat = session.onChat((text) => { completedMessages.push(text);});
await session.sendText("Write a title.");console.log("Completed assistant messages:", completedMessages);offChat();Bridge request 成功只表示 Core 已接納 operation。Promise 會等到相符的內部 operation_completed event;單獨的 request response 或 idle 不會完成 turn。Resolve 也不代表 rendering framework 已把最後狀態 paint 到畫面。
同一 session 不允許 concurrent turns
Section titled “同一 session 不允許 concurrent turns”const first = session.sendText("First task");const second = session.sendText("Second task"); // SESSION_BUSYawait first;需要 sequential prompts 時由 application queue:
let queue = Promise.resolve();
function enqueuePrompt(text: string) { queue = queue.then(() => session.sendText(text)); return queue;}Production queue 要針對每個 task catch 並重新檢查 session status;否則一個 rejected promise 會讓後續 .then() 停止。
Composer state
Section titled “Composer state”Status 加上 local submit guard:
let submitting = false;
async function submitPrompt(text: string) { if (submitting || session.getStatus() !== "idle") return; submitting = true; renderComposerDisabled(true);
try { await session.sendText(text); } finally { submitting = false; renderComposerDisabled(session.getStatus() !== "idle"); }}兩者都需要:
getStatus()擋住明顯 invalid send;submitting關閉 reactive UI update 前的同步 race;- SDK 的
SESSION_BUSY是最終 authority。
waiting_tool_result 期間 session 仍然 busy。若 agent 正在等 modal 或前端操作,不要把 normal prompt composer 提早打開。
Empty text
Section titled “Empty text”目前 SDK 會 forward 傳入 string,不強制 trimmed non-empty。應用程式通常應先擋掉:
const text = input.value.trim();if (!text) return;await session.sendText(text);Error handling
Section titled “Error handling”Promise rejection 與 onError() 都要處理:
session.onError((error, ctx) => { logSessionError(ctx.sessionId, error);});
try { await session.sendText(text);} catch (error) { showTurnFailure(error);}Promise rejection 適合處理發起本次 action 的 UI。Core error event 是 diagnostic,會送到 onError();語意上的 turn rejection 由相符的 failed operation completion 提供。onError() 還能收到 asynchronous session problem,例如 tool-result submission failure 或 Extension disconnect broadcast。
常見錯誤包括 SESSION_BUSY、SESSION_ENDED、SEND_TEXT_FAILED、SESSION_ERROR、transport error 與 provider-specific error。
目前沒有 turn cancellation API
Section titled “目前沒有 turn cancellation API”Public SDK 目前只有結束整個 session 的 end(),沒有 per-turn cancel。除非產品接受「Stop generation」會結束整個 session,或未來 SDK 加入 cancel,否則不要呈現無法正確實作的 stop button。
下一頁:串流回應。