可執行的傳輸預設
透過 Node HTTP 與 SSE 公開執行狀態
若產品需要慣例 REST run resource 與 server-sent event 串流,請使用 HTTP/SSE 層,避免重新實作 replay cursor、frame、背壓與中斷連線正確性。
客製化深度
傳輸轉接器
託管深度
Node HTTP;Express 參考
文件狀態
可執行參考範例
前置假設
- 長駐的 `ConversationRunService` 已負責 run 生命週期。
- 你的伺服器使用 Node `IncomingMessage` 與 `ServerResponse` 語意。
- 宿主會在解析 run 前驗證身分,並定義嚴格的對外 schema。
- 嚴格解析 replay cursor,且 query 優先於 header。
- 標準 SSE header 與 frame、response 背壓,以及訂閱端 cleanup。
- 訂閱完成或失敗時結束已開啟的事件串流。
- 路由註冊、request 驗證、身分驗證、授權與租戶範圍。
- 對外 payload schema、HTTP status 與錯誤政策、CORS、限制、速率限制與稽核。
- Run 查詢、結果投影、取消政策、部署與傳輸遙測。
慣例 resource
可執行範例使用以下 run endpoint:
POST /api/agent/runs
GET /api/agent/runs/:runId/events?after=<sequence>
POST /api/agent/runs/:runId/cancelStart 會回傳 202 Accepted 與穩定的 runId。訂閱與取消是分開且都需驗證身分的操作。額外的 session read 或 reset endpoint 屬於產品,不是 Heddle transport preset 的一部分。
安裝直接依賴
本頁使用 Heddle Node runtime、browser-safe protocol codec 與 Zod。每個 import 的套件都應直接宣告:
npm install @roackb2/heddle @roackb2/heddle-remote zod匯入 Node helper
import {
parseConversationRunSseReplayCursor,
streamConversationRunSse,
} from '@roackb2/heddle/hosted/http-sse'Helper 接受 Node request 與 response 物件。Express 是受維護且可執行的參考,但套件本身不會註冊路由,也不依賴 Express。
驗證對外邊界
請為 start input、accepted output、公開 activity、公開 result、cancellation 與 error 建立宿主擁有的 schema。使用 ConversationRunProtocolCodec 驗證事件 envelope,並移除不得跨越網路的欄位。
import { z } from 'zod'
import { ConversationRunProtocolCodec } from '@roackb2/heddle-remote'
const PublicActivitySchema = z.object({
type: z.string().min(1),
text: z.string().optional(),
tool: z.string().optional(),
})
const PublicResultSchema = z.object({
outcome: z.string().min(1),
summary: z.string(),
})
const protocol = new ConversationRunProtocolCodec({
activity: PublicActivitySchema,
result: PublicResultSchema,
})不要用 unknown schema 直接傳送內部 ConversationActivity 或 turn result。它們可能包含工具輸入、工具輸出、檔案系統路徑、trace 位置,或遠端呼叫者無權取得的其他資料。
從路由開啟串流
Framework adapter 應先驗證身分、驗證 resource ID、授權 retained handle、解析 replay cursor,最後才開啟串流:
const account = await authenticate(request)
const runId = RunIdSchema.parse(request.params.runId)
const afterSequence = parseConversationRunSseReplayCursor({
query: request.query.after,
lastEventId: request.header('Last-Event-ID'),
})
await streamConversationRunSse({
request,
response,
protocol,
subscribe: (signal) => agent.subscribe({
accountId: account.id,
runId,
afterSequence,
signal,
}),
})明確的 after query 優先於 Last-Event-ID。每個標準 run sequence 會成為 SSE id;event name、ID 與 JSON envelope 會維持一致。Response 背壓會被正確處理,而不是在使用者程式碼中無上限地緩衝串流。
當 request abort 或 response 關閉時,Heddle 會清理該訂閱。底層 run 會繼續執行,直到完成或收到明確取消。
Start 與 cancel 仍是宿主路由
Start handler 會驗證 { sessionId, prompt }、從已驗證身分取得 account scope、呼叫應用服務,並回傳 accepted run。Cancel handler 則在呼叫 cancel() 前授權同一個 retained run。
請把 validation failure、run conflict、missing run 與 internal error 轉換成穩定的公開 status code 與安全 error body。原始 server-side error 應寫入 log,不要回傳未處理的 provider 或 persistence 細節。
正式環境警告
Repository 內的 server 刻意只作本機示範。它會拒絕 NODE_ENV=production,使用固定 bearer token,只監聽 loopback,且省略產品 CORS、rate limit、audit 與 deployment policy。請複製它的生命週期邊界,不要複製其驗證設計。