可執行的傳輸預設

透過 Node HTTP 與 SSE 公開執行狀態

若產品需要慣例 REST run resource 與 server-sent event 串流,請使用 HTTP/SSE 層,避免重新實作 replay cursor、frame、背壓與中斷連線正確性。

客製化深度

傳輸轉接器

託管深度

Node HTTP;Express 參考

文件狀態

可執行參考範例

前置假設

  • 長駐的 `ConversationRunService` 已負責 run 生命週期。
  • 你的伺服器使用 Node `IncomingMessage` 與 `ServerResponse` 語意。
  • 宿主會在解析 run 前驗證身分,並定義嚴格的對外 schema。
Heddle 負責
  • 嚴格解析 replay cursor,且 query 優先於 header。
  • 標準 SSE header 與 frame、response 背壓,以及訂閱端 cleanup。
  • 訂閱完成或失敗時結束已開啟的事件串流。
產品端負責
  • 路由註冊、request 驗證、身分驗證、授權與租戶範圍。
  • 對外 payload schema、HTTP status 與錯誤政策、CORS、限制、速率限制與稽核。
  • Run 查詢、結果投影、取消政策、部署與傳輸遙測。

慣例 resource

可執行範例使用以下 run endpoint:

Text
POST /api/agent/runs
GET  /api/agent/runs/:runId/events?after=<sequence>
POST /api/agent/runs/:runId/cancel

Start 會回傳 202 Accepted 與穩定的 runId。訂閱與取消是分開且都需驗證身分的操作。額外的 session read 或 reset endpoint 屬於產品,不是 Heddle transport preset 的一部分。

安裝直接依賴

本頁使用 Heddle Node runtime、browser-safe protocol codec 與 Zod。每個 import 的套件都應直接宣告:

Shell
npm install @roackb2/heddle @roackb2/heddle-remote zod

匯入 Node helper

TypeScript
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,並移除不得跨越網路的欄位。

TypeScript
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,最後才開啟串流:

TypeScript
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。請複製它的生命週期邊界,不要複製其驗證設計。

權威來源