跳到內容

建立 Client

Pedelec 是 browser SDK 的入口。一個 instance 擁有一條連向 Pedelec Chrome Extension 的 external connection,並負責 request correlation、bridge timeout、event routing,以及透過這個 client 建立的 session objects。

import { Pedelec } from "@kaoruisaac/pedelec";
const pedelec = new Pedelec();

Constructor 會立刻嘗試連到設定好的 Pedelec Extension ID,不會等待 Desktop App 或 provider ready。

若執行時沒有 browser window,instance 就無法使用 browser bridge。若不存在 chrome.runtime.connect,或第一次 connection attempt 失敗,後續 bridge operation 仍會 retry;暫時性的啟動失敗不會永久 poison client。

const pedelec = new Pedelec({
bridgeTimeoutMs: 30_000,
});
type PedelecOptions = {
bridgeTimeoutMs?: number;
};

SDK request 等待 Extension bridge response 的最長時間。

  • 預設:30_000 ms。
  • 小於 1 的值會被 clamp 為 1
  • 適用於 listProviders()getSettings()createSession() 與 low-level request<T>() 等 bridge request。
  • 和 tool 的 timeoutMs 不同。
  • 不限制 sendText() 的整體 turn 時間;sendText() 先等待 bridge request,再等待 active agent turn 結束。

workspaceFolderPicker() 是例外:原生 picker 開啟並等待使用者操作時,不套用 SDK bridge wall-clock timeout;Extension/native disconnect 或明確的 bridge error 仍會立即 reject。

Timeout 會 reject SDK_BRIDGE_TIMEOUT,並在 details 附上 request metadata。

try {
await pedelec.listProviders();
} catch (error) {
if ((error as { code?: string }).code === "SDK_BRIDGE_TIMEOUT") {
showConnectionHelp();
}
}

在本機啟動特別慢時可提高數值,但過大的 timeout 也會讓斷線 UI 長時間像是卡住。應搭配 visible loading state 與分層 troubleshooting。

一個 browser page lifecycle 共用一個 client。

let pedelec: Pedelec | null = null;
export function getPedelec() {
pedelec ??= new Pedelec();
return pedelec;
}

同一 client 可以建立並追蹤多個 PedelecSession。共用 client 能避免不必要的 Extension ports,也讓 SDK 集中路由 session events。

不要把 client 當成跨 page singleton。Page refresh 會建立新的 JavaScript environment,因此一定會有新的 client 與 Extension port。

if (typeof window !== "undefined") {
const pedelec = new Pedelec();
startPedelecUI(pedelec);
}

在 framework browser lifecycle 建立 client,不要放入 server serialized state。

// 概念範例
onBrowserMount(() => {
const pedelec = new Pedelec();
setPedelecClient(pedelec);
});

SSR module 中不要這樣做:

// 不要在 server renderer 會 import 的 module 中建立。
export const pedelec = new Pedelec();

這個 instance 在沒有 window 時就已建立,hydration 後仍然 unavailable。

Client 會產生唯一 channelId 並開啟 external runtime port。每個 request 都包含 channel ID 與唯一 request ID;其他 channel 的 session event 會被忽略。

Port disconnect 時:

  • 已透過該 port 送出的 request 會以 EXTENSION_DISCONNECTED reject,且不會 replay;
  • 每個已註冊 session 收到 SDK-originated error callback,舊 handle 也會變成不可使用;
  • parent client 仍可 reuse,下一個新的 operation 會 lazy 開啟一條 replacement port。

getApprovalStatus()checkAvailability() 也會走這條 retry path。若 replacement connection attempt 失敗,當次 operation 會回報 unavailable error,之後的 operation 仍可再次嘗試。若要在 disconnect 後繼續 session,請明確呼叫 resumeSession(sessionId);舊的 PedelecSession handle 不會靜默移到 replacement route。

Pedelec client PedelecSession
Extension transport 單一 agent conversation/thread
Provider 與 settings query Turn lifecycle
Create/resume request Chat、status、tool、error callbacks
Request timeout prepare()sendText()end()
依 session ID 路由 event Session status 與 handler registration

完整環境 readiness 可使用 checkAvailability()

const availability = await pedelec.checkAvailability();

它不建立/resume session,也不開啟 approval。getApprovalStatus() 會做非敏感 ping,因此未 approval origin 也可 probe Desktop 而不開 popup;已 approval 時還會以 getSettings() 檢查。launchAttempted 僅表示 ping 或 settings probe 已送出,不能確認 Desktop 已啟動。

沒有單一 method 能證明所有 layer 都健康:

const approval = await pedelec.getApprovalStatus();
const providers = approval.installed
? await pedelec.listProviders()
: [];
  • getApprovalStatus().appConnected 是非敏感 Core connectivity 結果,不代表 approval 或 provider ready。
  • getSettings()listProviders() 需要 origin approval 且可能開啟 popup,且只公開 defaults 與 name/code/available/isDefault/error;為保持 forward compatibility,未知的 response 欄位會被忽略。
  • 建立 session 會更深入驗證 provider configuration。
  • 傳送 turn 才真正驗證 provider execution。

請採 progressive checks,不要只維護一個永久 connected boolean。

技術上可以建立多個 clients;每個都會開啟獨立 Extension port,只接收自己 channel 的 events。除非不同 application root 確實需要獨立 lifecycle,否則建議共用。

同一 session 需要多個 view 時,通常共用 client 與 application store 更簡單。Session 同時被多條 SDK connection 路由時,autoEndOnDisconnect 要等最後一條 route 消失才會 end。

下一頁:Providers 與 effort profiles