AgentOS プラグイン
自分で作業する代わりに Agentty のエージェントを通して仕事を進めるプラグイン — その形、守らされる規則、そしてブラウザが要らない理由。
AgentOS は、自分で作業する代わりに Agentty のエージェントを通して仕事を進めるプラグインです。ある分野のスキルと規則 — ブログ、マーケティング、インフルエンサーの 1 週間 — を持っていて、作業を 1 ステップずつそこに通します。実際に書くのはエージェントで、そのセッションは利用者が読むことも、引き取ることもできます。
他のプラグインと同じです。WebAssembly モジュール 1 つ、同じ権限、同じパネル。AgentOS たらしめているのは、その中身と prompt.inject の使い方です。
┌─ プラグイン (.wasm モジュール) ────────────────────────────────┐
│ スキル 各ステップのプロンプト。その分野向けに書かれたもの │
│ 規則 ステップがしてはいけないこと、「完了」の定義 │
│ 進行 次のステップ、各ステップの成果、利用者が承認したもの │
└────────────────────────────────────────────────────────────────┘
│ prompt/inject │ session/get
▼ ▼
Claude Code か Codex のセッション エージェントが書いたもの1 ステップはこうです。エージェントにプロンプトを送り、そのエージェントが作業を止めるまで待ち、成果を読み、判断する。プラグインは状態を storage/* に置くので再起動しても進行が続き、それをパネルに描きます。ステップ一覧、現在位置、返ってきた内容、承認して次へ進むボタンです。
AgentOS は新しいランタイムでも新しい権限でもありません。中身のほとんどがプロンプトであるプラグインの「形」です。
ブラウザが要らない理由#
投稿するプラグインを作る一番わかりやすい方法は、プラグインにブラウザを渡すことです。それでは、すべてのプラグインが利用者のログイン済みセッションを手にしてしまいます。ここではそうしません。
プラグインにはブラウザもネットワークもありません。 代わりにエージェントに頼みます。Agentty はすでにすべてのエージェントセッションに専用のブラウザを与えていて、それは利用者がすでにログインしているブラウザです。エージェントがもともと使えるシェルから操作します。
agentty browser navigate <url> agentty browser text [selector]
agentty browser click <selector> agentty browser type <selector> <text>
agentty browser elements agentty browser screenshot <path.png>つまり投稿するワークフローとは、どのページを開いて何を入力するかを書いたプロンプトを、利用者が見ているセッションに送ることです。プラグインは利用者のログインセッションを一度も渡されずに、ウェブサイトへ投稿できます。
本当に大事な規則 — ログインしない、何もインストールしない、止まって見たものを報告する、読んでいる間に行動しない — はそのプロンプトの一部です。ワークフローはそれらを glossary に 1 か所へまとめ、どのステップもその規則なしには書けないようにします。
Agentty が足すもの#
モジュールには時計もループもありません。メッセージを処理している間しか動きません。残りを可能にするメッセージが 2 つあり、どちらも apiVersion: 2 が必要です。
host/timer — 待つ#
| メソッド | 権限 | params | 結果 |
|---|---|---|---|
host/timer | { ms } | 時間が経つと { elapsedMs } |
あとで応答されるリクエストです。最短 100 ms、最長 1 時間、プラグインごとに同時 8 件まで。停止や再起動をしたプラグインは待っていたものを失います。バックグラウンド実行の手段ではありません。プラグインが得られるのはその応答だけです。
pane/status — エージェントが終わったのを聞く#
| メッセージ | 種別 | params |
|---|---|---|
pane/status | 通知 | { paneId, status, agent, title?, cwd?, running } |
このプラグインが開始したペインの状態が変わったときに送られます。プラグインは prompt/inject の応答({ status: "sent", paneId })でペイン id を知り、Agentty はどのプラグインがどのペインを開始したかを覚えていて、そのプラグインにだけ伝えます。すでに「エージェントの状態を見る」を意味する workspace.read が必要です。同時に最大 32 ペインまで追跡します。
描画されているかどうかに関係なく状態は届きます。他の窓の背後にある窓やロック画面の窓は描画されませんが、エージェントを待っているプラグインが利用者の戻りを待っていてはいけないからです。
利用者が自分で送ったプロンプトも追われます。target: "ask" はまだペインがないのでペイン id なしに { status: "asked" } を返しますが、利用者が選んだセッションも同じように見守られ、その最初の pane/status が、どのペインになったかをプラグインが知る場所です。
書く#
どの AgentOS にも共通する部分は Rust SDK の agentty_plugin::agentos にあります。ワークフローはステップの並びで、ステップはプロンプトと、返ってきたものへの検査と、次に進む前に利用者へ尋ねるかどうかです。
static BLOGGER: Workflow = Workflow {
id: "blogger",
title: "Blog post",
agent: Some("claude"),
// 複数のステップが共有するプロンプト片 — ブラウザ操作の規則、文体など。
glossary: &[],
steps: &[
Step { id: "outline", title: "Outline", prompt: OUTLINE, check: has_headings, approval: Approval::Auto },
Step { id: "draft", title: "Draft", prompt: DRAFT, check: long_enough, approval: Approval::Auto },
Step { id: "edit", title: "Edit", prompt: EDIT, check: no_placeholders, approval: Approval::Ask },
Step { id: "save", title: "Save", prompt: SAVE, check: names_a_file, approval: Approval::Ask },
],
};{input} は実行を始めたときの入力、{step.<id>} は前のステップが出したものです。どちらもワークフローの glossary とともに、プロンプトを送る前に埋められます。
Approval::Auto は次のステップを自動で始め、Approval::Ask は返ってきたものを見せて続けるを待ちます。
進行#
すべての遷移は、プラグインがすでに受け取っているメッセージです。
| こうなると | ランナーがすること |
|---|---|
| 利用者が開始を押す | 最初のステップのプロンプトを target: "newTab" で prompt/inject → paneId を記憶 |
pane/status がそのペインを working と伝える | プロンプトが受け取られたと記録 |
そのあと finished か idle になる | 停止が続くか 2.5 秒確認 |
| まだ止まっている | session/get のあと、そのステップの検査 |
| その 2.5 秒の間に作業へ戻った | さっき言っていたことは答えではなかった — 待ちに戻る |
| 検査を通る | 成果を保存し、見せるか次のステップを送る |
| 検査に落ちる | 足りないものをエージェントに送り返す — 3 回まで、その後は停止して理由を伝える |
| エージェントが何か尋ねる、ペインが消える | 進行を止めて知らせる |
| Agentty が再起動した | storage から進行を読み戻し、セッションに尋ね直す |
2.5 秒の待ちは当てずっぽうではありません。エージェントはツール呼び出しの合間に一瞬アイドルになり、その瞬間にセッションを読むと、文の半分と実行しようとしていたツール名が返ります。それを答えとしたステップは、何も読まないまま次へ進んでしまいます。
working の行があるのも同じ理由です。ペインは開いた瞬間から、エージェントがプロンプトを取り上げるまで idle です。ですから idle だけでは決して「終わった」という意味にならず、そのペインが一度でも working になったのを見てから初めて、停止を信じます。
2 つの規則#
ワークフローが決められないことが 2 つあります。AgentOS は利用者の代わりにエージェントと話すプラグインだからです。
最後のステップの前には、そのステップに何と書いてあっても利用者に尋ねます。 最後のステップは世界に作用するステップです。投稿し、プッシュし、送信します。その直前に利用者が見るものが、そのステップが扱うものです。そこを自動にしたワークフローは、利用者が見ていない間に利用者のアカウントへ書き込むプラグインになります。
誰も読んでいない答えで公開してしまうワークフローは、プラグインの起動時に拒否されます。 Workflow::checked() が、投稿しようとした瞬間ではなく init でそう伝えます。ステップが 2 つ未満、ステップ id の重複、誰も使わない glossary 項目があるときも拒否します。
そしてワークフロー自身が守るべきことが 2 つ。
- 利用者が前にいること。 ステップのプロンプトは、利用者が読めるセッション、引き取れるタブに入ります。
- リンクは実行ではない。 リンクが届いたプラグインはターミナルに入力できず、プロンプトはそのプラグインが動いているあいだずっと**送信先…**を通ります。そうして始まった AgentOS はまず尋ねることになり、利用者が置いたセッションをそのまま追うので、尋ねる代償は 1 ターンだけです。
実例#
どちらもマーケットプレイスのリポジトリに、ソースとチェックサムとともにあります。
Blogger AgentOS — アウトライン、下書き、推敲、保存。約 180 行で、ほとんどがプロンプトです。検査も実際に効くものです。アウトラインに見出し 3 つ、下書きに 300 語、TODO が残っていないこと。自分で書く前に読むならこちらです。権限は prompt.inject、session.read、workspace.read。
Social AgentOS — 3 つのワークフロー。いずれも利用者がログイン済みのブラウザを通して進みます。
| ステップ | |
|---|---|
| X に投稿 | 何が語られているか読む → 下書き → 利用者が読む → 投稿 |
| X で返信 | 参加する価値のある会話を探す → 返信の下書き → 利用者が読む → 送信 |
| Instagram のキャプション | そのアカウントの書き方を読む → キャプション → 利用者が読む → 保存してクリップボードへ |
読んでいないものは投稿せず、ログインせず(ログインや captcha に出くわしたプロンプトは止まって見たものを伝えます)、読んでいる間にいいね・リポスト・返信・フォローをせず、1 回の実行で返信は 5 件までです。Instagram は写真を手で選ぶ必要があるので、最後のステップはそこで止まります。キャプションをファイルに書き、クリップボードに入れ、Instagram を開きます。
要求する権限は prompt.inject、session.read、workspace.read だけです。パスワードを置く場所がありませんが、そもそも持ったことがありません。
まだないもの#
- スキルはモジュールの中にあります。 ステップのプロンプトを利用者が編集できるプラグインは、その編集を
storageに置きます。1 台なら十分ですが、スキル一式を 人どうしで共有するのはプロトコルではなくマーケットプレイスの問題です。 - 複数エージェントの同時実行。
prompt/injectはいくつでもペインを開けますしpane/statusはそれぞれを名指ししますが、「この 3 つで 1 ステップ」と言う手段はありません。プラグインが id を覚えて自分で扱います。 - コスト。 1 回の実行は複数のエージェントセッションです。コストは Agentty の使用量ページで見られますが、プラグインから尋ねることはできません。
- 時刻で走らせる。 実行は利用者が開始を押して始まります。平日の朝ごとに投稿する AgentOS には Agentty 側から始める仕組みが要りますが、
host/timerはプラグインが動いている間しか応答されず、プラグインは Agentty が開いている間しか動きません。
次に#
- Rust と WebAssembly — AgentOS を書く SDK
- プラグイン プロトコル · 権限