REFERENCE
開發者參考
認證、API、SDK、Flow Agent 處理節點與參數取值的完整參考。
JavaScript SDK (@asgard-js/core)
@asgard-js/core JavaScript SDK 參考文件
@asgard-js/core 是框架無關的 JavaScript SDK,處理 SSE 串流連線、Channel 管理與 Conversation 狀態。適用於 Node.js、瀏覽器,以及不使用 @asgard-js/react 元件的場景(Headless、Vue、Svelte 等)。
若你用 React,通常不需要直接使用 @asgard-js/core。改用 @asgard-js/react 的 <Chatbot> 元件即可,詳見 React SDK 參考文件。
安裝
npm install @asgard-js/core核心概念
類別 說明 AsgardServiceClient連線設定(botProviderEndpoint、apiKey、customHeaders) Channel一個對話頻道,透過 SSE 與 Bot Provider 通訊 Conversation儲存訊息狀態的容器 基本使用
import { AsgardServiceClient, Channel, Conversation } from '@asgard-js/core';
const client = new AsgardServiceClient({
botProviderEndpoint:
'https://api.asgard-ai.com/ns/{namespace}/bot-provider/{botProviderId}',
});
const conversation = new Conversation({ messages: new Map() });
const channel = await Channel.reset({
client,
customChannelId: 'channel-123',
conversation,
statesObserver: (states) => {
console.log('isConnecting:', states.isConnecting);
console.log(
'Messages:',
Array.from(states.conversation.messages.values()),
);
},
});
await channel.sendMessage({ text: '你好!' });AsgardServiceClient設定參數 型別 必填 說明 botProviderEndpointstring是 Bot Provider 的 base URL apiKeystring否 API 金鑰(Direct Connect 模式) customHeadersRecord<string, string>否 自訂 HTTP header 通道與檔案操作
AsgardServiceClient除了建立連線之外,還提供這些方法:方法 簽章 說明 uploadFile(file: File, customChannelId: string) => Promise<BlobUploadResponse>上傳檔案,回傳的 blob 資訊可以帶進後續訊息 downloadChannelHomeFile(relativePath: string, customChannelId: string) => Promise<ChannelHomeDownloadResult>下載該 channel home 底下的檔案 channelMetadata(customChannelId: string) => Promise<ChannelMetadata | null>查詢 channel 是否存在,不存在回傳 nullsuspendChannel(customChannelId: string, options?) => Promise<void>中止該 channel 上正在執行的 run deleteChannel(customChannelId: string) => Promise<void>刪除 channel detach(options: { timeoutMs: number }) => void停止接受新請求,等進行中的請求結束後才真正關閉 close() => void立即關閉 detach和close的差別在於進行中的請求。close直接關掉;detach會標記為不再接受新請求,若當下沒有進行中的請求就立即關閉,否則等它們結束,適合頁面離開前的收尾。這些方法都需要設定
botProviderEndpoint,因為各自的端點是從它推導出來的。沒設定時會丟出說明是哪個端點推導失敗的錯誤。訊息回饋
使用者對某一則回覆按讚或倒讚,透過
sendMessageFeedback送出:import {
FEEDBACK_COMMENT_MAX_BYTES,
feedbackCommentByteLength,
composeFeedbackMessage,
} from '@asgard-js/core';
await client.sendMessageFeedback({
customChannelId,
messageId, // 被評價的那則回覆的 messageId
verdict: 'BAD', // 'GOOD' | 'BAD'
comment: '答案跟我問的不是同一件事',
});回傳的
messageId是這筆回饋自己的 id,和被評價的那則回覆不同,另外還有 transcript 的seq,重新加入 channel 時會在該位置重播成asgard.message.feedback事件。留言長度上限是 8 KiB 而不是 8192 字元
FEEDBACK_COMMENT_MAX_BYTES是8 * 1024,單位是 UTF-8 位元組。中文一個字佔三個位元組,所以用字元數檢查會讓一則伺服器會用400拒絕的留言通過。要用feedbackCommentByteLength(comment)量:if (feedbackCommentByteLength(comment) > FEEDBACK_COMMENT_MAX_BYTES) {
// 提示使用者縮短
}把回饋一併說給 AI 聽
composeFeedbackMessage(verdict, comment?)組出要送進對話的後續訊息:開頭是該 verdict 對應的前綴,使用者有留言時空一行後接原文。composeFeedbackMessage('BAD', '答案跟我問的不是同一件事');
// "[Response Feedback: Bad]\n\n答案跟我問的不是同一件事"RESPONSE_FEEDBACK_PREFIX是那兩個前綴字串([Response Feedback: Good]/[Response Feedback: Bad])。它們是逐位元組的平台約定:後端的 system prompt 認得的就是這兩個字串,據此讓 agent 把這則訊息當成對前一則回覆的插話,簡短回應、必要時調整,然後繼續對話。不要自己改寫這段前綴。錯誤處理
@asgard-js/core匯出三個錯誤類別與各自的 type guard。它們都是Error的子類別,用instanceof或 guard 判斷皆可。類別 Type guard 何時丟出 HttpErrorisHttpErrorHTTP 請求回傳非 2xx ChannelBusyErrorisChannelBusyError該 channel 上已有一個 run 在執行中 ChannelAwaitingConsentErrorisChannelAwaitingConsentErrorchannel 停在工具呼叫的同意提示上 import {
isHttpError,
isChannelBusyError,
isChannelAwaitingConsentError,
} from '@asgard-js/core';
try {
await channel.sendMessage({ text: input });
} catch (error) {
if (isChannelAwaitingConsentError(error)) {
// 先回答同意提示,這一輪才送得出去
} else if (isChannelBusyError(error)) {
// 上一輪還沒結束,例如把送出按鈕維持在停用狀態
} else if (isHttpError(error) && error.status === 429) {
// 依 error.body 判斷是額度用完還是速率限制
}
}HttpError帶著
status、statusText與body三個唯讀屬性。body是解析後的錯誤內容,實務上是分辨同一個狀態碼底下不同原因的依據,例如以body裡的reason_code區分 429 是額度用盡還是單純的速率限制。body有可能是undefined:回應不一定帶得動 JSON,解析失敗是正常結果而不是呼叫端的錯,所以讀之前要先確認。ChannelBusyErrorChannel.sendMessage()在該 channel 已有 run 執行中時會 reject 這個錯誤,屬性runKind說明是哪一種 run 佔著。run 在伺服器端是背景執行的,再送一個等於讓兩個 run 同時寫同一份對話記錄,所以這裡是明確拒絕而不是靜默丟棄,呼叫端才知道訊息根本沒送出去。
SDK 內建的輸入框已經自己擋掉這種情況,所以這個錯誤實際上只會出現在你用程式送訊息的路徑上。
ChannelAwaitingConsentErrorChannel.sendMessage()與Channel.nudge()在 channel 停在工具呼叫同意提示時會 reject 這個錯誤。伺服器把這種 channel 保持在暫停狀態,只有回覆同意才能繼續,其他任何一輪都會被拒絕,nudge 也一樣。這個暫停狀態偵測不到「run 執行中」:同意提示是在 run 結束之後才送達的,所以提示出現在畫面上時 run 早就結束,channel 看起來是閒置的。先用
Channel.replyToolCallConsents()回答,再送下一輪。屬性
processId是暫停批次的 process id,但重新加入既有 channel 時它會是空字串。重新載入後恢復的提示由後端從持久化的暫停狀態重建,那條路徑刻意不填 process id,因為判斷是否處於同意暫停要看提示存不存在,不是看這個 id。把它當診斷線索用,不要當 key。Sandbox 檔案系統
Agent 執行時掛載的 Sandbox 有一組檔案操作 API,全部以
sandboxName加路徑定位:方法 簽章 sandboxFsList(sandboxName, path) => Promise<SandboxFsListResult>sandboxFsStat(sandboxName, path) => Promise<SandboxFsStatResult>sandboxFsRead(sandboxName, path, options?) => Promise<SandboxFsReadResult>sandboxFsWrite(sandboxName, path, ...) => Promise<void>sandboxFsMkdir(sandboxName, path) => Promise<void>sandboxFsRemove(sandboxName, path) => Promise<void>sandboxFsRemoveAll(sandboxName, path) => Promise<void>sandboxFsCopy(sandboxName, src, dst, ...) => Promise<void>sandboxFsMove(sandboxName, src, dst, ...) => Promise<void>sandboxFsWatch(sandboxName, path) => Observable<SandboxFsWatchEvent>sandboxFsWatch回傳的是 RxJSObservable而不是Promise,訂閱後持續收到該路徑的變動事件,不用時要自行 unsubscribe。generateSandboxBrowserOpenUrl(sandboxName)產生可在瀏覽器開啟該 Sandbox 的網址。resolveSandboxUri(uri)把 Sandbox URI 解析成SandboxUriIntent,無法解析時回傳null。reconcileLaunched(metadata)整理已啟動的 Sandbox 清單。React 端不必直接呼叫這些方法,
createSandboxFsProviders會把它們包成檔案總管需要的形狀,詳見 React SDK。SourceSet
AsgardSourceSetClient存取 SourceSet 的內容。相關常數:常數 值 說明 SOURCE_SET_MAX_PAGE_SIZE1000單次請求的筆數上限 SOURCE_SET_DEFAULT_MAX_ENTRIES10000預設最多取回的筆數 SOURCE_SET_VOLUME_ROOT''volume 根路徑,是空字串而不是 /assertVolumePath(path, options?)驗證路徑合法,不合法時丟出錯誤。根路徑是空字串這點容易寫錯,用這個函式檢查而不要自己比對。事件與衍生資料
fetchSse(payload, options?)建立 SSE 連線,回傳 RxJSSubscription。rejoinSse(customChannelId, options?)重新加入既有 channel 並重播 transcript。handleEvent(response)把單一 SSE 事件餵進 client 的內部狀態,通常由 SDK 自己呼叫。從事件推導任務與子代理的工具:
函式 用途 isTaskTool(call)/isAgentTool(call)判斷一次工具呼叫是否為任務 / 子代理 isSubagentChildTool(parentToolUseId?)判斷是否為子代理底下的工具呼叫 reduceTaskEvents(events)把 TaskToolEvent[]收斂成Task[]reduceSubagents(events)把 SubagentEvent[]收斂成Subagent[]conversationToSubagentEvents(messages)從對話訊息萃取子代理事件 tasksEqual(a, b)/subagentsEqual(a, b)比較兩份清單是否相同,用於避免不必要的重繪 Headless 衍生狀態(Task / Subagent / 標題)
0.3.x 起,
@asgard-js/core額外提供一組框架無關的衍生狀態 API,讓 Headless / Vue / Svelte 等消費端能在<Chatbot>之外自行渲染任務清單、子代理清單與對話標題:Channel上的響應式 store:channel.tasks$、channel.subagents$、channel.channelTitle$(RxJSObservable,只在該切片變動時發射),以及對應的快照 gettergetTasks()/getSubagents()/getChannelTitle()。- 從
conversation$自行建立:createDerivedStores(conversation$)回傳{ tasks$, subagents$, getTasks, getSubagents, teardown };或用deriveTasks(conversation)/deriveSubagents(conversation)做一次性推導。 - 加入既有 channel(不清空歷史):
Channel.restore(config, options?)會先透過client.channelMetadata()(GET /channel/metadata)判斷 channel 是否存在,存在則重播 server transcript(client.rejoinSse())而非送出RESET_CHANNEL。
React 消費端使用 React 時,直接用
@asgard-js/react的useTaskList/useSubagents/useChannelTitlehooks,它們已把上述 store 橋接進useSyncExternalStore。Sandbox 瀏覽器
Sandbox 裡跑的瀏覽器可以嵌進你自己的畫面直接操作,不必另開分頁。
createSandboxBrowserSession(sandboxName)向POST {botProviderEndpoint}/sandbox/{sandbox_name}/browser/session取得一組串流憑證,回傳{ wsUrl, token }。拿到之後交給createSandboxBrowserTransport建立連線,畫面與輸入都走這條 WebSocket。這跟既有的
generateSandboxBrowserOpenUrl(sandboxName)是兩條路,不是取代關係:後者給你一個一次性網址讓使用者在新分頁開啟,仍然保留作為退路;前者給你一個 socket,由 SDK 在你的頁面裡自己渲染。遠端桌面協定用的是 X11 keysym,不是瀏覽器的
KeyboardEvent.key,所以 core 也匯出一組轉換工具:匯出 用途 KEYSYM常用按鍵的 keysym 常數表 charToKeysym(char)單一字元轉 keysym keyToKeysym(key)KeyboardEvent.key轉 keysymmapModifierKeysym(...)處理 Shift/Ctrl/Alt 這類修飾鍵 isPrintableKeysym(keysym)判斷是不是可列印字元 滾輪事件各瀏覽器的單位不一致,
normalizeWheel(event)把它正規化;WHEEL_MAX是單次滾動量的上限,WHEEL_THROTTLE_MS是送出的節流間隔。遠端游標的影格用decodeCursorFrame解碼。這些低階工具在你用 React SDK 的
SandboxBrowserPanel時都不必自己碰,詳見 React SDK。互動式 Demo
@asgard-js/core的 Headless 用法與事件處理範例:SDK Demo — Headless延伸閱讀
- 前端 SDK 使用指南 — 模式選型與完整整合說明
- React SDK 參考文件 —
@asgard-js/react元件說明