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 のベース URL apiKeystringいいえ API キー(Direct Connect モード) customHeadersRecord<string, string>いいえ 独自の HTTP ヘッダー チャネルとファイルの操作
接続の確立以外に、
AsgardServiceClientは次のメソッドを提供します。メソッド シグネチャ 説明 uploadFile(file: File, customChannelId: string) => Promise<BlobUploadResponse>ファイルをアップロードし、返る blob 情報を後続のメッセージに添付できます downloadChannelHomeFile(relativePath: string, customChannelId: string) => Promise<ChannelHomeDownloadResult>そのチャネルの home 配下のファイルをダウンロードします channelMetadata(customChannelId: string) => Promise<ChannelMetadata | null>チャネルの存在を確認します。存在しない場合は nullsuspendChannel(customChannelId: string, options?) => Promise<void>そのチャネルで実行中の run を中止します deleteChannel(customChannelId: string) => Promise<void>チャネルを削除します 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が返り、チャネルに再参加した際にその位置でasgard.message.feedbackイベントとして再生されます。コメントの上限は 8192 文字ではなく 8 KiB
FEEDBACK_COMMENT_MAX_BYTESは8 * 1024で、単位は UTF-8 のバイト数です。日本語や中国語の文字は 3 バイトを占めるため、文字数で検査するとサーバーが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はその 2 つの接頭辞文字列([Response Feedback: Good]/[Response Feedback: Bad])です。これらはバイト単位のプラットフォーム規約で、バックエンドの system prompt がこの文字列そのものを認識し、直前の返信についての差し込みとして扱います。接頭辞を書き換えないでください。エラー処理
@asgard-js/coreは 3 つのエラークラスと、それぞれの type guard を公開しています。いずれもErrorのサブクラスなので、instanceofでも guard でも判定できます。クラス Type guard 発生する状況 HttpErrorisHttpErrorHTTP リクエストが 2xx 以外を返した ChannelBusyErrorisChannelBusyErrorそのチャネルで既に run が実行中 ChannelAwaitingConsentErrorisChannelAwaitingConsentErrorチャネルがツール呼び出しの同意プロンプトで停止している 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 で残高切れかレート制限かを判別します
}
}HttpErrorstatus、statusText、bodyの 3 つの読み取り専用プロパティを持ちます。bodyは解析済みのエラー内容で、実務では同じステータスコードの中で原因を見分ける手掛かりになります。たとえば429のときreason_codeを読んで、残高切れか単なるレート制限かを区別します。bodyはundefinedになり得ます。レスポンスが必ず JSON を伴うとは限らず、解析の失敗は呼び出し側の誤りではなく通常の結果なので、読む前に確認してください。ChannelBusyErrorChannel.sendMessage()は、そのチャネルで既に run が実行中のときこのエラーで reject します。runKindプロパティがどの種類の run が占有しているかを示します。run はサーバー側でバックグラウンド実行されるため、もう一つ送ると同じ対話記録に 2 つの run が書き込むことになります。黙って破棄せず明確に拒否することで、呼び出し側はメッセージが送られていないと分かります。
SDK 内蔵の入力欄は既にこの状況を防いでいるため、実際にこのエラーが出るのはプログラムからメッセージを送る経路だけです。
ChannelAwaitingConsentErrorChannel.sendMessage()とChannel.nudge()は、チャネルがツール呼び出しの同意プロンプトで停止しているときこのエラーで reject します。サーバーはそのチャネルを一時停止状態に保ち、同意への回答だけが再開させます。それ以外のターンは nudge も含めて拒否されます。この一時停止は「run 実行中」の判定では検出できません。同意のフレームは run の終了後に届くため、プロンプトが画面に出た時点で run は既に終わっており、チャネルはアイドルに見えます。まず
Channel.replyToolCallConsents()で回答し、その後に次のターンを送ってください。processIdプロパティは停止中のバッチの process id ですが、既存チャネルへの再参加時は空文字列になります。リロード後に復元されたプロンプトはバックエンドが永続化された停止状態から再構築し、その経路では process id を意図的に空にしています。同意による停止かどうかはプロンプトの有無で判断するためです。診断の手掛かりとして扱い、キーには使わないでください。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はPromiseではなく RxJS のObservableを返します。購読するとそのパスの変更イベントを受け取り続けるので、不要になったら unsubscribe してください。generateSandboxBrowserOpenUrl(sandboxName)はブラウザでその Sandbox を開く URL を生成します。resolveSandboxUri(uri)は Sandbox URI をSandboxUriIntentに解析し、解析できない場合はnullを返します。reconcileLaunched(metadata)は起動済み Sandbox の一覧を整理します。React 側でこれらを直接呼ぶ必要はありません。
createSandboxFsProvidersがファイルエクスプローラーの求める形にまとめます。React SDK を参照してください。SourceSet
AsgardSourceSetClientは SourceSet の内容にアクセスします。関連する定数は次のとおりです。定数 値 説明 SOURCE_SET_MAX_PAGE_SIZE10001 リクエストあたりの最大件数 SOURCE_SET_DEFAULT_MAX_ENTRIES10000取得件数の既定の上限 SOURCE_SET_VOLUME_ROOT''volume のルート。 /ではなく空文字列ですassertVolumePath(path, options?)はパスの妥当性を検証し、不正な場合はエラーを送出します。ルートが空文字列である点は誤りやすいので、自前で比較せずこの関数を使ってください。イベントと派生データ
fetchSse(payload, options?)は SSE 接続を確立し、RxJS のSubscriptionを返します。rejoinSse(customChannelId, options?)は既存チャネルに再参加し 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)2 つの一覧を比較し、不要な再描画を避けます Headless の派生状態(タスク / サブエージェント / タイトル)
0.3.x 以降、
@asgard-js/coreはフレームワーク非依存の派生状態 API も提供します。headless、Vue、Svelte などの利用者が<Chatbot>の外でタスク一覧、サブエージェント一覧、対話タイトルを描画できます。Channel上のリアクティブな store:channel.tasks$、channel.subagents$、channel.channelTitle$(該当部分が変化したときだけ発行する RxJS のObservable)と、対応するスナップショット取得のgetTasks()/getSubagents()/getChannelTitle()。conversation$から自分で構築する:createDerivedStores(conversation$)は{ tasks$, subagents$, getTasks, getSubagents, teardown }を返します。一度きりの導出にはderiveTasks(conversation)/deriveSubagents(conversation)を使います。- 履歴を消さずに既存チャネルへ参加する:
Channel.restore(config, options?)はまずclient.channelMetadata()(GET /channel/metadata)でチャネルの存在を確認し、存在すればRESET_CHANNELを送らずにclient.rejoinSse()でサーバーの transcript を再生します。
React での利用React では
@asgard-js/reactのuseTaskList/useSubagents/useChannelTitleフックを使ってください。上記の store をuseSyncExternalStoreに橋渡し済みです。Sandbox ブラウザー
Sandbox 内で動くブラウザーは、新しいタブを開かずに自分の画面へ埋め込んで操作できます。
createSandboxBrowserSession(sandboxName)はPOST {botProviderEndpoint}/sandbox/{sandbox_name}/browser/sessionからストリーミング用の資格情報を取得し、{ wsUrl, token }を返します。それをcreateSandboxBrowserTransportに渡して接続すると、画面も入力もその WebSocket を通ります。これは既存の
generateSandboxBrowserOpenUrl(sandboxName)の置き換えではなく、もう 1 つの経路です。あちらは新しいタブで開くための一度きりの URL を返し、フォールバックとして残っています。こちらは SDK がページ内で描画するためのソケットを返します。リモートデスクトップのプロトコルはブラウザーの
KeyboardEvent.keyではなく X11 keysym を使うため、core は変換ユーティリティも公開しています。エクスポート 役割 KEYSYMよく使うキーの keysym 定数 charToKeysym(char)1 文字を keysym へ keyToKeysym(key)KeyboardEvent.keyを keysym へmapModifierKeysym(...)Shift/Ctrl/Alt などの修飾キーの処理 isPrintableKeysym(keysym)印字可能な文字かどうか ホイールイベントは単位がブラウザーごとに違うので
normalizeWheel(event)で正規化します。WHEEL_MAXは 1 回のスクロール量の上限、WHEEL_THROTTLE_MSは送信の間隔です。リモートカーソルのフレームはdecodeCursorFrameでデコードします。React SDK の
SandboxBrowserPanelを使う場合、これらの低レベルユーティリティに直接触れる必要はありません。React SDK を参照してください。インタラクティブなデモ
@asgard-js/coreの headless での使い方とイベント処理の例: SDK Demo — Headless関連ドキュメント
- フロントエンド SDK ガイド — 統合パターンの選択と全体の手順
- React SDK リファレンス —
@asgard-js/reactのコンポーネント