Node.js SDK

ハンドラ、呼び出し、パネル UI ビルダー、コンテキストオブジェクト — agentty-plugin.mjs のすべて。

agentty-plugin.mjs は依存関係のない 1 ファイルです。型定義は同じ場所の agentty-plugin.d.ts にあります。プラグインページで開発者ガイドを押すと、両方が ~/.agentty/plugins/.sdk/ に展開されます。

js
import { createPlugin, ui } from './agentty-plugin.mjs';

const plugin = createPlugin();
// ハンドラを登録…
plugin.start();

ハンドラはすべて start() の前に登録してください。

ハンドラ#

すべて async にできます。エラーはログに残り、利用者には通知として表示されます。

ハンドラ呼ばれるとき
onActivate(info => …)プラグインが起動した。infoplugin.dataDirlanguagecontext
command(id, ({ context, args }) => …)パレットやペインバーのボタンからコマンドが実行された
onPanelOpen(context => …)パネルが表示された — ここで描画します
onPanelClose(context => …)パネルが隠れた
onEvent(elementId, (event, context) => …)その id の UI 要素が使われた
onAnyEvent((event, context) => …)onEvent が扱わなかったすべての UI イベント
onContextChange(context => …)フォーカス中のペイン、その状態、フォルダが変わった
onUrl(path, ({ path, query, url }) => …)agentty://plugin/<id>/<path>?… が開かれた
onShutdown(() => …)Agentty がプラグインを停止させる

呼び出し#

すべてプロミスを返します。

呼び出し権限
setPanel(tree) — パネルの内容を差し替える
showPanel() — このプラグインのパネルを開く
notify(message, kind)infosuccesswarningerror
setBadge(text) — タブ領域のボタンに最大 8 文字
getContext()
openUrl(url) — http/https
revealPath(path) — ファイルマネージャーで表示workspace.read
injectPrompt(request)prompt.inject
sendToTerminal({ paneId, text, submit })terminal.write
getSession({ paneId, maxTurns })session.read
listWorkspaces()workspace.read
log(...) — プラグインログ(stderr)に記録

plugin.info には initialize のデータが、plugin.context には最新のコンテキストが入っています。

パネル#

パネルは幅 360px で縦にスクロールします。プラグインがツリーで記述すると Agentty がネイティブに描くため、アプリと見た目が揃いウェブビューも要りません。何かが変わるたびに新しいツリーを送ってください。テキストフィールドは別の value を送らない限り、利用者が入力した内容を保ちます。

ビルダー要素イベント
ui.column(children, { gap }) / ui.row(children, { gap, wrap })レイアウト。gap: nonesmallmediumlarge
ui.section(title, children)見出し付きのまとまり
ui.text(text, style)bodytitlemutedsmallcodeerrorsuccess
ui.button(id, label, { icon, variant, disabled })primarysecondaryghostdangerclick
ui.input(id, { placeholder, value })1 行入力入力が止まると change、Enter で submitevent.value が文字列
ui.list(id, items, { empty }){ id, title, subtitle, detail, icon, tone, actions }event.item を伴う select、行のボタンは event.itemevent.action を伴う action
ui.choice(id, [{ value, label }], value)分割選択値を伴う change
ui.toggle(id, label, value)スイッチ新しい真偽値を伴う change
ui.badge(text, tone)neutralinfosuccesswarningerror
ui.spinner(text)
ui.divider()

null と false の子要素は飛ばされるので、条件 && ui.text('…') がそのまま動きます。

js
plugin.onPanelOpen(async (context) => {
  const notes = await search('');
  plugin.setPanel(
    ui.column([
      ui.input('q', { placeholder: 'Search notes' }),
      ui.list('notes', notes.map((n) => ({
        id: n.path,
        title: n.title,
        subtitle: n.folder,
        icon: 'notebook',
        actions: [{ id: 'insert', icon: 'send', tooltip: 'Insert into the focused pane' }],
      })), { empty: 'No notes yet' }),
    ]),
  );
});

plugin.onEvent('notes', (event, context) => {
  if (event.event === 'action' && event.action === 'insert') {
    return plugin.injectPrompt({ text: read(event.item), target: 'ask' });
  }
});
Note

制限: 要素 2,000 個、深さ 12 階層、テキスト 1 つあたり 20,000 文字。パネルの再描画は最短 50ms 間隔、通知は最短 700ms 間隔です。毎秒 240 件を超えて送るプラグインは暴走とみなされ停止します。

コンテキスト#

すべてのコマンド・イベント・パネル呼び出しには、フォーカス中のウィンドウのコンテキストが付きます。

json
{
  "workspace": { "id": 3, "name": "agentty", "cwd": "/Users/me/agentty", "active": true },
  "pane": {
    "id": 12,
    "kind": "claude",
    "tool": "claude",
    "title": "Claude Code",
    "cwd": "/Users/me/agentty",
    "sessionId": "…",
    "status": "idle",
    "running": true
  },
  "language": "ja"
}

見える範囲は宣言した権限で決まります。フォルダと名前の項目(workspace.cwdworkspace.namepane.cwdpane.title)には workspace.read が、pane.sessionId には session.read が必要です。権限がなければ id、kindtoolstatusrunning、言語だけが残ります。どのペインがフォーカスされているかは分かっても、利用者がどこで作業しているかは分かりません。

kindclaudecodexshell です。他のエージェント CLI は shell ペインで動き、tool が名前を示します。

status意味
idle入力待ち
workingツールを実行中
thinkingターンが開いており、ツール呼び出しの合間
finishedターンが終わった
permission許可を求めている
question利用者に質問している
interrupted利用者が中断した
shell通常のシェル
exitedプログラムが終了した

邪魔しないかどうかを判断するときは workingthinking を同じに扱ってください。

プロンプトを送る#

js
await plugin.injectPrompt({
  text: 'Continue the release checklist.',
  title: 'Release',          // 新規セッションのワークスペース名、ダイアログの見出し
  target: 'ask',             // ask | active | newWorkspace | newTab | pane | workspace
  agent: 'claude',           // 新規セッション用の claude | codex | shell
  cwd: '/Users/me/project',  // 新規セッションのフォルダ
  submit: true,              // Enter を押す(エージェントのみ)
});
  • ask(既定)は**送信先…**を出し、利用者に行き先を選ばせます。
  • active はフォーカス中のペイン、panepaneIdworkspaceworkspaceId に入力し、newWorkspacenewTab はそのプロンプトで新しいセッションを始めます。
  • ターミナルには常に入力されるだけです。injectPrompt が Enter を押すことはありません。
  • 60,000 バイトを超えるプロンプトは ~/.agentty/prompts/ に保存され、そのファイルを読むようエージェントに伝えられます。

他のアプリやリンクから始まるものには ask を使ってください。

セッションとターミナル#

js
const session = await plugin.getSession({ paneId: context.pane.id, maxTurns: 200 });
// { agent, sessionId, title, cwd, status, turnCount, turns: [{ role: 'user' | 'assistant', text }] }

await plugin.sendToTerminal({
  paneId: context.pane.id,
  text: 'Summarize what we did.',
  submit: true,
});

エージェントに入力する前に pane.status を確認してください。workingpermissionquestion は邪魔してはいけません。

次に読むもの#