一覧へ

TECHNO.0022 / 2026.10.09 FRI / 1 MIN READ

Tenoma:人間の操作を邪魔しないAIエージェントの設計

AIにカーソルを奪わせない。複数カーソルとリモート操作を実現するデスクトップエージェントの開発記録。

Tenoma は、AIエージェントがデスクトップを操作している最中でも、人間が自分の作業を続けられるようにする試みだ。最初に決めた原則はひとつだけ。「AIにカーソルを奪わせない」。

カーソルはひとつしかない

OSのポインタは基本的にひとつだ。エージェントがクリックしている間、人間は手を止めるしかない。これでは、任せている時間がそのまま待ち時間になる。

設計: 仮想カーソルと操作キュー

エージェントには仮想カーソルを渡し、実際の入力はキューに積む。人間の入力があれば、エージェントの操作は即座に一時停止する。

src/input/queue.ts
type Actor = 'human' | 'agent'

type InputEvent = Readonly<{
  actor: Actor
  kind: 'move' | 'click' | 'key'
  at: number
}>

// 人間の入力が来たら、エージェントの未実行の操作をすべて保留にする
export function schedule(queue: readonly InputEvent[], incoming: InputEvent): readonly InputEvent[] {
  if (incoming.actor === 'human') {
    return [incoming, ...queue.filter((e) => e.actor === 'human')]
  }
  return [...queue, incoming]
}

保留した操作は、人間の入力が 1.5 秒途切れたら再開する。この 1.5 秒は、実際に使いながら決めた値で、根拠はまだ体感だけだ。

横に長い設定

設定ファイルは横に長くなりがちなので、コードブロックは横スクロールで読めるようにしている。

tenoma.config.json
{
  "pauseOnHumanInput": true,
  "resumeAfterIdleMs": 1500,
  "virtualCursor": { "color": "#9cfb27", "trail": false, "label": "agent", "hideWhenHumanIsActive": true, "maxSpeedPxPerFrame": 48 },
  "allowList": ["com.apple.Safari", "com.google.Chrome", "com.microsoft.VSCode", "com.figma.Desktop", "md.obsidian", "com.mitchellh.ghostty", "com.tinyspeck.slackmacgap"]
}

リモート操作

別のマシンからエージェントの様子を見られるように、画面ではなく操作ログを送る。映像を送るより軽く、あとから再生もできる。

BASH
# 操作ログを手元で再生する
tenoma replay ./logs/2026-10-09.jsonl --speed 2 --from 00:12:30
仮想カーソル(ライム)と人間のカーソル。重なっても人間が優先される。

次にやること

  • 再開までの待ち時間を、作業の種類ごとに変える
  • 操作ログに「なぜその操作をしたか」を残す
  • 複数エージェントのカーソルを色で区別する