跳到內容

準備與傳送 Turn

一個 session 同時只處理一個 turn。prepare() 是 optional optimization;sendText() 會啟動 user turn,並在 Core 回報該 operation 完成後 resolve。

App 與 Agent 共用 session 實體上的 .pedelec-runtime/assets/ 目錄。SDK 以 public /... path 提供這個 store,隱含根目錄不會出現在回傳 path。Session 結束前,App 可以 upload input file、遞迴列出所有已完成檔案,並讀取任一方產生的檔案。

使用 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 能理解其格式。

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()

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

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。

await session.sendText("Summarize the selected document.");

SDK 依序:

  1. 等待 in-flight preparation;
  2. session ended 或已有 active turn 時 reject;
  3. 建立 SDK-local turn metadata;
  4. ctx.source === "sdk" 將 status 設為 running
  5. 經 bridge 傳送 send_text
  6. dispatch completed chat message、optional chat delta、status、tool events;
  7. 只有收到與本次 operation ID 相符的 completion 才 resolve 或 reject;idleerror status event 只是可觀察狀態,不是 promise terminal;
  8. turn error、session ended 或 transport setup 失敗時 reject。

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 到畫面。

const first = session.sendText("First task");
const second = session.sendText("Second task"); // SESSION_BUSY
await 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() 停止。

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 提早打開。

目前 SDK 會 forward 傳入 string,不強制 trimmed non-empty。應用程式通常應先擋掉:

const text = input.value.trim();
if (!text) return;
await session.sendText(text);

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_BUSYSESSION_ENDEDSEND_TEXT_FAILEDSESSION_ERROR、transport error 與 provider-specific error。

Public SDK 目前只有結束整個 session 的 end(),沒有 per-turn cancel。除非產品接受「Stop generation」會結束整個 session,或未來 SDK 加入 cancel,否則不要呈現無法正確實作的 stop button。

下一頁:串流回應