providerごとに通信を許可する
This commit is contained in:
parent
6bd698941c
commit
479ee7ec25
12
README.md
12
README.md
@ -803,10 +803,22 @@ Special `next` values: `COMPLETE` (success), `ABORT` (failure)
|
|||||||
| `provider` | - | Override provider for this movement (`claude`, `codex`, or `opencode`) |
|
| `provider` | - | Override provider for this movement (`claude`, `codex`, or `opencode`) |
|
||||||
| `model` | - | Override model for this movement |
|
| `model` | - | Override model for this movement |
|
||||||
| `permission_mode` | - | Permission mode: `readonly`, `edit`, `full` (provider-independent) |
|
| `permission_mode` | - | Permission mode: `readonly`, `edit`, `full` (provider-independent) |
|
||||||
|
| `provider_options` | - | Provider-specific options (e.g. `codex.network_access`, `opencode.network_access`) |
|
||||||
| `output_contracts` | - | Output contract definitions for report files |
|
| `output_contracts` | - | Output contract definitions for report files |
|
||||||
| `quality_gates` | - | AI directives for movement completion requirements |
|
| `quality_gates` | - | AI directives for movement completion requirements |
|
||||||
| `mcp_servers` | - | MCP (Model Context Protocol) server configuration (stdio/SSE/HTTP) |
|
| `mcp_servers` | - | MCP (Model Context Protocol) server configuration (stdio/SSE/HTTP) |
|
||||||
|
|
||||||
|
Piece-level defaults can be set with `piece_config.provider_options`, and movement-level `provider_options` overrides them.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
```
|
||||||
|
|
||||||
## API Usage Example
|
## API Usage Example
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: backend-cqrs
|
name: backend-cqrs
|
||||||
description: CQRS+ES, Security, QA Expert Review
|
description: CQRS+ES, Security, QA Expert Review
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 30
|
max_movements: 30
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: backend
|
name: backend
|
||||||
description: Backend, Security, QA Expert Review
|
description: Backend, Security, QA Expert Review
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 30
|
max_movements: 30
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: coding
|
name: coding
|
||||||
description: Lightweight development piece with planning and parallel reviews (plan -> implement -> parallel review -> complete)
|
description: Lightweight development piece with planning and parallel reviews (plan -> implement -> parallel review -> complete)
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 20
|
max_movements: 20
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: compound-eye
|
name: compound-eye
|
||||||
description: Multi-model review - send the same instruction to Claude and Codex simultaneously, synthesize both responses
|
description: Multi-model review - send the same instruction to Claude and Codex simultaneously, synthesize both responses
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 10
|
max_movements: 10
|
||||||
initial_movement: evaluate
|
initial_movement: evaluate
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: default
|
name: default
|
||||||
description: Standard development piece with planning and specialized reviews
|
description: Standard development piece with planning and specialized reviews
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 30
|
max_movements: 30
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
loop_monitors:
|
loop_monitors:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: e2e-test
|
name: e2e-test
|
||||||
description: E2E test focused piece (E2E analysis → E2E implementation → review → fix)
|
description: E2E test focused piece (E2E analysis → E2E implementation → review → fix)
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 20
|
max_movements: 20
|
||||||
initial_movement: plan_test
|
initial_movement: plan_test
|
||||||
loop_monitors:
|
loop_monitors:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: expert-cqrs
|
name: expert-cqrs
|
||||||
description: CQRS+ES, Frontend, Security, QA Expert Review
|
description: CQRS+ES, Frontend, Security, QA Expert Review
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 30
|
max_movements: 30
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: expert
|
name: expert
|
||||||
description: Architecture, Frontend, Security, QA Expert Review
|
description: Architecture, Frontend, Security, QA Expert Review
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 30
|
max_movements: 30
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: frontend
|
name: frontend
|
||||||
description: Frontend, Security, QA Expert Review
|
description: Frontend, Security, QA Expert Review
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 30
|
max_movements: 30
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: magi
|
name: magi
|
||||||
description: MAGI Deliberation System - Analyze from 3 perspectives and decide by majority
|
description: MAGI Deliberation System - Analyze from 3 perspectives and decide by majority
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 5
|
max_movements: 5
|
||||||
initial_movement: melchior
|
initial_movement: melchior
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: minimal
|
name: minimal
|
||||||
description: Minimal development piece (implement -> parallel review -> fix if needed -> complete)
|
description: Minimal development piece (implement -> parallel review -> fix if needed -> complete)
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 20
|
max_movements: 20
|
||||||
initial_movement: implement
|
initial_movement: implement
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: passthrough
|
name: passthrough
|
||||||
description: Single-agent thin wrapper. Pass task directly to coder as-is.
|
description: Single-agent thin wrapper. Pass task directly to coder as-is.
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 10
|
max_movements: 10
|
||||||
initial_movement: execute
|
initial_movement: execute
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: research
|
name: research
|
||||||
description: Research piece - autonomously executes research without asking questions
|
description: Research piece - autonomously executes research without asking questions
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 10
|
max_movements: 10
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: review-fix-minimal
|
name: review-fix-minimal
|
||||||
description: Review and fix piece for existing code (starts with review, no implementation)
|
description: Review and fix piece for existing code (starts with review, no implementation)
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 20
|
max_movements: 20
|
||||||
initial_movement: reviewers
|
initial_movement: reviewers
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: review-only
|
name: review-only
|
||||||
description: Review-only piece - reviews code without making edits
|
description: Review-only piece - reviews code without making edits
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 10
|
max_movements: 10
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: structural-reform
|
name: structural-reform
|
||||||
description: Full project review and structural reform - iterative codebase restructuring with staged file splits
|
description: Full project review and structural reform - iterative codebase restructuring with staged file splits
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 50
|
max_movements: 50
|
||||||
initial_movement: review
|
initial_movement: review
|
||||||
loop_monitors:
|
loop_monitors:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: unit-test
|
name: unit-test
|
||||||
description: Unit test focused piece (test analysis → test implementation → review → fix)
|
description: Unit test focused piece (test analysis → test implementation → review → fix)
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 20
|
max_movements: 20
|
||||||
initial_movement: plan_test
|
initial_movement: plan_test
|
||||||
loop_monitors:
|
loop_monitors:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: backend-cqrs
|
name: backend-cqrs
|
||||||
description: CQRS+ES・セキュリティ・QA専門家レビュー
|
description: CQRS+ES・セキュリティ・QA専門家レビュー
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 30
|
max_movements: 30
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: backend
|
name: backend
|
||||||
description: バックエンド・セキュリティ・QA専門家レビュー
|
description: バックエンド・セキュリティ・QA専門家レビュー
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 30
|
max_movements: 30
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: coding
|
name: coding
|
||||||
description: Lightweight development piece with planning and parallel reviews (plan -> implement -> parallel review -> complete)
|
description: Lightweight development piece with planning and parallel reviews (plan -> implement -> parallel review -> complete)
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 20
|
max_movements: 20
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: compound-eye
|
name: compound-eye
|
||||||
description: 複眼レビュー - 同じ指示を Claude と Codex に同時に投げ、両者の回答を統合する
|
description: 複眼レビュー - 同じ指示を Claude と Codex に同時に投げ、両者の回答を統合する
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 10
|
max_movements: 10
|
||||||
initial_movement: evaluate
|
initial_movement: evaluate
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: default
|
name: default
|
||||||
description: Standard development piece with planning and specialized reviews
|
description: Standard development piece with planning and specialized reviews
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 30
|
max_movements: 30
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
loop_monitors:
|
loop_monitors:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: e2e-test
|
name: e2e-test
|
||||||
description: E2Eテスト追加に特化したピース(E2E分析→E2E実装→レビュー→修正)
|
description: E2Eテスト追加に特化したピース(E2E分析→E2E実装→レビュー→修正)
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 20
|
max_movements: 20
|
||||||
initial_movement: plan_test
|
initial_movement: plan_test
|
||||||
loop_monitors:
|
loop_monitors:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: expert-cqrs
|
name: expert-cqrs
|
||||||
description: CQRS+ES・フロントエンド・セキュリティ・QA専門家レビュー
|
description: CQRS+ES・フロントエンド・セキュリティ・QA専門家レビュー
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 30
|
max_movements: 30
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: expert
|
name: expert
|
||||||
description: アーキテクチャ・フロントエンド・セキュリティ・QA専門家レビュー
|
description: アーキテクチャ・フロントエンド・セキュリティ・QA専門家レビュー
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 30
|
max_movements: 30
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: frontend
|
name: frontend
|
||||||
description: フロントエンド・セキュリティ・QA専門家レビュー
|
description: フロントエンド・セキュリティ・QA専門家レビュー
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 30
|
max_movements: 30
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: magi
|
name: magi
|
||||||
description: MAGI合議システム - 3つの観点から分析し多数決で判定
|
description: MAGI合議システム - 3つの観点から分析し多数決で判定
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 5
|
max_movements: 5
|
||||||
initial_movement: melchior
|
initial_movement: melchior
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: minimal
|
name: minimal
|
||||||
description: Minimal development piece (implement -> parallel review -> fix if needed -> complete)
|
description: Minimal development piece (implement -> parallel review -> fix if needed -> complete)
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 20
|
max_movements: 20
|
||||||
initial_movement: implement
|
initial_movement: implement
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: passthrough
|
name: passthrough
|
||||||
description: Single-agent thin wrapper. Pass task directly to coder as-is.
|
description: Single-agent thin wrapper. Pass task directly to coder as-is.
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 10
|
max_movements: 10
|
||||||
initial_movement: execute
|
initial_movement: execute
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: research
|
name: research
|
||||||
description: 調査ピース - 質問せずに自律的に調査を実行
|
description: 調査ピース - 質問せずに自律的に調査を実行
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 10
|
max_movements: 10
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: review-fix-minimal
|
name: review-fix-minimal
|
||||||
description: 既存コードのレビューと修正ピース(レビュー開始、実装なし)
|
description: 既存コードのレビューと修正ピース(レビュー開始、実装なし)
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 20
|
max_movements: 20
|
||||||
initial_movement: reviewers
|
initial_movement: reviewers
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: review-only
|
name: review-only
|
||||||
description: レビュー専用ピース - コードをレビューするだけで編集は行わない
|
description: レビュー専用ピース - コードをレビューするだけで編集は行わない
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 10
|
max_movements: 10
|
||||||
initial_movement: plan
|
initial_movement: plan
|
||||||
movements:
|
movements:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: structural-reform
|
name: structural-reform
|
||||||
description: プロジェクト全体レビューと構造改革 - 段階的なファイル分割による反復的コードベース再構築
|
description: プロジェクト全体レビューと構造改革 - 段階的なファイル分割による反復的コードベース再構築
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 50
|
max_movements: 50
|
||||||
initial_movement: review
|
initial_movement: review
|
||||||
loop_monitors:
|
loop_monitors:
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: unit-test
|
name: unit-test
|
||||||
description: 単体テスト追加に特化したピース(テスト分析→テスト実装→レビュー→修正)
|
description: 単体テスト追加に特化したピース(テスト分析→テスト実装→レビュー→修正)
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
max_movements: 20
|
max_movements: 20
|
||||||
initial_movement: plan_test
|
initial_movement: plan_test
|
||||||
loop_monitors:
|
loop_monitors:
|
||||||
|
|||||||
@ -803,10 +803,22 @@ rules:
|
|||||||
| `provider` | - | このムーブメントのプロバイダーを上書き(`claude`、`codex`、または`opencode`) |
|
| `provider` | - | このムーブメントのプロバイダーを上書き(`claude`、`codex`、または`opencode`) |
|
||||||
| `model` | - | このムーブメントのモデルを上書き |
|
| `model` | - | このムーブメントのモデルを上書き |
|
||||||
| `permission_mode` | - | パーミッションモード: `readonly`、`edit`、`full`(プロバイダー非依存) |
|
| `permission_mode` | - | パーミッションモード: `readonly`、`edit`、`full`(プロバイダー非依存) |
|
||||||
|
| `provider_options` | - | プロバイダー固有オプション(例: `codex.network_access`、`opencode.network_access`) |
|
||||||
| `output_contracts` | - | レポートファイルの出力契約定義 |
|
| `output_contracts` | - | レポートファイルの出力契約定義 |
|
||||||
| `quality_gates` | - | ムーブメント完了要件のAIディレクティブ |
|
| `quality_gates` | - | ムーブメント完了要件のAIディレクティブ |
|
||||||
| `mcp_servers` | - | MCP(Model Context Protocol)サーバー設定(stdio/SSE/HTTP) |
|
| `mcp_servers` | - | MCP(Model Context Protocol)サーバー設定(stdio/SSE/HTTP) |
|
||||||
|
|
||||||
|
ピース全体のデフォルトは `piece_config.provider_options` で設定でき、ムーブメント側 `provider_options` で上書きできます。
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
```
|
||||||
|
|
||||||
## API使用例
|
## API使用例
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: e2e-cycle-detect
|
name: e2e-cycle-detect
|
||||||
description: Piece with loop_monitors for cycle detection E2E testing
|
description: Piece with loop_monitors for cycle detection E2E testing
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
|
|
||||||
max_movements: 20
|
max_movements: 20
|
||||||
initial_movement: review
|
initial_movement: review
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: e2e-mock-max-iter
|
name: e2e-mock-max-iter
|
||||||
description: Piece with max_movements=2 that loops between two steps
|
description: Piece with max_movements=2 that loops between two steps
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
|
|
||||||
max_movements: 2
|
max_movements: 2
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: e2e-mock-no-match
|
name: e2e-mock-no-match
|
||||||
description: Piece with a strict rule condition that will not match mock output
|
description: Piece with a strict rule condition that will not match mock output
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
|
|
||||||
max_movements: 3
|
max_movements: 3
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: e2e-mock-single
|
name: e2e-mock-single
|
||||||
description: Minimal mock-only piece for CLI E2E
|
description: Minimal mock-only piece for CLI E2E
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
|
|
||||||
max_movements: 3
|
max_movements: 3
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: e2e-mock-slow-multi-step
|
name: e2e-mock-slow-multi-step
|
||||||
description: Multi-step mock piece to keep tasks in-flight long enough for SIGINT E2E
|
description: Multi-step mock piece to keep tasks in-flight long enough for SIGINT E2E
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
|
|
||||||
max_movements: 20
|
max_movements: 20
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: e2e-mock-two-step
|
name: e2e-mock-two-step
|
||||||
description: Two-step sequential piece for E2E testing
|
description: Two-step sequential piece for E2E testing
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
|
|
||||||
max_movements: 5
|
max_movements: 5
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: e2e-multi-step-parallel
|
name: e2e-multi-step-parallel
|
||||||
description: Multi-step piece with parallel sub-movements for E2E testing
|
description: Multi-step piece with parallel sub-movements for E2E testing
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
|
|
||||||
max_movements: 10
|
max_movements: 10
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: e2e-report-judge
|
name: e2e-report-judge
|
||||||
description: E2E piece that exercises report + judge phases
|
description: E2E piece that exercises report + judge phases
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
|
|
||||||
max_movements: 3
|
max_movements: 3
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: e2e-simple
|
name: e2e-simple
|
||||||
description: Minimal E2E test piece
|
description: Minimal E2E test piece
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
|
|
||||||
max_movements: 5
|
max_movements: 5
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
name: e2e-structured-output
|
name: e2e-structured-output
|
||||||
description: E2E piece to verify structured output rule matching
|
description: E2E piece to verify structured output rule matching
|
||||||
|
piece_config:
|
||||||
|
provider_options:
|
||||||
|
codex:
|
||||||
|
network_access: true
|
||||||
|
opencode:
|
||||||
|
network_access: true
|
||||||
|
|
||||||
|
|
||||||
max_movements: 5
|
max_movements: 5
|
||||||
|
|
||||||
|
|||||||
@ -14,11 +14,13 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|||||||
// ===== Codex SDK mock =====
|
// ===== Codex SDK mock =====
|
||||||
|
|
||||||
let mockEvents: Array<Record<string, unknown>> = [];
|
let mockEvents: Array<Record<string, unknown>> = [];
|
||||||
|
let lastThreadOptions: Record<string, unknown> | undefined;
|
||||||
|
|
||||||
vi.mock('@openai/codex-sdk', () => {
|
vi.mock('@openai/codex-sdk', () => {
|
||||||
return {
|
return {
|
||||||
Codex: class MockCodex {
|
Codex: class MockCodex {
|
||||||
async startThread() {
|
async startThread(options?: Record<string, unknown>) {
|
||||||
|
lastThreadOptions = options;
|
||||||
return {
|
return {
|
||||||
id: 'thread-mock',
|
id: 'thread-mock',
|
||||||
runStreamed: async () => ({
|
runStreamed: async () => ({
|
||||||
@ -44,6 +46,7 @@ describe('CodexClient — structuredOutput 抽出', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
mockEvents = [];
|
mockEvents = [];
|
||||||
|
lastThreadOptions = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('outputSchema 指定時に agent_message の JSON テキストを structuredOutput として返す', async () => {
|
it('outputSchema 指定時に agent_message の JSON テキストを structuredOutput として返す', async () => {
|
||||||
@ -149,4 +152,21 @@ describe('CodexClient — structuredOutput 抽出', () => {
|
|||||||
|
|
||||||
expect(result.structuredOutput).toEqual({ step: 1 });
|
expect(result.structuredOutput).toEqual({ step: 1 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('provider_options.codex.network_access が ThreadOptions に反映される', async () => {
|
||||||
|
mockEvents = [
|
||||||
|
{ type: 'thread.started', thread_id: 'thread-1' },
|
||||||
|
{ type: 'turn.completed', usage: { input_tokens: 0, cached_input_tokens: 0, output_tokens: 0 } },
|
||||||
|
];
|
||||||
|
|
||||||
|
const client = new CodexClient();
|
||||||
|
await client.call('coder', 'prompt', {
|
||||||
|
cwd: '/tmp',
|
||||||
|
networkAccess: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(lastThreadOptions).toMatchObject({
|
||||||
|
networkAccessEnabled: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -105,6 +105,54 @@ describe('PieceConfigRawSchema', () => {
|
|||||||
expect(result.movements![0]?.permission_mode).toBe('edit');
|
expect(result.movements![0]?.permission_mode).toBe('edit');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should parse movement with provider_options', () => {
|
||||||
|
const config = {
|
||||||
|
name: 'test-piece',
|
||||||
|
movements: [
|
||||||
|
{
|
||||||
|
name: 'implement',
|
||||||
|
provider: 'codex',
|
||||||
|
provider_options: {
|
||||||
|
codex: { network_access: true },
|
||||||
|
opencode: { network_access: false },
|
||||||
|
},
|
||||||
|
instruction: '{task}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = PieceConfigRawSchema.parse(config);
|
||||||
|
expect(result.movements![0]?.provider_options).toEqual({
|
||||||
|
codex: { network_access: true },
|
||||||
|
opencode: { network_access: false },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse piece-level piece_config.provider_options', () => {
|
||||||
|
const config = {
|
||||||
|
name: 'test-piece',
|
||||||
|
piece_config: {
|
||||||
|
provider_options: {
|
||||||
|
codex: { network_access: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
movements: [
|
||||||
|
{
|
||||||
|
name: 'implement',
|
||||||
|
provider: 'codex',
|
||||||
|
instruction: '{task}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = PieceConfigRawSchema.parse(config);
|
||||||
|
expect(result.piece_config).toEqual({
|
||||||
|
provider_options: {
|
||||||
|
codex: { network_access: true },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should allow omitting permission_mode', () => {
|
it('should allow omitting permission_mode', () => {
|
||||||
const config = {
|
const config = {
|
||||||
name: 'test-piece',
|
name: 'test-piece',
|
||||||
|
|||||||
@ -84,4 +84,16 @@ describe('OpenCode permissions', () => {
|
|||||||
action: 'deny',
|
action: 'deny',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should force allow web tools when networkAccess=true', () => {
|
||||||
|
const ruleset = buildOpenCodePermissionRuleset('readonly', true);
|
||||||
|
expect(ruleset.find((rule) => rule.permission === 'webfetch')?.action).toBe('allow');
|
||||||
|
expect(ruleset.find((rule) => rule.permission === 'websearch')?.action).toBe('allow');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should force deny web tools when networkAccess=false', () => {
|
||||||
|
const ruleset = buildOpenCodePermissionRuleset('full', false);
|
||||||
|
expect(ruleset.find((rule) => rule.permission === 'webfetch')?.action).toBe('deny');
|
||||||
|
expect(ruleset.find((rule) => rule.permission === 'websearch')?.action).toBe('deny');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
46
src/__tests__/provider-options-piece-parser.test.ts
Normal file
46
src/__tests__/provider-options-piece-parser.test.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { normalizePieceConfig } from '../infra/config/loaders/pieceParser.js';
|
||||||
|
|
||||||
|
describe('normalizePieceConfig provider_options', () => {
|
||||||
|
it('piece-level global を movement に継承し、movement 側で上書きできる', () => {
|
||||||
|
const raw = {
|
||||||
|
name: 'provider-options',
|
||||||
|
piece_config: {
|
||||||
|
provider_options: {
|
||||||
|
codex: { network_access: true },
|
||||||
|
opencode: { network_access: false },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
movements: [
|
||||||
|
{
|
||||||
|
name: 'codex-default',
|
||||||
|
provider: 'codex',
|
||||||
|
instruction: '{task}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'codex-override',
|
||||||
|
provider: 'codex',
|
||||||
|
provider_options: {
|
||||||
|
codex: { network_access: false },
|
||||||
|
},
|
||||||
|
instruction: '{task}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = normalizePieceConfig(raw, process.cwd());
|
||||||
|
|
||||||
|
expect(config.providerOptions).toEqual({
|
||||||
|
codex: { networkAccess: true },
|
||||||
|
opencode: { networkAccess: false },
|
||||||
|
});
|
||||||
|
expect(config.movements[0]?.providerOptions).toEqual({
|
||||||
|
codex: { networkAccess: true },
|
||||||
|
opencode: { networkAccess: false },
|
||||||
|
});
|
||||||
|
expect(config.movements[1]?.providerOptions).toEqual({
|
||||||
|
codex: { networkAccess: false },
|
||||||
|
opencode: { networkAccess: false },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -107,6 +107,7 @@ export class AgentRunner {
|
|||||||
maxTurns: options.maxTurns,
|
maxTurns: options.maxTurns,
|
||||||
model: AgentRunner.resolveModel(resolvedProvider, options, agentConfig),
|
model: AgentRunner.resolveModel(resolvedProvider, options, agentConfig),
|
||||||
permissionMode: options.permissionMode,
|
permissionMode: options.permissionMode,
|
||||||
|
providerOptions: options.providerOptions,
|
||||||
onStream: options.onStream,
|
onStream: options.onStream,
|
||||||
onPermissionRequest: options.onPermissionRequest,
|
onPermissionRequest: options.onPermissionRequest,
|
||||||
onAskUserQuestion: options.onAskUserQuestion,
|
onAskUserQuestion: options.onAskUserQuestion,
|
||||||
|
|||||||
@ -24,6 +24,11 @@ export interface RunAgentOptions {
|
|||||||
maxTurns?: number;
|
maxTurns?: number;
|
||||||
/** Permission mode for tool execution (from piece step) */
|
/** Permission mode for tool execution (from piece step) */
|
||||||
permissionMode?: PermissionMode;
|
permissionMode?: PermissionMode;
|
||||||
|
/** Provider-specific movement options */
|
||||||
|
providerOptions?: {
|
||||||
|
codex?: { networkAccess?: boolean };
|
||||||
|
opencode?: { networkAccess?: boolean };
|
||||||
|
};
|
||||||
onStream?: StreamCallback;
|
onStream?: StreamCallback;
|
||||||
onPermissionRequest?: PermissionHandler;
|
onPermissionRequest?: PermissionHandler;
|
||||||
onAskUserQuestion?: AskUserQuestionHandler;
|
onAskUserQuestion?: AskUserQuestionHandler;
|
||||||
|
|||||||
@ -80,6 +80,24 @@ export interface McpHttpServerConfig {
|
|||||||
/** MCP server configuration (union of all YAML-configurable transports) */
|
/** MCP server configuration (union of all YAML-configurable transports) */
|
||||||
export type McpServerConfig = McpStdioServerConfig | McpSseServerConfig | McpHttpServerConfig;
|
export type McpServerConfig = McpStdioServerConfig | McpSseServerConfig | McpHttpServerConfig;
|
||||||
|
|
||||||
|
/** Codex provider-specific options */
|
||||||
|
export interface CodexProviderOptions {
|
||||||
|
/** Enable network access for Codex workspace-write sandbox */
|
||||||
|
networkAccess?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** OpenCode provider-specific options */
|
||||||
|
export interface OpenCodeProviderOptions {
|
||||||
|
/** Enable/disable network tools (webfetch/websearch) */
|
||||||
|
networkAccess?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Provider-specific movement options */
|
||||||
|
export interface MovementProviderOptions {
|
||||||
|
codex?: CodexProviderOptions;
|
||||||
|
opencode?: OpenCodeProviderOptions;
|
||||||
|
}
|
||||||
|
|
||||||
/** Single movement in a piece */
|
/** Single movement in a piece */
|
||||||
export interface PieceMovement {
|
export interface PieceMovement {
|
||||||
name: string;
|
name: string;
|
||||||
@ -103,6 +121,8 @@ export interface PieceMovement {
|
|||||||
model?: string;
|
model?: string;
|
||||||
/** Permission mode for tool execution in this movement */
|
/** Permission mode for tool execution in this movement */
|
||||||
permissionMode?: PermissionMode;
|
permissionMode?: PermissionMode;
|
||||||
|
/** Provider-specific movement options */
|
||||||
|
providerOptions?: MovementProviderOptions;
|
||||||
/** Whether this movement is allowed to edit project files (true=allowed, false=prohibited, undefined=no prompt) */
|
/** Whether this movement is allowed to edit project files (true=allowed, false=prohibited, undefined=no prompt) */
|
||||||
edit?: boolean;
|
edit?: boolean;
|
||||||
instructionTemplate: string;
|
instructionTemplate: string;
|
||||||
@ -201,6 +221,8 @@ export interface LoopMonitorConfig {
|
|||||||
export interface PieceConfig {
|
export interface PieceConfig {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
/** Piece-level default provider options (used as movement defaults) */
|
||||||
|
providerOptions?: MovementProviderOptions;
|
||||||
/** Persona definitions — map of name to file path or inline content (raw, not content-resolved) */
|
/** Persona definitions — map of name to file path or inline content (raw, not content-resolved) */
|
||||||
personas?: Record<string, string>;
|
personas?: Record<string, string>;
|
||||||
/** Resolved policy definitions — map of name to file content (resolved at parse time) */
|
/** Resolved policy definitions — map of name to file content (resolved at parse time) */
|
||||||
|
|||||||
@ -59,6 +59,20 @@ export const StatusSchema = z.enum([
|
|||||||
|
|
||||||
/** Permission mode schema for tool execution */
|
/** Permission mode schema for tool execution */
|
||||||
export const PermissionModeSchema = z.enum(['readonly', 'edit', 'full']);
|
export const PermissionModeSchema = z.enum(['readonly', 'edit', 'full']);
|
||||||
|
/** Provider-specific movement options schema */
|
||||||
|
export const MovementProviderOptionsSchema = z.object({
|
||||||
|
codex: z.object({
|
||||||
|
network_access: z.boolean().optional(),
|
||||||
|
}).optional(),
|
||||||
|
opencode: z.object({
|
||||||
|
network_access: z.boolean().optional(),
|
||||||
|
}).optional(),
|
||||||
|
}).optional();
|
||||||
|
|
||||||
|
/** Piece-level provider options schema */
|
||||||
|
export const PieceProviderOptionsSchema = z.object({
|
||||||
|
provider_options: MovementProviderOptionsSchema,
|
||||||
|
}).optional();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Output contract item schema (new structured format).
|
* Output contract item schema (new structured format).
|
||||||
@ -204,6 +218,7 @@ export const ParallelSubMovementRawSchema = z.object({
|
|||||||
provider: z.enum(['claude', 'codex', 'opencode', 'mock']).optional(),
|
provider: z.enum(['claude', 'codex', 'opencode', 'mock']).optional(),
|
||||||
model: z.string().optional(),
|
model: z.string().optional(),
|
||||||
permission_mode: PermissionModeSchema.optional(),
|
permission_mode: PermissionModeSchema.optional(),
|
||||||
|
provider_options: MovementProviderOptionsSchema,
|
||||||
edit: z.boolean().optional(),
|
edit: z.boolean().optional(),
|
||||||
instruction: z.string().optional(),
|
instruction: z.string().optional(),
|
||||||
instruction_template: z.string().optional(),
|
instruction_template: z.string().optional(),
|
||||||
@ -235,6 +250,8 @@ export const PieceMovementRawSchema = z.object({
|
|||||||
model: z.string().optional(),
|
model: z.string().optional(),
|
||||||
/** Permission mode for tool execution in this movement */
|
/** Permission mode for tool execution in this movement */
|
||||||
permission_mode: PermissionModeSchema.optional(),
|
permission_mode: PermissionModeSchema.optional(),
|
||||||
|
/** Provider-specific movement options */
|
||||||
|
provider_options: MovementProviderOptionsSchema,
|
||||||
/** Whether this movement is allowed to edit project files */
|
/** Whether this movement is allowed to edit project files */
|
||||||
edit: z.boolean().optional(),
|
edit: z.boolean().optional(),
|
||||||
instruction: z.string().optional(),
|
instruction: z.string().optional(),
|
||||||
@ -295,6 +312,7 @@ export const InteractiveModeSchema = z.enum(INTERACTIVE_MODES);
|
|||||||
export const PieceConfigRawSchema = z.object({
|
export const PieceConfigRawSchema = z.object({
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
|
piece_config: PieceProviderOptionsSchema,
|
||||||
/** Piece-level persona definitions — map of name to .md file path or inline content */
|
/** Piece-level persona definitions — map of name to .md file path or inline content */
|
||||||
personas: z.record(z.string(), z.string()).optional(),
|
personas: z.record(z.string(), z.string()).optional(),
|
||||||
/** Piece-level policy definitions — map of name to .md file path or inline content */
|
/** Piece-level policy definitions — map of name to .md file path or inline content */
|
||||||
|
|||||||
@ -38,6 +38,7 @@ export class OptionsBuilder {
|
|||||||
provider: resolved.provider,
|
provider: resolved.provider,
|
||||||
model: resolved.model,
|
model: resolved.model,
|
||||||
permissionMode: step.permissionMode,
|
permissionMode: step.permissionMode,
|
||||||
|
providerOptions: step.providerOptions,
|
||||||
language: this.getLanguage(),
|
language: this.getLanguage(),
|
||||||
onStream: this.engineOptions.onStream,
|
onStream: this.engineOptions.onStream,
|
||||||
onPermissionRequest: this.engineOptions.onPermissionRequest,
|
onPermissionRequest: this.engineOptions.onPermissionRequest,
|
||||||
|
|||||||
@ -95,6 +95,7 @@ export class CodexClient {
|
|||||||
...(options.model ? { model: options.model } : {}),
|
...(options.model ? { model: options.model } : {}),
|
||||||
workingDirectory: options.cwd,
|
workingDirectory: options.cwd,
|
||||||
sandboxMode,
|
sandboxMode,
|
||||||
|
...(options.networkAccess === undefined ? {} : { networkAccessEnabled: options.networkAccess }),
|
||||||
};
|
};
|
||||||
let threadId = options.sessionId;
|
let threadId = options.sessionId;
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,8 @@ export interface CodexCallOptions {
|
|||||||
systemPrompt?: string;
|
systemPrompt?: string;
|
||||||
/** Permission mode for sandbox configuration */
|
/** Permission mode for sandbox configuration */
|
||||||
permissionMode?: PermissionMode;
|
permissionMode?: PermissionMode;
|
||||||
|
/** Enable network access for workspace-write sandbox */
|
||||||
|
networkAccess?: boolean;
|
||||||
/** Enable streaming mode with callback (best-effort) */
|
/** Enable streaming mode with callback (best-effort) */
|
||||||
onStream?: StreamCallback;
|
onStream?: StreamCallback;
|
||||||
/** OpenAI API key (bypasses CLI auth) */
|
/** OpenAI API key (bypasses CLI auth) */
|
||||||
|
|||||||
@ -24,6 +24,36 @@ import {
|
|||||||
|
|
||||||
type RawStep = z.output<typeof PieceMovementRawSchema>;
|
type RawStep = z.output<typeof PieceMovementRawSchema>;
|
||||||
|
|
||||||
|
function normalizeProviderOptions(
|
||||||
|
raw: RawStep['provider_options'],
|
||||||
|
): PieceMovement['providerOptions'] {
|
||||||
|
if (!raw) return undefined;
|
||||||
|
|
||||||
|
const codex = raw.codex?.network_access === undefined
|
||||||
|
? undefined
|
||||||
|
: { networkAccess: raw.codex.network_access };
|
||||||
|
const opencode = raw.opencode?.network_access === undefined
|
||||||
|
? undefined
|
||||||
|
: { networkAccess: raw.opencode.network_access };
|
||||||
|
|
||||||
|
if (!codex && !opencode) return undefined;
|
||||||
|
return { ...(codex ? { codex } : {}), ...(opencode ? { opencode } : {}) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeProviderOptions(
|
||||||
|
base: PieceMovement['providerOptions'],
|
||||||
|
override: PieceMovement['providerOptions'],
|
||||||
|
): PieceMovement['providerOptions'] {
|
||||||
|
const codexNetworkAccess = override?.codex?.networkAccess ?? base?.codex?.networkAccess;
|
||||||
|
const opencodeNetworkAccess = override?.opencode?.networkAccess ?? base?.opencode?.networkAccess;
|
||||||
|
|
||||||
|
const codex = codexNetworkAccess === undefined ? undefined : { networkAccess: codexNetworkAccess };
|
||||||
|
const opencode = opencodeNetworkAccess === undefined ? undefined : { networkAccess: opencodeNetworkAccess };
|
||||||
|
|
||||||
|
if (!codex && !opencode) return undefined;
|
||||||
|
return { ...(codex ? { codex } : {}), ...(opencode ? { opencode } : {}) };
|
||||||
|
}
|
||||||
|
|
||||||
/** Check if a raw output contract item is the object form (has 'name' property). */
|
/** Check if a raw output contract item is the object form (has 'name' property). */
|
||||||
function isOutputContractItem(raw: unknown): raw is { name: string; order?: string; format?: string } {
|
function isOutputContractItem(raw: unknown): raw is { name: string; order?: string; format?: string } {
|
||||||
return typeof raw === 'object' && raw !== null && !Array.isArray(raw) && 'name' in raw;
|
return typeof raw === 'object' && raw !== null && !Array.isArray(raw) && 'name' in raw;
|
||||||
@ -209,6 +239,7 @@ function normalizeStepFromRaw(
|
|||||||
step: RawStep,
|
step: RawStep,
|
||||||
pieceDir: string,
|
pieceDir: string,
|
||||||
sections: PieceSections,
|
sections: PieceSections,
|
||||||
|
inheritedProviderOptions?: PieceMovement['providerOptions'],
|
||||||
context?: FacetResolutionContext,
|
context?: FacetResolutionContext,
|
||||||
): PieceMovement {
|
): PieceMovement {
|
||||||
const rules: PieceRule[] | undefined = step.rules?.map(normalizeRule);
|
const rules: PieceRule[] | undefined = step.rules?.map(normalizeRule);
|
||||||
@ -241,6 +272,7 @@ function normalizeStepFromRaw(
|
|||||||
provider: step.provider,
|
provider: step.provider,
|
||||||
model: step.model,
|
model: step.model,
|
||||||
permissionMode: step.permission_mode,
|
permissionMode: step.permission_mode,
|
||||||
|
providerOptions: mergeProviderOptions(inheritedProviderOptions, normalizeProviderOptions(step.provider_options)),
|
||||||
edit: step.edit,
|
edit: step.edit,
|
||||||
instructionTemplate: (step.instruction_template
|
instructionTemplate: (step.instruction_template
|
||||||
? resolveRefToContent(step.instruction_template, sections.resolvedInstructions, pieceDir, 'instructions', context)
|
? resolveRefToContent(step.instruction_template, sections.resolvedInstructions, pieceDir, 'instructions', context)
|
||||||
@ -254,7 +286,9 @@ function normalizeStepFromRaw(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (step.parallel && step.parallel.length > 0) {
|
if (step.parallel && step.parallel.length > 0) {
|
||||||
result.parallel = step.parallel.map((sub: RawStep) => normalizeStepFromRaw(sub, pieceDir, sections, context));
|
result.parallel = step.parallel.map((sub: RawStep) =>
|
||||||
|
normalizeStepFromRaw(sub, pieceDir, sections, inheritedProviderOptions, context),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const arpeggioConfig = normalizeArpeggio(step.arpeggio, pieceDir);
|
const arpeggioConfig = normalizeArpeggio(step.arpeggio, pieceDir);
|
||||||
@ -327,8 +361,10 @@ export function normalizePieceConfig(
|
|||||||
resolvedReportFormats,
|
resolvedReportFormats,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const pieceProviderOptions = normalizeProviderOptions(parsed.piece_config?.provider_options as RawStep['provider_options']);
|
||||||
|
|
||||||
const movements: PieceMovement[] = parsed.movements.map((step) =>
|
const movements: PieceMovement[] = parsed.movements.map((step) =>
|
||||||
normalizeStepFromRaw(step, pieceDir, sections, context),
|
normalizeStepFromRaw(step, pieceDir, sections, pieceProviderOptions, context),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Schema guarantees movements.min(1)
|
// Schema guarantees movements.min(1)
|
||||||
@ -337,6 +373,7 @@ export function normalizePieceConfig(
|
|||||||
return {
|
return {
|
||||||
name: parsed.name,
|
name: parsed.name,
|
||||||
description: parsed.description,
|
description: parsed.description,
|
||||||
|
providerOptions: pieceProviderOptions,
|
||||||
personas: parsed.personas,
|
personas: parsed.personas,
|
||||||
policies: resolvedPolicies,
|
policies: resolvedPolicies,
|
||||||
knowledge: resolvedKnowledge,
|
knowledge: resolvedKnowledge,
|
||||||
|
|||||||
@ -311,7 +311,7 @@ export class OpenCodeClient {
|
|||||||
const parsedModel = parseProviderModel(options.model, 'OpenCode model');
|
const parsedModel = parseProviderModel(options.model, 'OpenCode model');
|
||||||
const fullModel = `${parsedModel.providerID}/${parsedModel.modelID}`;
|
const fullModel = `${parsedModel.providerID}/${parsedModel.modelID}`;
|
||||||
const port = await getFreePort();
|
const port = await getFreePort();
|
||||||
const permission = buildOpenCodePermissionConfig(options.permissionMode);
|
const permission = buildOpenCodePermissionConfig(options.permissionMode, options.networkAccess);
|
||||||
const config = {
|
const config = {
|
||||||
model: fullModel,
|
model: fullModel,
|
||||||
small_model: fullModel,
|
small_model: fullModel,
|
||||||
@ -332,7 +332,7 @@ export class OpenCodeClient {
|
|||||||
? { data: { id: options.sessionId } }
|
? { data: { id: options.sessionId } }
|
||||||
: await client.session.create({
|
: await client.session.create({
|
||||||
directory: options.cwd,
|
directory: options.cwd,
|
||||||
permission: buildOpenCodePermissionRuleset(options.permissionMode),
|
permission: buildOpenCodePermissionRuleset(options.permissionMode, options.networkAccess),
|
||||||
});
|
});
|
||||||
|
|
||||||
const sessionId = sessionResult.data?.id;
|
const sessionId = sessionResult.data?.id;
|
||||||
|
|||||||
@ -100,14 +100,38 @@ function buildPermissionMap(mode?: PermissionMode): OpenCodePermissionMap {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildOpenCodePermissionConfig(mode?: PermissionMode): OpenCodePermissionAction | Record<string, OpenCodePermissionAction> {
|
function applyNetworkAccessOverride(
|
||||||
if (mode === 'readonly') return 'deny';
|
map: OpenCodePermissionMap,
|
||||||
if (mode === 'full') return 'allow';
|
networkAccess?: boolean,
|
||||||
return buildPermissionMap(mode);
|
): OpenCodePermissionMap {
|
||||||
|
if (networkAccess === undefined) {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
const action: OpenCodePermissionAction = networkAccess ? 'allow' : 'deny';
|
||||||
|
return {
|
||||||
|
...map,
|
||||||
|
webfetch: action,
|
||||||
|
websearch: action,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildOpenCodePermissionRuleset(mode?: PermissionMode): Array<{ permission: string; pattern: string; action: OpenCodePermissionAction }> {
|
export function buildOpenCodePermissionConfig(
|
||||||
const permissionMap = buildPermissionMap(mode);
|
mode?: PermissionMode,
|
||||||
|
networkAccess?: boolean,
|
||||||
|
): OpenCodePermissionAction | Record<string, OpenCodePermissionAction> {
|
||||||
|
if (networkAccess === undefined) {
|
||||||
|
if (mode === 'readonly') return 'deny';
|
||||||
|
if (mode === 'full') return 'allow';
|
||||||
|
}
|
||||||
|
return applyNetworkAccessOverride(buildPermissionMap(mode), networkAccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildOpenCodePermissionRuleset(
|
||||||
|
mode?: PermissionMode,
|
||||||
|
networkAccess?: boolean,
|
||||||
|
): Array<{ permission: string; pattern: string; action: OpenCodePermissionAction }> {
|
||||||
|
const permissionMap = applyNetworkAccessOverride(buildPermissionMap(mode), networkAccess);
|
||||||
return OPEN_CODE_PERMISSION_KEYS.map((permission) => ({
|
return OPEN_CODE_PERMISSION_KEYS.map((permission) => ({
|
||||||
permission,
|
permission,
|
||||||
pattern: '**',
|
pattern: '**',
|
||||||
@ -165,6 +189,8 @@ export interface OpenCodeCallOptions {
|
|||||||
allowedTools?: string[];
|
allowedTools?: string[];
|
||||||
/** Permission mode for automatic permission handling */
|
/** Permission mode for automatic permission handling */
|
||||||
permissionMode?: PermissionMode;
|
permissionMode?: PermissionMode;
|
||||||
|
/** Override network access (webfetch/websearch) */
|
||||||
|
networkAccess?: boolean;
|
||||||
/** Enable streaming mode with callback (best-effort) */
|
/** Enable streaming mode with callback (best-effort) */
|
||||||
onStream?: StreamCallback;
|
onStream?: StreamCallback;
|
||||||
onAskUserQuestion?: AskUserQuestionHandler;
|
onAskUserQuestion?: AskUserQuestionHandler;
|
||||||
|
|||||||
@ -31,6 +31,7 @@ function toCodexOptions(options: ProviderCallOptions): CodexCallOptions {
|
|||||||
sessionId: options.sessionId,
|
sessionId: options.sessionId,
|
||||||
model: options.model,
|
model: options.model,
|
||||||
permissionMode: options.permissionMode,
|
permissionMode: options.permissionMode,
|
||||||
|
networkAccess: options.providerOptions?.codex?.networkAccess,
|
||||||
onStream: options.onStream,
|
onStream: options.onStream,
|
||||||
openaiApiKey: options.openaiApiKey ?? resolveOpenaiApiKey(),
|
openaiApiKey: options.openaiApiKey ?? resolveOpenaiApiKey(),
|
||||||
outputSchema: options.outputSchema,
|
outputSchema: options.outputSchema,
|
||||||
|
|||||||
@ -19,6 +19,7 @@ function toOpenCodeOptions(options: ProviderCallOptions): OpenCodeCallOptions {
|
|||||||
model: options.model,
|
model: options.model,
|
||||||
allowedTools: options.allowedTools,
|
allowedTools: options.allowedTools,
|
||||||
permissionMode: options.permissionMode,
|
permissionMode: options.permissionMode,
|
||||||
|
networkAccess: options.providerOptions?.opencode?.networkAccess,
|
||||||
onStream: options.onStream,
|
onStream: options.onStream,
|
||||||
onAskUserQuestion: options.onAskUserQuestion,
|
onAskUserQuestion: options.onAskUserQuestion,
|
||||||
opencodeApiKey: options.opencodeApiKey ?? resolveOpencodeApiKey(),
|
opencodeApiKey: options.opencodeApiKey ?? resolveOpencodeApiKey(),
|
||||||
|
|||||||
@ -30,6 +30,11 @@ export interface ProviderCallOptions {
|
|||||||
maxTurns?: number;
|
maxTurns?: number;
|
||||||
/** Permission mode for tool execution (from piece step) */
|
/** Permission mode for tool execution (from piece step) */
|
||||||
permissionMode?: PermissionMode;
|
permissionMode?: PermissionMode;
|
||||||
|
/** Provider-specific movement options */
|
||||||
|
providerOptions?: {
|
||||||
|
codex?: { networkAccess?: boolean };
|
||||||
|
opencode?: { networkAccess?: boolean };
|
||||||
|
};
|
||||||
onStream?: StreamCallback;
|
onStream?: StreamCallback;
|
||||||
onPermissionRequest?: PermissionHandler;
|
onPermissionRequest?: PermissionHandler;
|
||||||
onAskUserQuestion?: AskUserQuestionHandler;
|
onAskUserQuestion?: AskUserQuestionHandler;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user