10/Documentation
durableの通信プロトコル(実験的機能)
Collieとdurable agent間で送受信される内容(開発者向け): ルート、helloとエポック、askのライフサイクルとリクエストID、ソケットおよびトークンによるトランスポート、制限事項、バージョン規則。詳細な仕様についてはWIRE.mdへのリンクを参照してください
実験的。 ワイヤの変更や、機能の削除が今後のマイナーリリースで行われる可能性があります。変更がどのように届くかについてはバージョンと情報通知用helloを参照してください。
durableワイヤは、Collieがdurable agentと通信するためのプロトコルです。このページでは、SDKを使わずにdurable agentを作成する開発者や、SDKの内部動作を把握したい開発者向けに解説します。厳密なルールはWIRE.mdに記載されており、このページと当該ファイルの内容が異なる場合はWIRE.mdが正となります。durable SDKがこれらすべてを自動で処理します。
送受信されるデータ
Collieが接続し、durable agentが待受を行います。各やり取りはJSONボディを伴う1つのHTTPリクエストです。
durable agentはプライベートなUnixソケット、またはURL(トランスポート)のTLS配下で待受を行います。Collieはこれをポーリングします。プッシュ通知、WebSocket、ストリームはありません。すべてのパスは/session/v1で始まります。これは固定の名前であり、バージョンではありません。最初のhelloを除くすべての呼び出しでは、ヘッダーX-Collie-Sessionでバージョンを渡します。リクエストボディは最大64 KiBの1つのJSONオブジェクトであり、空のPOSTボディは{}として読み取られます。
| メソッドとパス | 機能 | 応答上限 |
|---|---|---|
GET /hello | durable agentの名前、バージョン範囲、エポック、準備状態、機能 | 16 KiB |
GET /snapshot | レイアウト情報。ワークスペース、タブ、各ペインの状態、ターン、保留中のask、starterが含まれます。ETagを付与し、変更がない場合は304を返します | 4 MiB |
GET /pane/:id/events?after=N&epoch=E | 指定カーソル以降のペインのイベント | 8 MiB |
POST /pane/:id/answer | askへの回答 | 16 KiB |
POST /pane/:id/send | ペイン向けのテキスト。実行中のターンを誘導するか、次のターンを待機します | 16 KiB |
POST /pane/:id/abort | 実行中のターンを停止 | 16 KiB |
POST /pane/:id/action | ペインのベルトボタンのタップ | 16 KiB |
POST /rename | ワークスペースまたはタブの新しいタイトル | 16 KiB |
POST /close | ペイン、タブ、またはワークスペースを閉じる | 16 KiB |
POST /starter/:id | starterを実行。応答には新しいペイン名が含まれます | 16 KiB |
GET /conversations?after=C&limit=N | durable agentの会話一覧(オープンおよびクローズ)。新しい順 | 1 MiB |
GET /conversation/:id/items?after=C&limit=N | 1つの会話のアイテム一覧。古い順 | 8 MiB |
POST /reopen | クローズされた会話を再表示 | 16 KiB |
すべてのエラーはcodeを含むJSONボディであり、409 staleや404 no-such-paneなどがあります。その他のパスは404 not-foundを返します。上限を超えた応答は読み取られず、応答が途切れたPOSTの結果は不明になります(リクエストID)。
Helloとエポック
helloはdurable agentの識別情報を表し、エポックはCollieが通信しているインスタンスの起動回次を表します。
helloのフィールド | 設定内容 |
|---|---|
service | 厳密にcollie-durable。これが一致しない場合、Collieはdurable agentを別のサーバーと見なし、それ以降の呼び出しを行いません |
protocol | durable agentが対応するワイヤバージョンの範囲({ min, max })。現在は{ min: 1, max: 1 } |
host.name | durable agent自身の名前。表示用テキストのみで最大64バイト |
epoch | durable agentの起動ごとにランダムに新規生成される値。128ビット以上で、再利用不可 |
ready | 起動後にdurable agentがペインを配置している間はfalse |
caps | durable agentが実行可能な操作。各値は真偽値。存在しない機能はfalse |
Collieはまずバージョンヘッダーを付けずにhelloを呼び出し、双方が対応する最も高いバージョンを選択して、以後のすべての呼び出しでそれを送信します。readyがfalseの間は、hello以外のすべてのルートが503 startingを返し、スマートフォン側ではdurable agentが起動中として表示されます。durable agentは、すべての会話が再表示され、すべての未解決のaskが再提起された後にのみreadyを設定するため、再起動によって中途半端なレイアウトが表示されることはありません。
スナップショットのETagは"<epoch>-<rev>"です。閲覧者に見える変更が発生するたびにrevが増加するため、304の処理コストはdurable agent側でほぼゼロです。新しいepochは、durable agentが再起動したことをCollieに伝えます。Collieは新しいスナップショットを読み取るまで、保持しているペインを表示し続けます。その後、各ペインを以前のカーソルで読み取り、1回の応答でペインの現在のビュー全体を取得します。
読み取りが成功した後に1回失敗した場合、そのポーリングでは最後のスナップショットが維持されます。2回連続で失敗すると、durable agentは到達不能として表示されます。プライバシーチェックに失敗したソケットパスは、直ちに到達不能となります。
AskとAnswer
askは質問カードであり、durable agentのaskイベントからask-closedまで未解決のまま開かれます。
| 状態 | 設定者 | 電話側に表示される内容 |
|---|---|---|
| 未解決 | durable agentがaskを提起します。いずれかのaskが開いている間、ペインはwaitingを読み取ります | 「要回答」の下にあるカード。durable agentの名前と、askにexpiresAtが設定されている場合はその残り時間が表示されます |
| 変更済み | durable agentがその場でaskを変更し、revisionが増加します | カードが再描画されます。古いフォームをタップすると409 staleが返され、カードには「質問が変更されました。内容を再度確認してから回答をタップしてください。」と表示されます |
| 回答済み | スマートフォンが回答し、durable agentはby: "phone"でaskを閉じます | カードは消去され、選択肢の指定に従ってツールが実行されるか拒否されます |
| durable agent側で決定済み | durable agent自身がaskを閉じます(decidedBy: "program")。ターンの停止もこれに該当します | Chat内の「durable agent側で決定済み」で始まる行(askのタイトル付き) |
| タイムアウト | 時刻がexpiresAtを過ぎました(decidedBy: "timeout") | Chat内の「回答前にタイムアウトしました」で始まる行(askのタイトル付き) |
決定後に行われたタップは、最初の結果とともに409 decidedを受け取り、何も実行されません。その後、カードには最初に誰が決定したかが表示されます(「durable agent側で決定済み」、「回答前にタイムアウトしました」、または別のタップの後は「回答済みです。このカードは次回の更新で消去されます。」)。durable agentが認識しなくなったaskは200 too-lateを返します。再起動後にdurable agentが同じIDで再度提起する可能性があるため、カードには「処理されませんでした。質問はすぐに再表示される場合があります。」と表示されます。
| ルール | 理由 |
|---|---|
| askのIDはペインIDとツール呼び出しIDのハッシュです | 再起動後に提起された同一のツール呼び出しは同じIDを持つため、再起動をまたいで送信された回答も正常に届きます |
permissionカードはツールの名前と引数から生成され、モデルの文言から生成されることは決してありません | 「はい」を望むモデルは、呼び出しを好意的に説明する傾向があります |
TLS配下のdurable agentはallow-alwaysを提供すべきではありません | トークンのみが認証手段であるため、トークン保持者からの回答が1つあれば、以後のすべての呼び出しが通過してしまいます |
文言が切り詰められたか整理されたカード、またはツール名のないカードでは、スマートフォン側にallow-alwaysが表示されません | ユーザーには、付与された権限の適用範囲が見えません |
expiresAtはタイムゾーン付きのISO 8601形式です | それを過ぎるとaskはtimeoutによって判定され、スナップショットの一覧でもオープン状態として表示されなくなります |
| durable agentは、ペインごとに判定済みの直近500件以上のaskの結果を保持します | 遅れてタップされた場合は、単なるtoo-lateではなくdecidedと最初の結果が返されます |
askにはpermission、choice、text、formの4種類があります。それぞれ応答の形式が異なり、SDKの質問に記載されています。
リクエストID
スマートフォンからの各操作にはリクエストIDが付与されるため、実行は最大1回に限られます。
スマートフォンはインテントごとに1つのリクエストIDを生成し、CollieはそのIDを渡す前に自身のレシートを書き込みます。durable agentは自エポックの直近500件以上のIDを保持します。完了済みIDの再送にはreplayed: trueを付けて最初の応答が再送され、何も実行されません。同じIDでペイロードが異なる場合は409 request-id-reusedが返されます。
Collieが独自にPOSTを再試行することはありません。タイムアウト、接続切断、または5xxが返されたPOSTの結果は不明となります。その場合、回答用のカードには「Not sure the answer arrived. Try again sends it once more.」と表示されます。「Try again」を押すと同一のリクエストIDが送信され、durable agentのメモリが再試行に応答します。
再試行時にaskがすでに閉じられているかターンが遷移していた場合、最初の試行が成功したかどうかを判断できません。その場合、Collieはスマートフォンに「maybe applied」と応答し、カードには「May already be applied. Check the chat.」と表示され、操作が3回送信されることはありません。
トランスポート
durable agentは、Collieが稼働するマシン上のプライベートソケット、またはトークン認証を伴うTLSのいずれかのトランスポートを使用します。
ルート、ボディ、バージョンヘッダー、ETag、エラーコードなど、送受信されるバイトデータはどちらも同一です。異なるのは、どの主体がdurable agentにアクセスできるかという点のみです。
| ソケット経由 | 規則 |
|---|---|
| パス | 絶対パスで、最大103バイトです。双方がCOLLIE_DURABLE_SOCKETから読み取ります |
| モード | ソケットのモードは0600で、モード0700のフォルダー内に配置されます。durable agentはリッスンを開始する前に、古いソケットファイルを削除します |
| パスの巡回 | 接続するたびに、Collieはパスの各構成要素をlstatで検証します。シンボリックリンクは許可されません。ソケットが配置されたフォルダーの所有者は実行ユーザーであり、モードは0700である必要があります。/tmpなどのスティッキービット付きフォルダーを除き、それより上位のフォルダーにグループまたはその他の書き込み権限があってはなりません。ソケットは実行ユーザーが所有し、グループやその他のパーミッションを持ちません |
| 巡回の失敗 | Collieは接続を行わず、socket-not-privateおよび問題のあった箇所を示して、durable agentに到達できない旨を表示します |
| トークン | なし。プライベートソケット自体がゲートとして機能します |
| 実行可能な主体 | 実行ユーザーに属する任意のプロセスです。durable agentは自らの子孫プロセスを403 caller-refusedで拒否できます |
| タイムアウト | hello、snapshot、eventsは1000ミリ秒。answer、send、stop、action、rename、closeは3000ミリ秒。starterは30000ミリ秒です |
| Collie のポーリング間隔 | スマートフォンの監視中は1秒ごと、それ以外は3秒ごとです。停止中のdurable agentにはポーリングごとに1回問い合わせが行われます |
| システム | LinuxおよびmacOS。Windowsにはソケットトランスポートがありません |
| TLS経由 | 規則 |
|---|---|
| URL | https://、またはローカルテスト用のhttp://127.0.0.1:<port>やhttp://localhost:<port>。最大2048バイト、ユーザー名・パスワード・クエリ・フラグメントは不可。Collieがこれに/session/v1を追加します |
| トークン | 32バイト以上のスペースを含まない印刷可能ASCII。Collieはhelloを含むすべてのリクエストでAuthorization: Bearer <token>を送信します |
| durable agentによる検証 | トークンを最優先で検証し、バージョン、hello、readyよりも先に定数時間で比較します。トークンが存在しないか誤っている場合は、何も含めず単に401 { "code": "unauthorized" }を返します |
| リダイレクト | Collieはリダイレクトを追跡しません。3xxはステータスとしてそのまま上位に渡されるため、トークンが別のホストに送信されることはありません |
| ETag | レスポンスを圧縮するプロキシは、タグをW/"<epoch>-<rev>"に弱める場合があります。双方はW/を取り除いて比較します |
| 拒否 | 401または403の場合、durable agentはtoken-refusedで到達不能として表示されます |
| タイムアウト | 読み取りと書き込みにはそれぞれ5000ミリ秒を指定します。スリープしていたdurable agentがコールドスタートする可能性があるためです。スターターには30000ミリ秒を指定します |
| Collie のポーリング間隔 | スマートフォンが表示中の場合は2秒ごと、それ以外は10秒ごと。停止中または拒否されたdurable agentへの問い合わせは、最大でも10秒に1回です |
各タイムアウトは、接続から最後のバイトまでの通信全体に対する期限です。TLS経由の場合、ポーリングはすべてホストの課金対象となる可能性があり、これには304も含まれます。
制限事項
Collieはこれらの制限を超えたものを切断または拒否するため、制限内に収まっているdurable agentでデータが失われることはありません。
| 項目 | 制限 |
|---|---|
| スナップショットあたりのワークスペース数 | 64 |
| スナップショットあたりのタブ数 | 256 |
| スナップショットあたりのペイン数 | 256 |
| ペインあたりの未解決の問い合わせ数 | 50件、古い順 |
permissionのオプション数 | 6 |
choiceおよび選択フィールドのオプション数 | 8 |
formのフィールド数 | 6 |
| スナップショットあたりのスターター数 | 50 |
| ペインあたりのベルトボタン数 | 4個、ラベルは16文字 |
| id | 1〜128バイト。制御文字、空白文字、/、?、#は使用不可 |
| リクエストボディ | 64 KiB。超過時は413 too-large |
| レスポンス | hello 16 KiB、snapshot 4 MiB、events 8 MiB、conversations 1 MiB、items 8 MiB、POST 16 KiB |
| レスポンスあたりのイベント数 | 1000件。処理が遅延したリーダーには現在のビュー(最新の最大2000件)を返却 |
user、reply、thinking、noticeアイテムのテキスト | 64 KiB |
markdownまたはcodeアイテムのテキスト | 32 KiB |
| スマートフォンが送信するテキスト | 32 KiB |
linkアイテム | https://のみ、最大2048文字 |
| 履歴ページ | 100件の会話、または1000件のアイテム |
| 履歴カーソル | 512バイトの印字可能ASCII文字、スペース不可 |
制限長を超えた文字列は切り詰められ、制御文字や不可視文字は削除されます。制限数を超えたリストは先頭のエントリが保持されます。制限サイズを超えた履歴ページは一切読み込まれません。
バージョンと情報通知用hello
現在の通信プロトコルバージョンは1のみであり、Collieは1..1を使用します。
バージョン2のリリース後、Collieは1マイナーリリースの間バージョン1を受け付け、その次のマイナーリリースでサポートを終了します。リーダーは未知のフィールド、イベント種別、アイテム種別、コードを無視します。expiresAtやdecidedByなど、バージョン1内で追加されたフィールドは任意です。新しい必須フィールド、既存フィールドの意味変更、フィールドの削除には新しいバージョンが必要です。
スマートフォンが認識できない種類の問い合わせは、端末から回答できないものとしてボタンなしで表示されます。
2つの範囲が重複しない場合、Collieはdurable agentからの読み込みを停止します。durable agentのエポックごとに1回だけ、Collieは自身のバージョンを含めてhelloを再度送信し、durable agent側で不整合をログに記録できるようにします。durable agentは両方の範囲を指定したrefusalとともに409 incompatibleを返します。これが通知用hello(telling hello)です。
Collieは拒絶した範囲ごとにログへ1行出力し、collie doctorに両方の範囲を表示します。スマートフォンのdurable agent画面にも同様のメッセージ(例:「The durable agent speaks wire v2, this Collie speaks v1. Update Collie, or the agent's SDK.」)が表示されます。古い側を更新してください。
詳細な仕様の記載場所
WIRE.mdがコントラクトであり、すべての形状、コード、ルールを定義しています。
その背景にある設計判断は、リポジトリ内のレコードに記載されています。