ツール
1つのツールモジュールがすべてのエージェントハーネスで実行されます。beeuni.yaml には Beeuni のツールとプロジェクト独自のツールが記載され、各エージェントの tools リストで呼び出せるツールが決まります。
ツールとは、モデルがターン中に呼び出す関数です。 セッションには、どちらの ハーネス(標準ハーネス と 実験的ハーネス)でも、次の3種類があります。
| 種類 | 名前 | 実装元 |
|---|---|---|
| ハーネスツール | bash, read, write, edit, glob, grep, task, question, … |
ハーネス。 |
| Beeuni ツール | web_search, image_search, scrape_webpage, memory, show |
Beeuni。beeuni.yamlには、それぞれが<name>: beeuni:<name>として記載されています。tools:キーのないプロジェクトでは、5つすべてが使えます。 |
| プロジェクトツール | 宣言した任意の名前 | プロジェクト。beeuni.yamlのtools:の下に宣言する1つのモジュールです。 |
Beeuni ツールとプロジェクトツールはハーネスに依存しません。サンドボックスデーモンはツールごとに1つのモジュールを読み込み、どちらのハーネスでも同じ名前、引数、出力で実行します。実験的ハーネス はデーモンのプロセス内でモジュールを呼び出します。標準ハーネス はデーモンが書き出すプラグイン経由で呼び出し、プラグインは各呼び出しをデーモンに転送します。モジュールがハーネスのパッケージをインポートすることはありません。
beeuni.yaml の Beeuni ツール
新しいプロジェクトでは、tools:の下に5つの Beeuni ツールを記載します。
tools:
web_search: beeuni:web_search # maintained by Beeuni, updated automatically
image_search: beeuni:image_search
scrape_webpage: beeuni:scrape_webpage
memory: beeuni:memory
show: beeuni:show
# lookup_order: tools/lookup_order.ts # your own tool: a module in your repo
beeuni:<name>は、Beeuni が保守するツールを意味します。修正すると、そのツールを記載しているすべてのプロジェクトに反映され、プロジェクト側の変更は不要です。
セッションで使える Beeuni ツール
beeuni.yaml |
セッションで使えるツール |
|---|---|
ルートファイルにもインポートしたファイルにもtools:キーがない |
Beeuni ツール5つすべて。リストが存在する前に作成されたプロジェクトでも、5つすべてが使えます。 |
tools:キーがある(空の場合も含む。tools: {}、またはすべての行を削除したtools:など) |
リストにある Beeuni ツールのみ。beeuni:<name>として指定するか、ツールを置き換えるモジュールとして指定します。 |
どちらの場合も、プロジェクト独自のツールは読み込まれます。
セッションで使えるツールを確認する
beeuni tools lsはbeeuni.yamlとそのインポート先を読み込み、セッションで使える各ツールと、プロジェクトが除外した Beeuni ツールを表示します。
$ beeuni tools ls
NAME KIND SOURCE
web_search beeuni (overridden) tools/web_search.ts
scrape_webpage beeuni beeuni:scrape_webpage
memory beeuni beeuni:memory
show beeuni beeuni:show
lookup_order project tools/lookup_order.ts
Removed Beeuni tools: image_search
Add `<name>: beeuni:<name>` under `tools:` to get one back.
| 種類 | 意味 |
|---|---|
beeuni |
beeuni:<name>として記載された Beeuni ツール(またはtools:キーがない場合はすべて)。 |
beeuni (overridden) |
モジュールパスが指定された Beeuni ツール名。プロジェクトのモジュールが代わりに実行されます。 |
project |
プロジェクト独自のツール。 |
beeuni tools ls --jsonは{ manifest, tools_key, tools: [{ name, kind, source }], removed }を表示します。
Beeuni ツールを削除する
tools:の下にある該当行を削除します。変更リクエストがマージされた後に開始するすべてのセッションで、両方のハーネスを使うプロジェクト内の全エージェントが、そのツールを使えなくなります。特定のエージェントだけからツールを取り除くには、代わりにagents.<name>.toolsを使います。
ツールを削除したプロジェクトのセッションでは、次のようになります。
- 標準ハーネス は、そのツールをモデルに提示しません。
- 実験的ハーネス は、そのツールを提示しません。呼び出すと
Tool show not foundで失敗します(showの場合)。 - デーモンは、
POST /beeuni/tools/<name>に404とno tool named <name> is loadedで応答します。 - そのツールを記載したスキルは引き続き読み込まれます。 たとえば、
beeuni-memoryにはmemoryが記載されています。モデルはスキルを読みますが該当するツールがないため、別のツールを使います。memoryがなければ、ファイルツールでmemory/以下のファイルを読み取り、編集します。
Beeuni ツールを変更する:コピーして切り離す
beeuni tools eject <name>を使うと、Beeuni ツールの独自コピーをプロジェクトに作成できます。
$ beeuni tools eject web_search
✓ Ejected web_search
wrote tools/web_search.ts (the Beeuni source, now the project's to change)
set beeuni.yaml: tools.web_search: tools/web_search.ts
Next:
1. Edit tools/web_search.ts. Keep the default export: { description, parameters, execute(args, context) }.
2. beeuni validate
3. git add tools/web_search.ts beeuni.yaml && git commit -m "Own the web_search tool"
4. Open a change request: git push origin HEAD && beeuni cr open --title "Own the web_search tool"
Sessions load the copy after the change request merges.
- サンドボックスが実行するものと同じバイト列の Beeuni ソースを
tools/<name>.tsに書き出します。このファイルはnode:*モジュールのみをインポートし、context.envからBEEUNI_API_URL、BEEUNI_TOKEN、プロバイダーキーを読み込むため、そのまま実行できます。 - エントリを宣言しているファイル(ルートファイルまたはインポートしたファイル)の
beeuni.yamlで<name>: tools/<name>.tsを設定します。それ以外の行やコメントはそのまま残ります。変更した行の末尾には# ejected from beeuni:<name>が付きます。 tools:キーがない場合は作成し、残り4つの Beeuni ツールをbeeuni:<name>として記載します。そのため、切り離しによってツールが削除されることはありません。tools/<name>.tsが存在する場合や、ツールがすでに別のモジュールを指している場合は実行を拒否します。--forceは上書きします。- Beeuni ツールではない名前は拒否します。
コピーの所有者はあなたです。Beeuni による更新は停止します。保守対象のツールに戻すには、該当行を<name>: beeuni:<name>に戻し、ファイルを削除します。
コピーせずに置き換える
モジュールパスを指定した Beeuni ツール名は、すべてのハーネスで Beeuni ツールを置き換えます。
tools:
web_search: integrations/search/web_search.ts
Web UI のビュー
Web UI には各 Beeuni ツールに対応するビューがあり、ツール名で選択されます。showとmemoryのビューは、呼び出しの引数(type、path、url、content、items、command、path)を読み取ります。web_search、image_search、scrape_webpageのビューは出力 JSON を読み取ります。名前を維持したままコピーを変更する場合は、引数と出力の形式も維持してください。そうしないと、UI に生の出力が表示されます。
検証
beeuni validateはリストを検証します。
beeuni:<name>内の<name>は Beeuni ツールで、そのキーも同じ名前でなければなりません。web_search: beeuni:image_searchはエラーです。must be `beeuni:web_search` (the Beeuni tool) or a repo-relative path to a .ts or .js module (e.g. `tools/web_search.ts`) that replaces it.- Beeuni ツールを1つも記載していない
tools:キーは警告です。Sessions of this project get no Beeuni tool (web_search, image_search, scrape_webpage, memory, show). Add `<name>: beeuni:<name>` to keep one.プロジェクト独自の最初のツールを追加した際に、誤って5つのツールをすべて削除してしまうケースを検出します。 toolsに、プロジェクトで読み込まれない Beeuni ツールが指定されているエージェントは警告されます。"show" is a Beeuni tool this project does not load: add `show: beeuni:show` under the top-level `tools`, or remove it here.
マニフェストのリファレンスに、すべてのルールが記載されています。
プロジェクトツールを作成する
// tools/lookup_order.ts
export default {
description: 'Look up an order by id. Returns its status and carrier.',
parameters: {
type: 'object',
properties: {
id: { type: 'string', description: 'The order id, e.g. "A-1042"' },
include_items: { type: 'boolean', description: 'Also list the items' },
},
required: ['id'],
},
async execute(args, context) {
const response = await fetch(`https://orders.example.com/v1/orders/${args.id}`, {
headers: { authorization: `Bearer ${context.env.ORDERS_API_KEY}` },
signal: context.signal,
})
if (!response.ok) throw new Error(`orders API answered ${response.status}`)
return await response.json()
},
}
# beeuni.yaml
tools:
web_search: beeuni:web_search
# … the other Beeuni tools you keep
lookup_order: tools/lookup_order.ts
デフォルトエクスポートはプレーンオブジェクトです。
| フィールド | ルール |
|---|---|
description |
空でない文字列。モデルはこれを読んで、ツールを呼び出すタイミングを判断します。 |
parameters |
引数のJSON Schema。type: 'object'。モデルにはそのまま渡されるため、必須引数はrequiredに列挙してください。Beeuni は引数をこのスキーマに照らして検証しません。 |
execute(args, context) |
文字列または任意の JSON 値を返します(インデント付き JSON として送信)。asyncの場合もあります。エラーがスローされるとツールのエラーとなり、モデルはそのメッセージを読み取ります。 |
contextには次の情報が含まれます。
| フィールド | 内容 |
|---|---|
sessionId |
この呼び出しが属する Beeuni セッション。 |
agent |
呼び出しを行ったエージェント。 |
directory |
プロジェクトのチェックアウト先。/workspace。相対パスはこの場所を基準に解決します。 |
env |
ボックス環境と、このエージェントに渡されるシークレット(現在の値)。シークレットはcontext.env.NAMEとして読み取ります。ボックスの起動後に変更されたシークレットはprocess.envではなくcontext.envにあります。 |
signal |
ユーザーがターンを停止すると中断するAbortSignal。fetchに渡してください。 |
- 出力サイズ。 出力が50 KBまたは2,000行を超える場合、サンドボックスの一時ディレクトリにファイル全体が保存されます。モデルには先頭部分、サイズ、ファイルパスが渡されます。生のダンプではなく、モデルに必要な内容を返してください。
- エラー。 エラーがスローされると呼び出しは終了し、モデルはエラーメッセージを読み取ります。変更すべき内容が分かるメッセージをスローしてください。
- インポート。 モジュールは
node:*モジュール、Bun API、相対パスのファイルをインポートできます。パッケージは、モジュールの隣または上位にあるnode_modulesから解決されます。サンドボックスはツール用のパッケージをインストールしません。また、config release に含まれるのは、Gitが追跡するファイルだけです。HTTP APIにはfetchを使ってください。TypeScriptはビルド手順なしで実行されます。 - 読み込み失敗。 モジュールを読み込めない場合(ファイルがない、default exportがない、
parametersがオブジェクトスキーマではない、インポート時にエラーが起きる、パッケージを解決できないなど)、そのモジュールは除外され、ほかのツールは引き続き読み込まれます。デーモンはツールごとに、理由を添えて[tools] project tools that did not loadをログに記録します。
zodを使ったparameters
zod 4は同じJSON Schemaを生成します。
import { z } from 'zod'
export default {
description: 'Look up an order by id.',
parameters: z.toJSONSchema(z.object({ id: z.string().describe('The order id') })),
async execute(args) {
return `order ${args.id}: shipped`
},
}
このモジュールが読み込まれるのは、セッションを実行するチェックアウト内の node_modules から zod を解決できる場合だけです。それ以外の場合は Cannot find package 'zod' で失敗します。
zodでスキーマを書き、プレーンなJSON Schemaとして提供するには、一度出力してから parameters に貼り付けます。
bun -e "import { z } from 'zod'; console.log(JSON.stringify(z.toJSONSchema(z.object({ id: z.string() })), null, 2))"
例:プロジェクトシークレットを使ってHTTP APIを呼び出す
キーをプロジェクトシークレットとして保存します。コマンドは標準入力から値を読み取ります。
beeuni secrets set WEATHER_API_KEY=-
このツールを呼び出すエージェントにシークレットへのアクセスを許可します。スターターの beeuni エージェントには secrets: all があります。スコープ付きエージェントでの指定方法:secrets: [WEATHER_API_KEY]。
モジュールを作成します。
// tools/weather_now.ts
export default {
description: 'Current weather for a city: temperature in °C and conditions.',
parameters: {
type: 'object',
properties: { city: { type: 'string', description: 'City name, e.g. "Berlin"' } },
required: ['city'],
},
async execute(args, context) {
const key = context.env.WEATHER_API_KEY
if (!key) throw new Error('WEATHER_API_KEY is not set: run `beeuni secrets set WEATHER_API_KEY=-` and grant it to this agent.')
const url = `https://api.weather.example.com/v1/current?city=${encodeURIComponent(args.city)}`
const response = await fetch(url, { headers: { authorization: `Bearer ${key}` }, signal: context.signal })
if (!response.ok) throw new Error(`weather API answered ${response.status}: ${await response.text()}`)
const data = await response.json()
return { city: args.city, temperature_c: data.temp_c, conditions: data.conditions }
},
}
宣言します。
tools:
# … the Beeuni tools you keep
weather_now: tools/weather_now.ts
例:web_search をオーバーライドする
beeuni tools eject web_search は tools/web_search.ts を書き込み、web_search: tools/web_search.ts を設定します。その後、コピーを変更します。たとえば、1つのサイトの結果だけを残すには、リクエストの前に execute 内の各クエリにサイトを追加します。
// tools/web_search.ts, inside execute()
const queries = String(args.query ?? '').split('|||').map((query) => query.trim()).filter(Boolean)
.map((query) => `site:docs.example.com ${query}`)
名前、引数、出力JSONはそのままにしてください。そうすれば、web_search を指定するスキルもWeb UIの表示も引き続き動作します。
ツールをテストする
beeuni validate は、名前、パス、ファイルがない場合、および beeuni: の値を確認します。beeuni tools ls は、セッションで利用できる内容を表示します。
次に、コミットしてプッシュし、変更リクエストを作成します。マージ後にセッションを開始し、実際の引数を使った呼び出しを1回依頼します。
beeuni sessions new --wait --prompt "Call the weather_now tool once with city Berlin and print its output."
読み込めないツールはモデルに提示されません。エラーをスローするツールは、そのエラーメッセージを返します。beeuni sessions log <id> は、各ツール呼び出しとそのステータスを含むセッションのメッセージを出力します。
ツールを宣言する
tools: は、各ツール名をそのモジュールに割り当てるか、Beeuniツール名を beeuni:<name> に割り当てます。モジュールを置くフォルダーは自由に選べます。
tools:
web_search: beeuni:web_search
memory: beeuni:memory
lookup_order: tools/lookup_order.ts
crm_note: integrations/crm/note.ts
- 名前はsnake_caseで、64文字以内です:
^[a-z][a-z0-9_]{0,63}$。 - ハーネスツール名はエラーになります。Beeuniツール名には
beeuni:<name>またはモジュールパスを指定します。モジュールは、すべてのハーネスでBeeuniツールを置き換えます。 - パスはリポジトリ相対で、
.ts、.js、.mts、.mjs、.cts、または.cjsで終わる必要があります。 imports:に記載されたファイルでは、ツールを宣言できます。同じ名前を2つのファイルで宣言するとエラーになります。インポートされたファイル内のtools:キーは、選択ルールにおいてtools:キーとして扱われます。
マニフェストのリファレンス にすべてのルールが記載されています。
ツールを使えるユーザー
agents.<name>.tools は、エージェントのモデルにどのツールを表示するかを決定します。ハーネス、Beeuni、プロジェクトの各ツールを指定できます。
agents:
beeuni:
tools: all # every tool (the default)
researcher:
tools: [read, grep, web_search, lookup_order] # only these
support:
tools:
exclude: [bash, write, edit] # every tool but these
greeter:
tools: none # no tool
| 値 | エージェントが使えるもの |
|---|---|
省略、または all |
すべてのツール。 |
none |
ツールなし。 |
| リスト | リストに記載されたツールのみ。* を含むリストは all です。 |
{ exclude: [...] } |
リストに記載されたものを除く、すべてのツール。 |
{ bash: false, … } |
以前のマップ形式。false はツールを除外します。 |
toolsは表示可否を決めます。呼び出しを許可、確認、拒否するかは、エージェントのpermissionルールで引き続き決まります。リストにないツールはモデルに表示されず、呼び出された場合は拒否されます。- エージェントのリストで、プロジェクトが読み込んでいないBeeuniツールを追加することはできません。まず最上位の
tools:に記載してください。 taskツールで起動したサブエージェントの場合:実験的ハーネスでは、そのエージェント自身のリストとセッションエージェントのリストの両方で許可されたツールだけを使えます。標準ハーネスでは、そのエージェント自身のリストにあるツールを使えます。標準ハーネスでは、エージェントのルールはそのエージェントにのみ適用されます。エージェントによる委譲を禁止するには、そのリストからtaskを外してください。- Connector ツールには、このリストではなく
agents.<name>.connectorsが適用されます。 beeuni validateは、ハーネス、Beeuni、プロジェクトのいずれのツールでもない名前や、プロジェクトが読み込んでいないBeeuniツールについて警告します。excludeリストで名前を誤記しても、実際のツールは有効なままです。
標準ハーネスでは、bash はターミナルプラグインの pty_* ツールも対象とします。また、write、edit、apply_patch は1つの権限を共有します。そのうち1つをリストに含めると3つすべてが表示され、1つを除外すると3つすべてが非表示になります。実験的ハーネスでは、write ツールと edit ツールは別々です。
変更が反映されるタイミング
セッションでは、ランタイムの起動時にツールが読み込まれます。変更リクエストがマージされると変更が適用されます。新しいセッションでは変更が読み込まれ、config releases を使っている場合、実行中のセッションでは次の収束時に読み込まれます。実験的ハーネスでは、プロジェクトツールまたはBeeuniツールのリストを変更すると、ターンが実行されていない間にランタイムがその場で再起動し、会話は続行されます。
harnesses/opencode/tools/ 内のツール
標準ハーネスは引き続き、設定ディレクトリからカスタムツールを読み込みます。実験的ハーネスは読み込みません。ホスト型ツールの提供開始前に作成されたプロジェクトには、Beeuniツールのコピーが置かれています(web_search.ts、memory.ts、…)。
- 標準ハーネスでは、そのフォルダー内のツールが同名のホスト型ツールより優先されるため、削除するまでプロジェクトのコピーが使われます。Beeuniツールのコピーを削除してください。両方のハーネスで、デーモンが
beeuni.yamlに記載されたツールをホストします。 - 標準ハーネスのツールを両方のハーネスで実行するには、ツールをそのフォルダーから移動し、
tool({ description, args, execute })をparametersがJSON Schemaであるプレーンなオブジェクトに置き換え、context.envからシークレットを読み取り、tools:の下に宣言します。