快速開始
本頁會使用第一個 available provider 建立一個 browser-side session,並加入可實際使用 UI 所需的最小 lifecycle handling。
1. 建立 client
Section titled “1. 建立 client”請在應用程式已進入 browser 後建立:
import { Pedelec, type PedelecError, type PedelecSession,} from "@kaoruisaac/pedelec";
const pedelec = new Pedelec();同一個 page 通常共用這個 client,不要每次點按鈕就建立新的 instance。
2. 檢查 Extension 與 providers
Section titled “2. 檢查 Extension 與 providers”const approval = await pedelec.getApprovalStatus();
if (!approval.installed) { throw new Error("請安裝或啟用 Pedelec Chrome Extension。 ");}
const providers = await pedelec.listProviders();const provider = providers.find((item) => item.available);
if (!provider) { throw new Error("無法啟動或連線 Pedelec Desktop;請手動開啟或修復安裝後再設定 provider。 ");}讀取 approval status 不會主動要求核准。真正建立或 resume session 時,尚未核准的網站才會進入 approval flow。
3. 建立 session
Section titled “3. 建立 session”const session = await pedelec.createSession({ provider: provider.code,});只提供 provider 時,SDK 使用 default effort;Core 從 Desktop Settings 解析 profile。可以在有無 provider 時傳入 effortLevel: "low" 或 "high"。Ollama 選中的 profile 必須有 model。
4. 傳送前先註冊 callbacks
Section titled “4. 傳送前先註冊 callbacks”let assistantText = "";
const unsubscribeChatDelta = session.onChatDelta((delta, ctx) => { assistantText += delta; renderAssistantMessage(assistantText); console.debug("turn", ctx.turnId, "received", delta.length, "characters");});
const unsubscribeChat = session.onChat((text, ctx) => { console.debug("completed message", ctx.turnId, text);});
const unsubscribeStatus = session.onStatus((status, ctx) => { renderSessionStatus(status); setComposerDisabled(status !== "idle"); console.debug(ctx.previousStatus, "→", status, ctx.source);});
const unsubscribeError = session.onError((error, ctx) => { renderError(`${error.code}: ${error.message}`); console.error(ctx.type, error.details);});
const unsubscribeEnded = session.onEnded(() => { setComposerDisabled(true);});onStatus() 只在狀態變化時觸發。初始狀態請使用 session.getStatus()。
5. 傳送訊息
Section titled “5. 傳送訊息”try { assistantText = ""; setComposerDisabled(true);
await session.sendText("說明這個應用程式可以做什麼。 ");
console.log("本次 agent turn 已完成。 ");} catch (error) { const pedelecError = error as PedelecError; renderError(`${pedelecError.code}: ${pedelecError.message}`);} finally { if (session.getStatus() === "idle") { setComposerDisabled(false); }}sendText() 會等待 Core 發出相符的 semantic operation completion 才 resolve;單獨恢復成 idle 或 transport 接到文字都不代表完成。
一個 session 同時只接受一個 active turn。Turn 進行中請 disable send button,同時仍需捕捉 SESSION_BUSY,避免兩條 application path race。
6. 結束與清理
Section titled “6. 結束與清理”await session.end();
unsubscribeChatDelta();unsubscribeChat();unsubscribeStatus();unsubscribeError();unsubscribeEnded();對已 ended 的 session 再呼叫 end() 不會重複執行。Session 處於 ended 期間,相同 handle 必須先成功呼叫 session.resume() 回到 idle,才能接收新訊息。
import { Pedelec, type PedelecError, type PedelecSession,} from "@kaoruisaac/pedelec";
let activeSession: PedelecSession | null = null;let disposeSessionHandlers: (() => void) | null = null;
export async function connectAndRun(prompt: string) { const pedelec = new Pedelec();
const approval = await pedelec.getApprovalStatus(); if (!approval.installed) { throw new Error("Pedelec Extension 目前無法使用。 "); }
const providers = await pedelec.listProviders(); const provider = providers.find((item) => item.available); if (!provider) { throw new Error("目前沒有 available provider。 "); }
const session = await pedelec.createSession({ provider: provider.code, }); activeSession = session;
let text = ""; const offChatDelta = session.onChatDelta((delta) => { text += delta; document.querySelector("#answer")!.textContent = text; }); const offStatus = session.onStatus((status) => { document.querySelector("#status")!.textContent = status; }); const offError = session.onError((error) => { document.querySelector("#error")!.textContent = `${error.code}: ${error.message}`; }); const offEnded = session.onEnded(() => { document.querySelector("#status")!.textContent = "ended"; });
disposeSessionHandlers = () => { offChatDelta(); offStatus(); offError(); offEnded(); };
try { await session.sendText(prompt); } catch (error) { const value = error as PedelecError; console.error(value.code, value.message, value.details); throw error; }}
export async function disconnect() { const session = activeSession; activeSession = null;
try { await session?.end(); } finally { disposeSessionHandlers?.(); disposeSessionHandlers = null; }}Production UI 還需要補什麼
Section titled “Production UI 還需要補什麼”- 不要只 throw generic error,應提供 approval 操作說明;
- 當產品需要本地 override 時,讓使用者選擇 provider 與 provider-independent effort level;
- 依頁面需求定義 tools;
- 依 lifecycle 需求保存或刻意丟棄
sessionId; - 分開處理 Extension、native host、Core 與 provider failure。
接著閱讀網站核准與連線狀態,或開始定義 browser-side tools。