Skip to content

Commit e7c930f

Browse files
authored
Merge pull request #43 from mkXultra/feat/peek_supoort_tooluse
feat: peekコマンドにtool_callイベントサポートを追加
2 parents dc3558b + c05b916 commit e7c930f

14 files changed

Lines changed: 709 additions & 101 deletions

README.ja.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -278,26 +278,29 @@ Claude CLI、Codex CLI、Gemini CLI、Forge CLI、または OpenCode を使用
278278

279279
### `peek`
280280

281-
実行中の子エージェントを短時間だけ観測し、その `peek` 呼び出しの観測ウィンドウ内で ai-cli-mcp が受理した自然言語メッセージだけを返します。履歴APIではなく、欠落のないストリーミングでもなく、シェルの `stdout` / `stderr` tail でもありません。別々の `peek` 呼び出しの間に出たメッセージは取得できない場合があります。v1 では `--follow` はありません。
281+
実行中の子エージェントを短時間だけ観測し、その `peek` 呼び出しの観測ウィンドウ内で ai-cli-mcp が受理した構造化イベントを返します。デフォルトでは自然言語メッセージイベントだけを返し、`include_tool_calls` または `--include-tool-calls` を指定すると正規化された tool-call イベントも含めます。履歴APIではなく、欠落のないストリーミングでもなく、シェルの `stdout` / `stderr` tail でもありません。別々の `peek` 呼び出しの間に出たイベントは取得できない場合があります。v1 では `--follow` はありません。
282282

283283
CLI v1:
284284

285285
```bash
286286
ai-cli peek 123 --time 10
287287
ai-cli peek 123 456 --time 10
288+
ai-cli peek 123 --time 10 --include-tool-calls
288289
```
289290

290291
**引数:**
291292
- `pids` (array of numbers, 必須): `run` が返したプロセスIDを 1..32 件指定します。重複したPIDはサーバー側で重複排除され、最初に出た順序が維持されます。未知または管理外のPIDは、呼び出し全体の失敗ではなく、プロセスごとに `not_found` として返されます。
292293
- `peek_time_sec` (number, 任意): 観測時間(秒)の正の整数です。デフォルトは10秒、最大60秒です。`0`、負数、小数は無効です。
294+
- `include_tool_calls` (boolean, 任意): `true` の場合、各プロセスの `events` 配列にメッセージイベントに加えて正規化された `tool_call` イベントを含めます。デフォルトは `false` です。
293295

294296
**観測とフィルタリング:**
295-
- `peek_started_at``messages[].ts` は、ai-cli-mcp サーバー側の UTC RFC3339 タイムスタンプです。`peek_started_at` は検証とリスナー登録後に観測ウィンドウが始まった時刻、`messages[].ts` は ai-cli-mcp がメッセージを観測して受理した時刻です
297+
- `peek_started_at``events[].ts` は、ai-cli-mcp サーバー側の UTC RFC3339 タイムスタンプです。`peek_started_at` は検証とリスナー登録後に観測ウィンドウが始まった時刻、`events[].ts` は ai-cli-mcp がイベントを観測して受理した時刻です
296298
- 観測ウィンドウは `peek_time_sec` が経過するか、対象プロセスがすべて終端状態になった時点で終了します。
297-
- 観測開始前のメッセージは返しません。同じPIDへの同時 `peek` は可能で、それぞれ独立した観測ウィンドウを持つため、メッセージが重複して返ることがあります。
298-
- 返すのは認識済みの自然言語メッセージだけです。Codex の `agent_message` text、Claude assistant の text content、OpenCode の `type: "text"` かつ `part.type``"text"` のイベント、Gemini stream-json の `role``"assistant"``message` イベントを含めます。raw `stdout` / `stderr`、raw JSONL、reasoning、`tool_use``tool_result`、コマンドの `stdout` / `stderr`、command execution メタデータ、token usage、verbose メタデータは除外します。
299-
- 未知のイベント形状はデフォルトで拒否します。Forge など、自然言語抽出がまだ明示対応されていない管理対象エージェントは、実際のプロセス状態を返しつつ、`messages: []``truncated: false``error: null` にします。
300-
- 各PIDごとに、観測ウィンドウ内で最初に観測された50件までを保持します。それ以降のメッセージを捨てた場合は `truncated``true` になります。
299+
- 観測開始前のイベントは返しません。同じPIDへの同時 `peek` は可能で、それぞれ独立した観測ウィンドウを持つため、イベントが重複して返ることがあります。
300+
- メッセージイベントは、Codex の `agent_message` text、Claude assistant の text content、OpenCode の `type: "text"` かつ `part.type``"text"` のイベント、Gemini stream-json の `role``"assistant"``message` イベントから認識します。
301+
- tool call を含める場合、Codex の command/MCP call、Claude の tool use/result、Gemini の tool use/result、OpenCode の完了済み tool use event を正規化した `tool_call` イベントとして返します。tool summary は tool 名と入力メタデータだけから作る短い1行文字列です。raw `stdout` / `stderr`、raw JSONL、tool result output、コマンド出力、`result.response`、stats、token usage、verbose メタデータは除外します。
302+
- 未知のイベント形状はデフォルトで拒否します。Forge など、まだ明示対応されていない管理対象エージェントは、実際のプロセス状態を返しつつ、`events: []``truncated: false``error: null` にします。
303+
- 各PIDごとに、観測ウィンドウ内で最初に観測された50件までを保持します。それ以降のイベントを捨てた場合は `truncated``true` になります。
301304
- `status``running``completed``failed``not_found` のいずれかで、観測ウィンドウ終了時点の状態を表します。
302305
- `agent``claude``codex``gemini``forge``opencode`、将来追加される追跡済みエージェント文字列、または `null` です。`null` はプロセスが見つからない、またはエージェント種別を判断できない場合を表します。
303306

@@ -312,8 +315,9 @@ ai-cli peek 123 456 --time 10
312315
"pid": 123,
313316
"agent": "codex",
314317
"status": "running",
315-
"messages": [
316-
{ "ts": "2026-04-11T12:34:59.120Z", "text": "I'm checking the implementation." }
318+
"events": [
319+
{ "kind": "message", "ts": "2026-04-11T12:34:59.120Z", "text": "I'm checking the implementation." },
320+
{ "kind": "tool_call", "ts": "2026-04-11T12:35:00.000Z", "phase": "started", "id": "item_0", "tool": "command_execution", "summary": "/bin/sh -c 'echo hi'" }
317321
],
318322
"truncated": false,
319323
"error": null
@@ -322,7 +326,7 @@ ai-cli peek 123 456 --time 10
322326
"pid": 999,
323327
"agent": null,
324328
"status": "not_found",
325-
"messages": [],
329+
"events": [],
326330
"truncated": false,
327331
"error": "process not found"
328332
}

README.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -275,26 +275,29 @@ By default, each returned result item uses the compact shape shared with `get_re
275275

276276
### `peek`
277277

278-
Starts a one-shot short observation window for running child agents and returns only natural-language agent messages observed during that specific call. It is not a history API, not gapless streaming, and not shell stdout/stderr tailing. Separate `peek` calls may miss messages emitted between calls; `--follow` is intentionally not part of v1.
278+
Starts a one-shot short observation window for running child agents and returns structured events observed during that specific call. By default this includes only natural-language message events; pass `include_tool_calls` or `--include-tool-calls` to also include normalized tool-call events. It is not a history API, not gapless streaming, and not shell stdout/stderr tailing. Separate `peek` calls may miss events emitted between calls; `--follow` is intentionally not part of v1.
279279

280280
CLI v1:
281281

282282
```bash
283283
ai-cli peek 123 --time 10
284284
ai-cli peek 123 456 --time 10
285+
ai-cli peek 123 --time 10 --include-tool-calls
285286
```
286287

287288
**Arguments:**
288289
- `pids` (array of numbers, required): 1..32 process IDs returned by `run`. Duplicate PIDs are deduplicated server-side, preserving first occurrence order. Unknown or unmanaged PIDs are returned per process as `not_found`, not as a whole-call failure.
289290
- `peek_time_sec` (number, optional): Positive integer observation length in seconds. Defaults to 10 and is capped at 60. `0`, negative values, and fractional values are invalid.
291+
- `include_tool_calls` (boolean, optional): When `true`, each process `events` array includes normalized `tool_call` events in addition to message events. Defaults to `false`.
290292

291293
**Observation and filtering:**
292-
- `peek_started_at` and `messages[].ts` are ai-cli-mcp server-side UTC RFC3339 timestamps. `peek_started_at` is when the observation window starts after validation and listener registration; `messages[].ts` is when ai-cli-mcp observed and accepted the message.
294+
- `peek_started_at` and `events[].ts` are ai-cli-mcp server-side UTC RFC3339 timestamps. `peek_started_at` is when the observation window starts after validation and listener registration; `events[].ts` is when ai-cli-mcp observed and accepted the event.
293295
- The window ends when `peek_time_sec` elapses or all target processes reach a terminal state, whichever comes first.
294-
- Messages emitted before the window starts are not returned. Concurrent `peek` calls for the same PID are allowed; each has an independent window and may return overlapping messages.
295-
- Only recognized natural-language agent messages are returned: Codex `agent_message` text, Claude assistant text content, OpenCode `type: "text"` events where `part.type` is `"text"`, and Gemini stream-json `message` events where `role` is `"assistant"`. Raw stdout/stderr, raw JSONL, reasoning, `tool_use`, `tool_result`, command stdout/stderr, command execution metadata, token usage, and verbose metadata are excluded.
296-
- Unknown event shapes are denied by default. Managed agents without supported natural-language extraction, such as Forge until explicitly supported, return their real process status with `messages: []`, `truncated: false`, and `error: null`.
297-
- Each PID keeps the first 50 messages observed in the window. If later messages are dropped, `truncated` is `true`.
296+
- Events emitted before the window starts are not returned. Concurrent `peek` calls for the same PID are allowed; each has an independent window and may return overlapping events.
297+
- Message events are recognized from Codex `agent_message` text, Claude assistant text content, OpenCode `type: "text"` events where `part.type` is `"text"`, and Gemini stream-json `message` events where `role` is `"assistant"`.
298+
- When tool calls are included, `tool_call` events are normalized for Codex command/MCP calls, Claude tool use/results, Gemini tool use/results, and OpenCode completed tool use events. Tool summaries are bounded one-line strings derived from tool names and input metadata only. Raw stdout/stderr, raw JSONL, tool result output, command output, `result.response`, stats, token usage, and verbose metadata are excluded.
299+
- Unknown event shapes are denied by default. Managed agents without supported extraction, such as Forge until explicitly supported, return their real process status with `events: []`, `truncated: false`, and `error: null`.
300+
- Each PID keeps the first 50 events observed in the window. If later events are dropped, `truncated` is `true`.
298301
- `status` is one of `running`, `completed`, `failed`, or `not_found`, and reflects state when the observation window closes.
299302
- `agent` is `claude`, `codex`, `gemini`, `forge`, `opencode`, a future tracked string value, or `null` when the process is not found or the agent cannot be determined.
300303

@@ -309,8 +312,9 @@ Example response:
309312
"pid": 123,
310313
"agent": "codex",
311314
"status": "running",
312-
"messages": [
313-
{ "ts": "2026-04-11T12:34:59.120Z", "text": "I'm checking the implementation." }
315+
"events": [
316+
{ "kind": "message", "ts": "2026-04-11T12:34:59.120Z", "text": "I'm checking the implementation." },
317+
{ "kind": "tool_call", "ts": "2026-04-11T12:35:00.000Z", "phase": "started", "id": "item_0", "tool": "command_execution", "summary": "/bin/sh -c 'echo hi'" }
314318
],
315319
"truncated": false,
316320
"error": null
@@ -319,7 +323,7 @@ Example response:
319323
"pid": 999,
320324
"agent": null,
321325
"status": "not_found",
322-
"messages": [],
326+
"events": [],
323327
"truncated": false,
324328
"error": "process not found"
325329
}

src/__tests__/app-cli.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ describe('ai-cli app', () => {
210210
});
211211

212212
const exitCode = await runCli(
213-
['peek', '123', '456', '123', '--time', '5'],
213+
['peek', '123', '456', '123', '--time', '5', '--include-tool-calls'],
214214
{
215215
stdout,
216216
stderr,
@@ -219,7 +219,7 @@ describe('ai-cli app', () => {
219219
);
220220

221221
expect(exitCode).toBe(0);
222-
expect(peekProcesses).toHaveBeenCalledWith([123, 456], 5);
222+
expect(peekProcesses).toHaveBeenCalledWith([123, 456], 5, true);
223223
expect(stdout).toHaveBeenCalledWith(expect.stringContaining('"peek_started_at"'));
224224
expect(stderr).not.toHaveBeenCalled();
225225
});
@@ -235,7 +235,7 @@ describe('ai-cli app', () => {
235235

236236
const defaultExitCode = await runCli(['peek', '123'], { stdout, stderr, peekProcesses });
237237
expect(defaultExitCode).toBe(0);
238-
expect(peekProcesses).toHaveBeenCalledWith([123], 10);
238+
expect(peekProcesses).toHaveBeenCalledWith([123], 10, false);
239239

240240
const followExitCode = await runCli(['peek', '123', '--follow'], { stdout, stderr, peekProcesses });
241241
expect(followExitCode).toBe(1);

src/__tests__/cli-process-service.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,9 @@ printf '%s\n' '{"type":"user","message":{"content":[{"type":"tool_result","tool_
171171
pid: runResult.pid,
172172
agent: 'claude',
173173
status: 'completed',
174-
messages: [
174+
events: [
175175
{
176+
kind: 'message',
176177
ts: expect.any(String),
177178
text: 'new cli message',
178179
},
@@ -184,7 +185,7 @@ printf '%s\n' '{"type":"user","message":{"content":[{"type":"tool_result","tool_
184185
pid: 999999,
185186
agent: null,
186187
status: 'not_found',
187-
messages: [],
188+
events: [],
188189
truncated: false,
189190
error: 'process not found',
190191
});

src/__tests__/mcp-contract.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ describe('MCP Contract Tests', () => {
137137
const peekTool = tools.find((tool: any) => tool.name === 'peek');
138138
expect(peekTool.inputSchema.required).toEqual(['pids']);
139139
expect(Object.keys(peekTool.inputSchema.properties).sort()).toEqual([
140+
'include_tool_calls',
140141
'peek_time_sec',
141142
'pids',
142143
]);

0 commit comments

Comments
 (0)