機能エージェント

作成

エージェントを定義し、リリースを作成して有効化し、セッションに紐付ける

エージェントはワークスペース内の名前付きの定義である。名前と説明は人が見る値で、実行に使われる定義はカード (モデル、ツール) である。カードを修正するたびに新しいバージョンが積み重なる。バージョン 1 つをプロファイルと紐付けたものがリリースであり、エージェントが指しているリリースが有効なリリースである。セッションを作成するときにリリースを指定すると、そのセッションはそのエージェントのものになる。

セッションは profile_id のプロファイルで動き、リリースから作ったセッションはそのリリースに固定されたツールだけを使う。名前と説明はモデルに渡されず、エージェントの仕事の進め方はリポジトリの AGENTS.md か CLAUDE.md で伝える。詳しくは 指示文と説明 と 権限 を参照。

Web コンソールで

ローカルスタックの Web コンソールは http://127.0.0.1:3000/ である。

  1. 初回のみ、/bootstrap で最初の owner とワークスペースを作成する。最初の owner のトークンの受け取り方は設置方式によって異なる。単一サーバー設置でトークンを自分で設定していない場合は、scripts/test-ops.sh bootstrap-token が一度だけ表示する。詳しくは最初の owner と keyを参照する。
  2. ログイン後、左のサイドバーの「エージェント」を開き、右上の「新規エージェント」を押す。「新規エージェント」画面が 5 つのステップ(名前 → 役割 → リリース → 有効化 → チャンネル接続)で開く。
  3. 「名前」ステップで名前を入力する。識別子 (slug) は名前から自動で入力され、変更もできる。必要なら「説明(任意)」に人が読む紹介を書く。「次へ」を押す。
  4. 「役割」ステップでモデルを選び、「使用できるツール」でプロファイルのツールをチェックで選んで (指示文と説明)、回答言語を決め、「下書きを保存して次へ」を押す。エージェントが下書きとして保存され、バージョン 1 が作成される。動作の指示は、リポジトリの AGENTS.md か CLAUDE.md に書く。
  5. 「リリース」ステップでプロファイルを選び、「リリースを作成」を押す。カードのモデルがプロファイルで実行できるモデルのひとつでなければリリースは作成できない。
  6. 「有効化」ステップで「エージェントを有効化」を押す。以降、新しいセッションはこのリリースで開始する。「あとで設定」を押すと、エージェントは下書きのまま詳細画面に移る。

owner・admin・member のいずれも、エージェントを作成できる。作成した人がそのエージェントの管理者になる。

API で

エージェント API は、Web コンソールと同じ cookie ログインでのみ呼び出せる。API キーで呼び出すと 403 になる。

ログイン

read -rs WERFT_PASSWORD
WERFT_COOKIE=$(curl -sS -o /dev/null -D - "$API/v1/auth/login" \
  -H 'Content-Type: application/json' -H 'X-Requested-With: werft-web' \
  -d "$(jq -nc --arg e 'owner@example.test' --arg p "$WERFT_PASSWORD" '{email: $e, password: $p}')" \
  | sed -n 's/^[Ss]et-[Cc]ookie: \(__Host-werft_session=[^;]*\).*/\1/p')

ログイン cookie は名前が __Host- で始まり Secure なので、http では curl の cookie jar に保存されない。そのため応答ヘッダーから取り出し、Cookie ヘッダーで送る。状態を変更するリクエストには X-Requested-With: werft-web が必要である。

WEB=(-H 'Content-Type: application/json' -H 'X-Requested-With: werft-web' -H "Cookie: $WERFT_COOKIE")
curl -sS "$API/v1/auth/me" "${WEB[@]}" | jq '{role: .principal.role, workspace: .workspace.slug}'

作成

エージェントには担当チームが必ず必要である。owner・admin は GET /v1/teams のどのチームでも、それ以外の人は GET /v1/my-teams に出る自分のチームを選ぶ。以下では owner としてチームを一つ作って使う(同じ名前のチームがすでにあると 409 なので、そのチームの id を使う)。

TEAM=$(curl -sS "$API/v1/teams" "${WEB[@]}" -d '{"name": "Research"}' | jq -r .team.id)
agent=$(curl -sS "$API/v1/agents" "${WEB[@]}" -d '{
  "slug": "research-bot",
  "name": "Research Bot",
  "description": "リポジトリを読んで質問に答える",
  "managing_team_id": "'"$TEAM"'",
  "card": {
    "model": "claude-sonnet-5",
    "capabilities": { "tools": ["Read", "Grep", "Glob"], "mcp": null }
  }
}')
AID=$(echo "$agent" | jq -r .agent.id)
VID=$(echo "$agent" | jq -r '.versions[0].id')
echo "$agent" | jq '{slug: .agent.slug, version: .versions[0].version_no, active_release_id: .agent.active_release_id}'
{
  "slug": "research-bot",
  "version": 1,
  "active_release_id": null
}

同じワークスペースに同じ slug があると 409 ALREADY_EXISTS になる。作成しただけでは何も有効化されない。

managing_team_id に入れたチームが担当チームになる。省くと 400 BAD_REQUEST で、error.details.issues に {"path": "managing_team_id", "message": "managing_team_id is required"} が入る。owner・admin が存在しないチームを入れると 404 である。それ以外の人が自分の所属しないチームを入れると、存在しないチームでも 403(details.reason_code: team_not_allowed)である。そのため、所属するチームのない member はエージェントを作成できない。レスポンスの agent.managing_team_name はそのチームの名前である。

すべてのエージェントには担当チームが一つある。担当チームは PUT /v1/agents/{id}/managing-team で変えられるだけで、空にはできない。担当チームが必須になる前に作ったエージェントには、マイグレーションが担当チームを入れた。担当チームのなかったエージェントは、すべてワークスペースごとに一つ作った、メンバーのいない Unassigned agents チームが担当になった(同じ名前のチームがすでにあれば、Unassigned agents (1a2b3c4d) のように後ろに 8 文字の識別子が付く)。そのチームは普通のチームと同じで、名前を変えたり、エージェントを別のチームへ移したりできる。

リリースの作成

リリースはバージョン 1 つとプロファイル 1 つを紐付ける。カードの model がプロファイルで実行できるモデル (model と models) にない場合や、カードのツールがプロファイルにない場合は 422 になる。リリースで作成したセッションは、model を省略するとカードのモデルで動く。

release=$(curl -sS "$API/v1/agents/$AID/releases" "${WEB[@]}" \
  -d "$(jq -nc --arg v "$VID" '{version_id: $v, runtime_profile_id: "claude-coding-local"}')")
REL=$(echo "$release" | jq -r .release.id)
echo "$release" | jq '{id: .release.id, created, tools: .release.effective_tools.tools}'
{
  "id": "sha256:…",
  "created": true,
  "tools": [
    "Read",
    "Glob",
    "Grep"
  ]
}

リリース id はカードとプロファイルの内容のハッシュである。同じ内容で再度作成すると、同じリリースが created: false で返る。

リクエストに note(前後の空白を除いて 1–200 文字)を入れると、リリースにメモとして残る。メモは id に含まれないため、同じ内容で再度作成しても最初のメモのままである。空、または 200 文字を超えると 400 になる。

人がリリースを呼ぶ名前は label である。作成した時刻をソウル時間で秒まで書いた 2026-10-30_14:30:28 の形で、同じエージェントで同じ秒にリリースが複数できると、2 つ目から -2、-3 が付く。コンソールのリリース履歴、エージェントの見出しタグ、セッション一覧はこの名前を使う。識別子は引き続きリリース id である。カードの version_no はカードを変えたときだけ上がるため、同じカードを別のプロファイルでリリースしてもバージョンは同じである。

コンソールのリリース履歴は、リリースごとに同じエージェントで直前に作ったリリースと比べて変わったもの(許可リポジトリ、モデル、ツール・MCP、プロンプト、設定値)を、作成者と時刻とともに示す。認証情報は値を出さず「変更あり」とだけ示す。セッション画面はそのセッションが使うリリースを示し、エージェントの有効なリリースでなければその旨を示す。セッションは開始時のリリースを使い続けるため、変わった設定は新しいセッションから使われる。セッション応答の agent.release_is_active がこの比較結果である。

有効化

expected_revision はエージェントの現在の activation_revision である。初回は -1 である。他の場所で先に変更されていた場合は 409 REVISION_CONFLICT になる。

curl -sS "$API/v1/agents/$AID/activate" "${WEB[@]}" \
  -d "$(jq -nc --arg r "$REL" '{release_id: $r, expected_revision: -1}')" \
  | jq '{activation_revision, previous_release_id}'
{
  "activation_revision": 0,
  "previous_release_id": null
}

リリースの activated_at はそのリリースが最後に有効化された時刻、deactivated_at は次の有効化がその座を引き継いだ時刻である。いま有効なリリースの deactivated_at と、一度も有効化されていないリリースの両方の値は null である。どちらも有効化の記録から読むため、これらのフィールドができる前に有効化したリリースにも値がある。

一覧と件数

curl -sS "$API/v1/agents?limit=50" "${WEB[@]}" | jq -c '{n: (.items | length), next_cursor}'
curl -sS "$API/v1/agents/counts" "${WEB[@]}" | jq -c
{"n":1,"next_cursor":null}
{"counts":{"all":1,"administered":1,"active":1,"draft":0}}

一覧は古いエージェントから limit 件ずつ(既定 50、最大 100)返る。レスポンスの next_cursor を cursor として送ると次のページになり、最後のページでは null である。cursor なしで呼ぶと最初のページだけが返る。q(最大 200 文字)を渡すと名前か識別子にその文字列を含むエージェントだけが大文字小文字を区別せずに返り、ids にエージェント ID をカンマでつないで(最大 100 件)渡すとそのエージェントだけが返る。runtime_profile_id を渡すと、有効な release がそのプロファイルで動くエージェントだけが返り、有効な release のないエージェントは除かれる。いずれも同じ cursor で次のページを読む。GET /v1/agents/counts は、一覧を最後まで読んだときの件数(all)、そのうち自分が直接またはチームで管理している件数(administered)、有効な release で動いている件数(active)、アーカイブされていないがまだ有効な release のない下書きの件数(draft)を返す。どちらも一覧と同じ権限が必要で、API キーは拒否される。

コンソールの担当チーム

担当チームはエージェントを受け持つチームである。そのチームのチーム管理者はエージェントを管理し、メンバーは設定を見て使う(権限)。

  • 新しいエージェント: 最初のステップで担当チームを必ず選ぶ。owner・admin はすべてのチームから、member は自分が所属するチームから選ぶ。選べるチームが一つなら最初から選ばれている。所属するチームのない member には「所属するチームがないため、エージェントを作成できません。ワークスペース管理者にチームへの追加を依頼してください。」だけが表示され、次へ進めない。
  • エージェント一覧: 表とカードに担当チームが表示される。設定を読めないエージェントのカードには名前・状態・担当チームだけがある。
  • 管理者タブ: 一番上の 担当チーム のまとまりに現在の担当チーム名がチームページへのリンクとして表示される。エージェントを管理する人は、その隣の 変更 でチームを選ぶとすぐ切り替わり、空にはできない。担当チームにチーム管理者がいなければ、そのことを知らせる。下の エージェント管理者 の一覧は人とチームを同じ1行の行で表示し、検索結果から選ぶとすぐ追加される。担当チームが管理者チームでもある場合、その行にメンバー全員が管理者であることが書かれる。
  • 担当チームのメンバー: 詳細画面の設定はすべて読めるが、編集・リリース・接続のボタンはなく、画面の上に読むだけだという案内がある。
  • 外の member: 詳細のアドレスを開くと、設定の代わりに名前・状態・担当チームと「このエージェントの設定は担当チームのメンバーと管理者だけが見られます。」が表示される。

セッションに紐付ける

セッションを作成するときに agent_release_id を指定する。profile_id はそのリリースのプロファイルでなければならない (そうでなければ 409 RELEASE_PROFILE_MISMATCH)。キーはそのエージェントのエージェントキーを使う。エージェントキーは Web コンソールか Web ログインの cookie で POST /v1/api-keys に kind: "agent" と agent_id を渡して発行する(API キー)。この発行には SDK のメソッドがない。秘密値は変数にだけ入れる。

repository_id は任意である。省くと、GitHub 設定があるエージェントはリポジトリなしで始まり、必要なリポジトリだけを付ける(リポジトリなしのセッション)。設定がない古いエージェントはリリースのリポジトリで始まる。下の例は sample-app を直接指定して、そのリポジトリで始める。

import { Werft } from "@werft-ai/sdk";

// WERFT_API_KEY holds the agent key; WERFT_AGENT_RELEASE_ID the release above.
const agent = new Werft();
const started = await agent.sessions.create({
  profile_id: "claude-coding-local",
  repository_id: "sample-app",
  agent_release_id: process.env.WERFT_AGENT_RELEASE_ID ?? "",
  message: 'GATE-SPEC {"id":"agent","steps":[],"final":"hello from research-bot"}',
});
console.log(started.status, started.turn_id);

エージェントキーで作成したセッションのリクエストは、別のキーか人が承認する(承認用のキーを分けて使う)。

ワークスペースに属さないキー (例: scripts/local.sh key quickstart …) で送ると、リリースが見つからず 404 になる。

次のステップ

このページ