Skip to content

feat: commandmate wait にLLM APIエラー検出機能を追加(exit 3) #523

@Kewton

Description

@Kewton

概要

commandmate wait にターミナル出力のパターンマッチによるLLM APIエラー検出機能を追加し、exit code 3 で返却する。

関連: Issue #518(CLI基盤), Issue #520(wait完了検出)
構想ドキュメント: workspace/v0.5/orchestration-concept.html セクション「子エージェントのエラー検出とリカバリ」

背景

現在の wait はエージェントのステータス(idle/ready/running/waiting)のみを監視しており、ターミナル出力の内容は検査していない。LLM APIエラー(Rate limit, ネットワークエラー等)でエージェントが停止しても、tmuxセッションは生存しているため sessionStatus=running のまま、タイムアウトまで待ち続ける。

# 現状: Rate limit でエージェントが止まっても検知できない
commandmate wait localllm-test-main --timeout 600
# → 10分間ただ待つだけ → exit 124 (タイムアウト)

検出対象パターン

エラー種別 検出パターン例 retryable
APIレート制限 rate limit, 429, overloaded, too many requests true
ネットワークエラー ECONNREFUSED, ETIMEDOUT, fetch failed, network error true
認証エラー unauthorized, invalid API key, 401, 403 false
サーバーエラー 500, 502, 503, internal server error true

実装方針

wait のポーリングループにパターンマッチを追加

current-output API の content または fullOutput に対して、エラーパターンを検査する。

// wait.ts のポーリングループ内(sessionStatus チェック後)
const errorMatch = detectLlmError(data.content || data.fullOutput || '');
if (errorMatch) {
  const errorOutput = {
    worktreeId,
    errorType: errorMatch.type,
    message: errorMatch.message,
    retryable: errorMatch.retryable,
  };
  return { exitCode: WaitExitCode.LLM_ERROR, output: errorOutput };
}

exit code 3 の追加

// types/index.ts
export const WaitExitCode = {
  SUCCESS: 0,
  PROMPT_DETECTED: 10,
  LLM_ERROR: 3,      // ← 新規
  TIMEOUT: 124,
} as const;

エラー検出関数

// cli/utils/llm-error-detector.ts
interface LlmErrorMatch {
  type: 'rate_limit' | 'network_error' | 'auth_error' | 'server_error';
  message: string;
  retryable: boolean;
}

function detectLlmError(output: string): LlmErrorMatch | null {
  // 出力の末尾N行のみ検査(パフォーマンス + 過去のエラーを誤検出しない)
  const tail = getLastNLines(output, 20);
  // パターンマッチ...
}

stdout出力(exit 3 時)

{
  "worktreeId": "localllm-test-main",
  "errorType": "rate_limit",
  "message": "API Error: Rate limit reached",
  "retryable": true
}

使用例

commandmate wait localllm-test-main --timeout 600
# exit 3 の場合:
# stdout: {"worktreeId":"...","errorType":"rate_limit","message":"...","retryable":true}

# 親エージェントのリカバリ
EXIT_CODE=$?
if [ $EXIT_CODE -eq 3 ]; then
  ERROR_JSON=$(commandmate wait ... 2>/dev/null)
  RETRYABLE=$(echo "$ERROR_JSON" | jq -r '.retryable')
  if [ "$RETRYABLE" = "true" ]; then
    sleep 60  # 待機後リトライ
    commandmate send "$WT" "続けてください"
  else
    echo "リカバリ不可能なエラー"
  fi
fi

注意事項

  • エラーパターンは出力の末尾N行のみを検査する(過去のエラーメッセージを誤検出しない)
  • パターンはCLI側に定義(サーバー側の変更は不要)
  • 既存の --stall-timeout との併用可能(stall-timeoutは出力変化なし、LLMエラー検出は特定パターン)

受け入れ条件

  • Rate limit パターンが出力に含まれる場合、exit 3 で返却される
  • ネットワークエラーパターンが検出される
  • 認証エラーパターンが検出される
  • exit 3 時にstdoutにエラー情報JSONが出力される
  • retryable フラグが正しく設定される
  • 出力の末尾のみ検査し、過去のエラーを誤検出しない
  • 既存の exit 0/10/124 の動作に影響がない
  • 単体テストでカバー

影響範囲

ファイル 変更内容
src/cli/utils/llm-error-detector.ts 新規: エラーパターン検出関数
src/cli/commands/wait.ts ポーリングループにエラー検出追加
src/cli/types/index.ts WaitExitCode.LLM_ERROR = 3 追加
src/cli/types/api-responses.ts エラー出力型追加
src/cli/docs/agent-operations.ts ドキュメント更新(exit 3 追記)
tests/unit/cli/commands/wait.test.ts テスト追加
tests/unit/cli/utils/llm-error-detector.test.ts 新規テスト

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions