可執行的瀏覽器安全邊界
從遠端客戶端使用託管執行
當 run event 跨越不受信任的傳輸邊界,或客戶端可能在工作持續時斷線,請安裝獨立的 remote package。
客製化深度
客戶端 protocol
託管深度
瀏覽器或遠端 JavaScript 執行環境
文件狀態
可執行參考範例
前置假設
- 伺服器公開經授權的 run 生命週期,以及文件化的對外 payload contract。
- 需要 reload 復原時,客戶端可以保存最後完整處理的 sequence。
- 你的應用程式仍負責 timer、UI 狀態、重試呈現與產品結果處理。
- 執行期驗證 run envelope 與宿主提供的 activity、result schema。
- 游標推進、重複抑制、sequence gap failure、終結偵測與有界重試計算。
- 選用的 fetch 生命週期、漸進式 SSE parsing、response 驗證、identity check 與 reader cleanup。
- 哪些欄位可供遠端使用者存取,以及強制執行該 allowlist 的 schema。
- 驗證 header、傳輸 timer、subscription handle、游標持久化、online/offline policy 與 retry UX。
- 訊息、工具呈現、optimistic state、通知、錯誤與終結結果如何套用至產品。
只安裝瀏覽器安全套件
npm install @roackb2/heddle-remote zod不要把 @roackb2/heddle 打包進瀏覽器。獨立的 remote package 不包含 Node Agent 執行環境、model provider、server、CLI 或 control plane。本教學使用 Zod;若改用其他同步 Standard Schema validator,請直接安裝該套件並調整 import。
定義公開事件 contract
import { z } from 'zod'
import { ConversationRunProtocolCodec } from '@roackb2/heddle-remote'
const protocol = new ConversationRunProtocolCodec({
activity: z.object({
type: z.string().min(1),
text: z.string().optional(),
}),
result: z.object({
outcome: z.string().min(1),
summary: z.string(),
}),
})Validator 使用 Standard Schema 介面。Zod 3.24+、Zod 4、Valibot、ArkType 與相容 validator 都不需要 Heddle 專用 adapter。因為 parsing 與 serialization 在串流中同步發生,validation 必須是同步的。
Schema output 就是對外資料投影。Codec 會驗證你的選擇,但不會替你決定授權或清除 secret。一般 Zod object schema 會移除未知欄位;若使用其他 validator,請明確設定等效的 allowlist 行為。
Codec 會驗證非空 runId、正的 safe-integer sequence、ISO timestamp、activity、result、cancelled 或 error kind、宿主 payload,以及 JSON-safe value。無法安全跨越 JSON 的 bigint、function、symbol、非有限數值與 undefined 會被拒絕。
讓 consumer 負責游標正確性
以下 composition excerpt 假設 runId 來自已接受的 start response,而 restoredSequence 來自產品持久化的 UI state。完整 lifecycle 請參考頁尾的可執行範例。
import { ConversationRunConsumerService } from '@roackb2/heddle-remote'
const consumer = new ConversationRunConsumerService<{ runId: string }>({
retry: { maxAttempts: 6, baseDelayMs: 500, maxDelayMs: 4_000 },
})
consumer.select({ runId }, { afterSequence: restoredSequence })訂閱前呼叫 consumer.subscriptionInput()。每個通過驗證的 event 都要先呼叫 consumer.accept(event) 再 render。Accepted event 會推進 cursor;duplicate 會被忽略;其他 run 的 event 會被忽略;sequence gap 會拋出錯誤;result、cancellation 或 error 會標記 terminal;terminal 之後的 event 會被拒絕。
只有在應用程式成功處理對應事件後,才能保存 subscriptionInput().afterSequence。否則 reload 可能跳過使用者從未看見的資料。
若串流在 terminal 前斷線,請呼叫 consumer.nextRetry()。Heddle 會計算有界 delay 與標準 next cursor。你的應用程式負責實際 timer、abort controller、online/offline 行為與可見重試狀態。成功接受新事件後,attempt budget 會重設。
選用 REST/SSE 客戶端
若伺服器使用慣例 run resource,請匯入 preset:
這是 composition excerpt。StartRunResultSchema 與 CancelRunResultSchema 必須和 server 使用相同的 public schema;accessToken、sessionId、prompt 與名為 subscription 的 AbortController 則來自產品已驗證身分的流程。完整檔案請參考頁尾的 runnable browser client。
import { ConversationRunHttpSseClient } from '@roackb2/heddle-remote/http-sse'
const client = new ConversationRunHttpSseClient({
baseUrl: '/api/agent',
protocol,
accepted: StartRunResultSchema,
cancellation: CancelRunResultSchema,
getHeaders: () => ({ Authorization: `Bearer ${accessToken}` }),
})
const accepted = await client.start({ sessionId, prompt })
await client.subscribe({
runId: accepted.runId,
afterSequence: consumer.subscriptionInput()?.afterSequence,
signal: subscription.signal,
onEvent(event) {
const acceptance = consumer.accept(event)
if (acceptance.accepted) renderProductEvent(event)
},
})Client 負責 URL encoding、header composition、HTTP error decoding、response validation、漸進式 SSE parsing、event identity check、reader cleanup 與 abort propagation,但不負責 reconnect timer 或 UI state。
使用 bearer authentication 的瀏覽器串流要用 fetch,因為原生 EventSource 無法附加 Authorization header。使用 cookie authentication 的產品可以使用 EventSource,讓瀏覽器在重新連線時傳送 Last-Event-ID。
支援範圍
受維護的套件以 JavaScript 與 TypeScript 為目標。非 JavaScript 客戶端可以實作文件中的 wire envelope 與 sequence rule,但 Heddle 目前沒有為其他語言提供官方 client。這些整合應視為調整方案,不是可直接使用的支援。