diff --git a/.gitignore b/.gitignore index 42938c9..28a5490 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,8 @@ dist /agentcore /roboppi -# Local AgentCore workflow state -.agentcore-loop/ +# Local Roboppi workflow state +.roboppi-loop/ # Local scratch notes (do not commit) docs/wip/ diff --git a/AGENTS.md b/AGENTS.md index 5a08404..c728612 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # AGENTS.md -This repository ("Roboppi" / package name: `agentcore`) is a Bun + TypeScript (ESM) +This repository ("Roboppi" / package name: `roboppi`) is a Bun + TypeScript (ESM) codebase that implements an execution-control runtime for agentic workers. ## Build / Lint / Test @@ -57,11 +57,11 @@ bun test --watch test/unit # watch mode ### Build -Builds a native executable `./agentcore` (gitignored). +Builds a native executable `./roboppi` (gitignored). ```bash make build # or: bun run build -make install # /usr/local/bin/agentcore +make install # /usr/local/bin/roboppi make uninstall ``` diff --git a/Makefile b/Makefile index 629279f..f4eb90b 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,6 @@ .PHONY: all build test tests test-unit test-integration test-at test-branch test-all typecheck clean install uninstall dev help BIN := roboppi -LEGACY_BIN := agentcore # Legacy build artifacts (no longer built by default) EXTRA_BINS := roboppi-core roboppi-workflow roboppi-daemon @@ -20,7 +19,6 @@ all: build ## Build the binary (default) build: ## Build a single binary (roboppi) $(BUN) $(BUN_FLAGS) build $(BUN_BUILD_FLAGS) --compile $(SRC) --outfile $(BIN) - @ln -sf $(BIN) $(LEGACY_BIN) 2>/dev/null || cp $(BIN) $(LEGACY_BIN) test: ## Run all tests $(BUN) $(BUN_FLAGS) test @@ -46,14 +44,13 @@ typecheck: ## Run TypeScript type checker $(BUN) $(BUN_FLAGS) x tsc --noEmit clean: ## Remove build artifacts - rm -f $(BIN) $(LEGACY_BIN) $(EXTRA_BINS) + rm -f $(BIN) $(EXTRA_BINS) install: build ## Install binary to /usr/local/bin install -m 755 $(BIN) /usr/local/bin/$(BIN) - @ln -sf /usr/local/bin/$(BIN) /usr/local/bin/$(LEGACY_BIN) 2>/dev/null || cp /usr/local/bin/$(BIN) /usr/local/bin/$(LEGACY_BIN) uninstall: ## Remove binary from /usr/local/bin - rm -f /usr/local/bin/$(BIN) /usr/local/bin/$(LEGACY_BIN) $(addprefix /usr/local/bin/,$(EXTRA_BINS)) + rm -f /usr/local/bin/$(BIN) $(addprefix /usr/local/bin/,$(EXTRA_BINS)) dev: ## Run in dev mode (no build) $(BUN) $(BUN_FLAGS) run $(SRC) diff --git a/README.ja.md b/README.ja.md index a1190ff..2327188 100644 --- a/README.ja.md +++ b/README.ja.md @@ -7,8 +7,7 @@ Roboppi(ろぼっぴ)は、エージェント(worker)の実行を安全 Roboppi 自身が「エージェントとして仕事をする」ことは目的にしません。外部の worker CLI(OpenCode / Claude Code / Codex CLI / シェル)に重い作業(編集/コマンド/テスト)を委譲し、Roboppi は安全の不変条件(止める/制限する/観測する/隔離する)を強制します。 注意: 主な CLI/バイナリ名は `roboppi` です。 -- 互換: `agentcore` はレガシー alias として残しています。 -- 環境変数/状態ディレクトリ: `ROBOPPI_` / `.roboppi-loop/` を推奨(レガシーの `AGENTCORE_` / `.agentcore-loop/` も引き続き利用可能)。 +- 環境変数/状態ディレクトリ: `ROBOPPI_` / `.roboppi-loop/` を使います。 ## Roboppiでできること(Capabilities) @@ -184,7 +183,7 @@ ROBOPPI_ROOT="$ROBOPPI_ROOT" bun run --cwd "$ROBOPPI_ROOT" src/workflow/run.ts \ 注意: -- workflow は target workspace 内に `.roboppi-loop/` と `context/` を作ります(通常は gitignore 推奨。レガシーの `.agentcore-loop/` も引き続き利用可能)。 +- workflow は target workspace 内に `.roboppi-loop/` と `context/` を作ります(通常は gitignore 推奨)。 - PR 作成は `.roboppi-loop/enable_pr` による opt-in です。 ## Workflow YAML の最小例 @@ -264,9 +263,9 @@ git repo に対する workflow 実行向けに、base branch 解決 / Branch Loc supervised 実行では runner/daemon が Core 子プロセスを起動し、IPC 経由で step を委譲します。 - 既定: 対話実行は stdio、非対話実行は socket transport が既定 -- transport 切替: `AGENTCORE_SUPERVISED_IPC_TRANSPORT=stdio|socket|tcp` -- IPC デバッグ: `AGENTCORE_IPC_TRACE=1` -- request timeout 調整: `AGENTCORE_IPC_REQUEST_TIMEOUT=2m`(または `AGENTCORE_IPC_REQUEST_TIMEOUT_MS=120000`) +- transport 切替: `ROBOPPI_SUPERVISED_IPC_TRANSPORT=stdio|socket|tcp` +- IPC デバッグ: `ROBOPPI_IPC_TRACE=1` +- request timeout 調整: `ROBOPPI_IPC_REQUEST_TIMEOUT=2m`(または `ROBOPPI_IPC_REQUEST_TIMEOUT_MS=120000`) supervised 実行を無効化(direct 実行): diff --git a/README.md b/README.md index e031114..ebe2d95 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,7 @@ Roboppi (ろぼっぴ, pronounced "roh-boh-pee") is an execution-control runtime It does not try to be "the agent". It enforces safety invariants around external worker CLIs (OpenCode / Claude Code / Codex CLI / plain shell), so your automation stops when it should, stays within budgets, and leaves a trail you can audit. Note: the primary CLI/binary name is `roboppi`. -- Compatibility: `agentcore` remains as a legacy alias. -- Env vars/state dirs: prefer `ROBOPPI_` / `.roboppi-loop/` (legacy `AGENTCORE_` / `.agentcore-loop/` are still accepted). +- Env vars/state dirs: use `ROBOPPI_` / `.roboppi-loop/`. ## What Roboppi Can Do (Capabilities) @@ -191,7 +190,7 @@ ROBOPPI_ROOT="$ROBOPPI_ROOT" bun run --cwd "$ROBOPPI_ROOT" src/workflow/run.ts \ Notes: -- The workflow writes state/artifacts under `.roboppi-loop/` and `context/` inside the target workspace (typically gitignored; legacy `.agentcore-loop/` is still accepted). +- The workflow writes state/artifacts under `.roboppi-loop/` and `context/` inside the target workspace (typically gitignored). - PR creation is opt-in via `.roboppi-loop/enable_pr`. ## Workflow YAML At A Glance @@ -273,9 +272,9 @@ Docs: Workflows/daemon run supervised by default. In supervised mode, the runner starts a Core child process and delegates steps over IPC. - Default: interactive runs typically use stdio; non-interactive runs default to a socket transport. -- Override transport: `AGENTCORE_SUPERVISED_IPC_TRANSPORT=stdio|socket|tcp` -- Debug IPC: `AGENTCORE_IPC_TRACE=1` -- Tune request timeouts: `AGENTCORE_IPC_REQUEST_TIMEOUT=2m` (or `AGENTCORE_IPC_REQUEST_TIMEOUT_MS=120000`) +- Override transport: `ROBOPPI_SUPERVISED_IPC_TRANSPORT=stdio|socket|tcp` +- Debug IPC: `ROBOPPI_IPC_TRACE=1` +- Tune request timeouts: `ROBOPPI_IPC_REQUEST_TIMEOUT=2m` (or `ROBOPPI_IPC_REQUEST_TIMEOUT_MS=120000`) Disable supervised mode: diff --git a/bun.lock b/bun.lock index b2f7179..5fa6d88 100644 --- a/bun.lock +++ b/bun.lock @@ -3,7 +3,7 @@ "configVersion": 1, "workspaces": { "": { - "name": "agentcore", + "name": "roboppi", "dependencies": { "yaml": "^2.8.2", }, diff --git a/docs/daemon-design.md b/docs/daemon-design.md index 94b967a..3be410b 100644 --- a/docs/daemon-design.md +++ b/docs/daemon-design.md @@ -820,23 +820,23 @@ Template variables available in `evaluate` / `analyze` instructions: ```bash # start daemon -agentcore daemon start [--verbose] +roboppi daemon start [--verbose] # check daemon status (from another terminal) -agentcore daemon status [--state-dir ] +roboppi daemon status [--state-dir ] # manual trigger (debug) -agentcore daemon trigger [--state-dir ] +roboppi daemon trigger [--state-dir ] # pause/resume trigger -agentcore daemon pause -agentcore daemon resume +roboppi daemon pause +roboppi daemon resume # show execution history -agentcore daemon history [--trigger ] [--limit 10] +roboppi daemon history [--trigger ] [--limit 10] # stop daemon -agentcore daemon stop [--state-dir ] +roboppi daemon stop [--state-dir ] ``` --- @@ -945,7 +945,7 @@ Log tags: - [ ] TriggerEngine (filter + debounce + cooldown) - [ ] DaemonStateStore (file-based persistence) - [ ] Daemon class (event loop + graceful shutdown) -- [ ] CLI: `agentcore daemon start` +- [ ] CLI: `roboppi daemon start` ### Phase 2: Expand event sources diff --git a/docs/design.md b/docs/design.md index c2f87a5..39c7bb0 100644 --- a/docs/design.md +++ b/docs/design.md @@ -121,19 +121,19 @@ Workers provide execution environments and capabilities. AgentCore calls them th ``` Scheduler (parent) | - +-- spawn(agentcore, args...) + +-- spawn(roboppi, args...) +-- JSON Lines IPC over stdin/stdout +-- periodic heartbeat/health monitoring | - +-> AgentCore (child) + +-> Roboppi Core (child) +-- processes externally submitted jobs +-- issues permits and delegates to workers +-- observes, limits, cuts off, escalates ``` -The Scheduler spawns AgentCore as a child process, keeps stdin/stdout open, and uses **JSON Lines** for bidirectional communication. It monitors response latency via heartbeat and checks memory/CPU thresholds periodically. +The Scheduler spawns Roboppi Core as a child process, keeps stdin/stdout open, and uses **JSON Lines** for bidirectional communication. It monitors response latency via heartbeat and checks memory/CPU thresholds periodically. -AgentCore behaves as a runtime that processes "jobs given from outside", issues Permits, and spawns/uses workers as needed. +Roboppi Core behaves as a runtime that processes "jobs given from outside", issues Permits, and spawns/uses workers as needed. ### 3.2 Crash / hang handling (Supervisor policy) diff --git a/docs/guide/daemon.md b/docs/guide/daemon.md index 36077f4..21344a1 100644 --- a/docs/guide/daemon.md +++ b/docs/guide/daemon.md @@ -133,9 +133,9 @@ triggers: # trigger definitions (event -> workflow) If your workflows use `step.agent`, provide an agent catalog to the daemon: - `agents_file`: a default catalog path for the daemon (relative paths are resolved from `workspace`) -- `AGENTCORE_AGENTS_FILE`: you can also set this via environment variables (or per-trigger via `triggers..context.env`) as a colon-separated list of YAML paths +- `ROBOPPI_AGENTS_FILE`: you can also set this via environment variables (or per-trigger via `triggers..context.env`) as a colon-separated list of YAML paths -When multiple catalogs are provided, later sources override earlier ones (so a per-trigger `context.env.AGENTCORE_AGENTS_FILE` can override the daemon-wide catalog). +When multiple catalogs are provided, later sources override earlier ones (so a per-trigger `context.env.ROBOPPI_AGENTS_FILE` can override the daemon-wide catalog). See also: `docs/guides/agents.md`. @@ -703,8 +703,8 @@ name: simple-cron version: "1" description: "A simple daemon that runs health checks every 30 seconds" -workspace: "/tmp/agentcore-daemon-simple" -state_dir: "/tmp/agentcore-daemon-simple/.daemon-state" +workspace: "/tmp/roboppi-daemon-simple" +state_dir: "/tmp/roboppi-daemon-simple/.daemon-state" events: tick: @@ -734,8 +734,8 @@ Fires every 5 minutes via cron. An evaluate gate checks whether there are new co ```yaml name: smart-reviewer version: "1" -workspace: "/tmp/agentcore-daemon-reviewer" -state_dir: "/tmp/agentcore-daemon-reviewer/.daemon-state" +workspace: "/tmp/roboppi-daemon-reviewer" +state_dir: "/tmp/roboppi-daemon-reviewer/.daemon-state" max_concurrent_workflows: 1 events: @@ -800,8 +800,8 @@ Detect TypeScript file changes and run tests automatically. ```yaml name: file-watcher version: "1" -workspace: "/tmp/agentcore-daemon-watcher" -state_dir: "/tmp/agentcore-daemon-watcher/.daemon-state" +workspace: "/tmp/roboppi-daemon-watcher" +state_dir: "/tmp/roboppi-daemon-watcher/.daemon-state" events: src-change: @@ -838,8 +838,8 @@ Use interval, cron, and command event sources together and start different workf ```yaml name: multi-trigger version: "1" -workspace: "/tmp/agentcore-daemon-multi" -state_dir: "/tmp/agentcore-daemon-multi/.daemon-state" +workspace: "/tmp/roboppi-daemon-multi" +state_dir: "/tmp/roboppi-daemon-multi/.daemon-state" max_concurrent_workflows: 2 events: diff --git a/docs/guide/quickstart.md b/docs/guide/quickstart.md index 0af5564..b8d0b9d 100644 --- a/docs/guide/quickstart.md +++ b/docs/guide/quickstart.md @@ -1,6 +1,6 @@ -# AgentCore Quickstart +# Roboppi Quickstart -AgentCore is an execution-control runtime for AI agent systems. It provides safe execution control via Permits, failure cut-off via Circuit Breakers, and delegation of actual work to external Workers. +Roboppi is an execution-control runtime for AI agent systems. It provides safe execution control via Permits, failure cut-off via Circuit Breakers, and delegation of actual work to external Workers. ## Prerequisites @@ -19,22 +19,22 @@ Optional (if you plan to use them as workers): ## Install ```bash -git clone agentcore -cd agentcore +git clone roboppi +cd roboppi bun install ``` ## Build ```bash -make # build a single binary -> ./roboppi (+ ./agentcore alias) +make # build a single binary -> ./roboppi ``` Sanity check: ```bash -./agentcore --version -./agentcore --help +./roboppi --version +./roboppi --help ``` Other make targets: @@ -48,23 +48,23 @@ make install # install to /usr/local/bin --- -## Usage 1: One-shot execution with `agentcore run` +## Usage 1: One-shot execution with `roboppi run` Use the `run` subcommand to delegate a task to a Worker using only CLI flags. ```bash # Generate files with OpenCode -./agentcore run --worker opencode --workspace /tmp/demo "Create hello.ts" +./roboppi run --worker opencode --workspace /tmp/demo "Create hello.ts" # Fix tests with Claude Code -./agentcore run --worker claude-code --workspace ./my-project \ +./roboppi run --worker claude-code --workspace ./my-project \ --capabilities EDIT,RUN_TESTS "Fix the failing tests" # Refactor with Codex CLI -./agentcore run --worker codex --workspace ./src "Refactor this function" +./roboppi run --worker codex --workspace ./src "Refactor this function" # You can also set timeouts and budgets -./agentcore run --worker opencode --workspace /tmp/demo \ +./roboppi run --worker opencode --workspace /tmp/demo \ --timeout 60000 --concurrency 5 "Write a README for this repo" ``` @@ -77,20 +77,20 @@ Use the `run` subcommand to delegate a task to a Worker using only CLI flags. | `--capabilities ` | `READ,EDIT,RUN_TESTS,RUN_COMMANDS` | `EDIT` | | `--timeout ` | task timeout | `120000` | -Internally, AgentCore checks PermitGate -> CircuitBreaker -> ExecutionBudget before delegating to the worker. +Internally, Roboppi checks PermitGate -> CircuitBreaker -> ExecutionBudget before delegating to the worker. --- ## Usage 2: IPC server mode -If you run `agentcore` with no subcommand, it starts in IPC server mode. A Scheduler or custom driver can talk to it over JSON Lines. +If you run `roboppi` with no subcommand, it starts in IPC server mode. A Scheduler or custom driver can talk to it over JSON Lines. ```bash # Start with defaults -./agentcore +./roboppi # Start with custom settings -./agentcore --concurrency 20 --rps 100 --log-level debug +./roboppi --concurrency 20 --rps 100 --log-level debug ``` Shared options (also available in `run` mode): @@ -111,7 +111,7 @@ Shared options (also available in `run` mode): ### Example: submit a job via JSON Lines ```bash -echo '{"type":"submit_job","requestId":"req-1","job":{"jobId":"job-001","type":"LLM","priority":{"value":1,"class":"INTERACTIVE"},"payload":{"prompt":"hello"},"limits":{"timeoutMs":5000,"maxAttempts":3},"context":{"traceId":"t-1","correlationId":"c-1"}}}' | ./agentcore 2>/dev/null +echo '{"type":"submit_job","requestId":"req-1","job":{"jobId":"job-001","type":"LLM","priority":{"value":1,"class":"INTERACTIVE"},"payload":{"prompt":"hello"},"limits":{"timeoutMs":5000,"maxAttempts":3},"context":{"traceId":"t-1","correlationId":"c-1"}}}' | ./roboppi 2>/dev/null ``` Output (returned on stdout as JSON Lines): @@ -330,7 +330,7 @@ OpenCode generates files and the result returns `SUCCEEDED`. ## Usage 5: Start with a Scheduler (full configuration) -The Scheduler launches and supervises AgentCore as a child process. This includes a job queue, dedup control, retries, and a DLQ. +The Scheduler launches and supervises Roboppi Core as a child process. This includes a job queue, dedup control, retries, and a DLQ. ```typescript import { Scheduler } from "./src/scheduler/index.js"; @@ -342,7 +342,7 @@ const scheduler = new Scheduler({ retry: { baseDelayMs: 1000, maxDelayMs: 30000, maxAttempts: 3 }, }); -// Start AgentCore as a child process +// Start Roboppi Core as a child process await scheduler.start(); // Submit a job @@ -401,7 +401,7 @@ roboppi workflow my-workflow.yaml --verbose # (dev) bun run src/workflow/run.ts my-workflow.yaml --verbose ``` -For each step, AgentCore requests a Permit, delegates to the worker, and collects results. If a step fails, it aborts depending on policy. +For each step, Roboppi Core requests a Permit, delegates to the worker, and collects results. If a step fails, it aborts depending on policy. ### Reuse worker settings with agent catalogs @@ -481,7 +481,7 @@ See the [Daemon guide](./daemon.md) for details. | Scheduler (parent process) | | JobQueue / InFlightRegistry / RetryPolicy | | +-----------------------------------------+ | -| | AgentCore (child process) | | +| | Roboppi Core (child process) | | | | PermitGate / CircuitBreaker | | | | Watchdog / EscalationManager | | | | +-----------------------------------+ | | diff --git a/docs/guide/workflow.md b/docs/guide/workflow.md index 87433d0..1a519d8 100644 --- a/docs/guide/workflow.md +++ b/docs/guide/workflow.md @@ -153,7 +153,7 @@ steps: The runner loads agent catalogs from these sources: -- Explicit: `AGENTCORE_AGENTS_FILE` (colon-separated list) + `--agents ` (repeatable). If the same agent id is defined multiple times, later definitions override earlier ones (so `--agents` wins over `AGENTCORE_AGENTS_FILE`). +- Explicit: `ROBOPPI_AGENTS_FILE` (colon-separated list) + `--agents ` (repeatable). If the same agent id is defined multiple times, later definitions override earlier ones (so `--agents` wins over `ROBOPPI_AGENTS_FILE`). - Implicit (only when no explicit paths are provided): `agents.yaml` / `agents.yml` next to the workflow YAML. ### capabilities @@ -389,7 +389,7 @@ Step start (iteration 1) | `instructions` | string | yes | check instructions | | `capabilities` | enum[] | yes | checker capabilities (usually `[READ]`) | | `timeout` | DurationString | no | timeout per check | -| `decision_file` | string | no | optional decision file path (workspace-relative). If set, Roboppi reads it after the check runs. Supported formats: structured JSON `{"decision":"complete"|"incomplete","check_id":"...","reasons":[...],"fingerprints":[...]}` (recommended) and legacy `PASS/COMPLETE` / `FAIL/INCOMPLETE` text. When `decision_file` is set, the runner provides `ROBOPPI_COMPLETION_CHECK_ID` (and legacy `AGENTCORE_COMPLETION_CHECK_ID`) in the check environment so the checker can include it as `check_id`. | +| `decision_file` | string | no | decision file path (workspace-relative). Required for non-`CUSTOM` completion checks. Roboppi reads it after the check runs. Format: structured JSON `{"decision":"complete"|"incomplete","check_id":"...","reasons":[...],"fingerprints":[...]}`. The runner provides `ROBOPPI_COMPLETION_CHECK_ID` in the check environment so the checker can include it as `check_id`. | ### convergence fields (opt-in) @@ -583,7 +583,7 @@ Use `--direct` to spawn worker processes directly from the runner (no Core IPC). | `--no-keepalive` | - | disable keepalive output | - | | `--keepalive-interval ` | - | keepalive interval (DurationString) | `10s` | | `--ipc-request-timeout ` | - | IPC request timeout for supervised mode (DurationString) | `2m` | -| `--agents ` | - | agent catalog YAML (repeatable); merged with `AGENTCORE_AGENTS_FILE` | auto-load `agents.yaml` / `agents.yml` next to workflow | +| `--agents ` | - | agent catalog YAML (repeatable); merged with `ROBOPPI_AGENTS_FILE` | auto-load `agents.yaml` / `agents.yml` next to workflow | | `--help` | `-h` | show help | - | ### Examples @@ -615,7 +615,7 @@ roboppi workflow examples/agent-pr-loop.yaml --direct --verbose Running prints output like: ``` -Workflow: /home/user/agentcore/examples/build-test-report.yaml +Workflow: /home/user/roboppi/examples/build-test-report.yaml Name: build-test-report Steps: build, test-math, test-greet, report Timeout: 5m diff --git a/docs/guides/agents.ja.md b/docs/guides/agents.ja.md index 19b631a..0cefc45 100644 --- a/docs/guides/agents.ja.md +++ b/docs/guides/agents.ja.md @@ -50,12 +50,12 @@ steps: Workflow runner: -- 明示: `AGENTCORE_AGENTS_FILE=/a.yaml:/b.yaml`(コロン区切り)と `--agents `(複数回指定可) +- 明示: `ROBOPPI_AGENTS_FILE=/a.yaml:/b.yaml`(コロン区切り)と `--agents `(複数回指定可) - 暗黙: 明示指定がない場合、workflow YAML と同じディレクトリの `agents.yaml` / `agents.yml` を自動探索 Daemon: - daemon 設定で `agents_file: "./agents.yaml"` を指定可能(相対パスは `workspace` 基準) -- さらに `AGENTCORE_AGENTS_FILE` を環境変数(または trigger の `context.env`)で指定可能 +- さらに `ROBOPPI_AGENTS_FILE` を環境変数(または trigger の `context.env`)で指定可能 同じ agent id が複数の catalog に定義されている場合、後から読み込んだ定義が上書きされます。 diff --git a/docs/guides/agents.md b/docs/guides/agents.md index e3062ea..502a2cf 100644 --- a/docs/guides/agents.md +++ b/docs/guides/agents.md @@ -57,12 +57,12 @@ completion_check: Workflow runner: -- Explicit: `ROBOPPI_AGENTS_FILE=/a.yaml:/b.yaml` (colon-separated) and/or `--agents ` (repeatable) (legacy `AGENTCORE_AGENTS_FILE` also supported) +- Explicit: `ROBOPPI_AGENTS_FILE=/a.yaml:/b.yaml` (colon-separated) and/or `--agents ` (repeatable) - Implicit: if no explicit paths are provided, auto-load `agents.yaml` / `agents.yml` next to the workflow YAML Daemon: - Daemon config can set `agents_file: "./agents.yaml"` (relative to `workspace`) -- You can also set `ROBOPPI_AGENTS_FILE` globally or per-trigger via `triggers..context.env` (legacy `AGENTCORE_AGENTS_FILE` also supported) +- You can also set `ROBOPPI_AGENTS_FILE` globally or per-trigger via `triggers..context.env` When multiple catalogs define the same agent id, later sources override earlier ones. diff --git a/docs/guides/branch.ja.md b/docs/guides/branch.ja.md index 672dbdf..409f336 100644 --- a/docs/guides/branch.ja.md +++ b/docs/guides/branch.ja.md @@ -168,13 +168,13 @@ main, master, release/* 保護ブランチリストの上書き: - CLI: `--protected-branches ` -- env: `AGENTCORE_PROTECTED_BRANCHES=` +- env: `ROBOPPI_PROTECTED_BRANCHES=` - 優先順位: CLI > env > default ガードの明示無効化(危険): - CLI: `--allow-protected-branch` -- env: `AGENTCORE_ALLOW_PROTECTED_BRANCH=1` +- env: `ROBOPPI_ALLOW_PROTECTED_BRANCH=1` override 有効時は warning を出し、workflow メタデータにも記録します。 @@ -187,23 +187,23 @@ workflow 実行時、runner は各 step に以下の env を渡します(`src/ base / startup: - `BASE_BRANCH`(`effective_base_branch` に設定) -- `AGENTCORE_EFFECTIVE_BASE_BRANCH` -- `AGENTCORE_EFFECTIVE_BASE_BRANCH_SOURCE`(`cli|env|current`) -- `AGENTCORE_EFFECTIVE_BASE_SHA` -- `AGENTCORE_STARTUP_BRANCH` -- `AGENTCORE_STARTUP_HEAD_SHA` -- `AGENTCORE_STARTUP_TOPLEVEL` +-- `ROBOPPI_EFFECTIVE_BASE_BRANCH` +-- `ROBOPPI_EFFECTIVE_BASE_BRANCH_SOURCE`(`cli|env|current`) +-- `ROBOPPI_EFFECTIVE_BASE_SHA` +-- `ROBOPPI_STARTUP_BRANCH` +-- `ROBOPPI_STARTUP_HEAD_SHA` +-- `ROBOPPI_STARTUP_TOPLEVEL` Branch Lock の期待値: -- `AGENTCORE_CREATE_BRANCH`(`1` または `0`) -- `AGENTCORE_EXPECTED_WORK_BRANCH` -- `AGENTCORE_EXPECTED_CURRENT_BRANCH` +-- `ROBOPPI_CREATE_BRANCH`(`1` または `0`) +-- `ROBOPPI_EXPECTED_WORK_BRANCH` +-- `ROBOPPI_EXPECTED_CURRENT_BRANCH` 保護ブランチガード: -- `AGENTCORE_PROTECTED_BRANCHES`(CSV) -- `AGENTCORE_ALLOW_PROTECTED_BRANCH`(`1` または `0`) +-- `ROBOPPI_PROTECTED_BRANCHES`(CSV) +-- `ROBOPPI_ALLOW_PROTECTED_BRANCH`(`1` または `0`) --- @@ -276,8 +276,8 @@ roboppi workflow examples/agent-pr-loop.yaml --workspace /path/to/repo \ 重い処理や環境依存の処理は、`CUSTOM` script 側で sentinel file(有無フラグ)を見て実行可否を切り替える運用ができます。 例(Appthrust Platform workflow): -- `.agentcore-loop/enable_live_validation`: 存在する場合のみ live cluster validation step を実行 -- `.agentcore-loop/live-validation.args`: validation wrapper に追加で渡す引数 +-- `.roboppi-loop/enable_live_validation`: 存在する場合のみ live cluster validation step を実行 +-- `.roboppi-loop/live-validation.args`: validation wrapper に追加で渡す引数 挙動: - ファイルなし -> script は `0` で終了し、step は skip/pass 扱い diff --git a/docs/guides/branch.md b/docs/guides/branch.md index 150afcd..83c0308 100644 --- a/docs/guides/branch.md +++ b/docs/guides/branch.md @@ -165,13 +165,13 @@ This is intentionally strict because `create_branch: false` implies the workflow Override the protected list: - CLI: `--protected-branches ` -- env: `AGENTCORE_PROTECTED_BRANCHES=` +- env: `ROBOPPI_PROTECTED_BRANCHES=` - priority: CLI > env > default Disable the guard explicitly (dangerous): - CLI: `--allow-protected-branch` -- env: `AGENTCORE_ALLOW_PROTECTED_BRANCH=1` +- env: `ROBOPPI_ALLOW_PROTECTED_BRANCH=1` When override is enabled, the runner logs a warning and records the override in workflow metadata. @@ -184,23 +184,23 @@ When the workflow is executed, Roboppi passes these values to each step as envir Base / startup: - `BASE_BRANCH` (set to `effective_base_branch`) -- `AGENTCORE_EFFECTIVE_BASE_BRANCH` -- `AGENTCORE_EFFECTIVE_BASE_BRANCH_SOURCE` (`cli|env|current`) -- `AGENTCORE_EFFECTIVE_BASE_SHA` -- `AGENTCORE_STARTUP_BRANCH` -- `AGENTCORE_STARTUP_HEAD_SHA` -- `AGENTCORE_STARTUP_TOPLEVEL` +-- `ROBOPPI_EFFECTIVE_BASE_BRANCH` +-- `ROBOPPI_EFFECTIVE_BASE_BRANCH_SOURCE` (`cli|env|current`) +-- `ROBOPPI_EFFECTIVE_BASE_SHA` +-- `ROBOPPI_STARTUP_BRANCH` +-- `ROBOPPI_STARTUP_HEAD_SHA` +-- `ROBOPPI_STARTUP_TOPLEVEL` Branch lock expectations: -- `AGENTCORE_CREATE_BRANCH` (`1` or `0`) -- `AGENTCORE_EXPECTED_WORK_BRANCH` -- `AGENTCORE_EXPECTED_CURRENT_BRANCH` +-- `ROBOPPI_CREATE_BRANCH` (`1` or `0`) +-- `ROBOPPI_EXPECTED_WORK_BRANCH` +-- `ROBOPPI_EXPECTED_CURRENT_BRANCH` Protected branch guard: -- `AGENTCORE_PROTECTED_BRANCHES` (CSV) -- `AGENTCORE_ALLOW_PROTECTED_BRANCH` (`1` or `0`) +-- `ROBOPPI_PROTECTED_BRANCHES` (CSV) +-- `ROBOPPI_ALLOW_PROTECTED_BRANCH` (`1` or `0`) --- @@ -272,8 +272,8 @@ roboppi workflow examples/agent-pr-loop.yaml --workspace /path/to/repo \ Some teams gate expensive or environment-dependent steps behind sentinel files checked by `CUSTOM` scripts. Example (Appthrust Platform workflow): -- `.agentcore-loop/enable_live_validation`: if present, run the live-cluster validation step. -- `.agentcore-loop/live-validation.args`: optional extra args passed to the validation wrapper. +-- `.roboppi-loop/enable_live_validation`: if present, run the live-cluster validation step. +-- `.roboppi-loop/live-validation.args`: optional extra args passed to the validation wrapper. Behavior: - sentinel file absent -> script exits `0` and the step is treated as skipped/pass. diff --git a/docs/issues/base-branch-default-current-branch.ja.md b/docs/issues/base-branch-default-current-branch.ja.md index 4f4b53b..a2959df 100644 --- a/docs/issues/base-branch-default-current-branch.ja.md +++ b/docs/issues/base-branch-default-current-branch.ja.md @@ -105,11 +105,11 @@ controller loop 実行時に `BASE_BRANCH` の手動指定が前提になって - パターン仕様: `*` を含む場合は glob として扱う(例: `release/*`)。それ以外は完全一致。 - 設定(リスト上書き): - CLI: `--protected-branches `(例: `main,master,release/*`) - - env: `AGENTCORE_PROTECTED_BRANCHES=` + - env: `ROBOPPI_PROTECTED_BRANCHES=` - 優先順位: CLI > env > default - 一時 override(ガード無効化; 危険操作なので明示が必要): - CLI: `--allow-protected-branch` - - env: `AGENTCORE_ALLOW_PROTECTED_BRANCH=1` + - env: `ROBOPPI_ALLOW_PROTECTED_BRANCH=1` - override 有効時も、起動ログ/context に `allow_protected_branch: true` を残し、強い warning を出す ## 4) 可観測性の強化 @@ -137,8 +137,8 @@ controller loop 実行時に `BASE_BRANCH` の手動指定が前提になって 4. mismatch 時のエラーメッセージを定型化 5. run script / docs を「現在ブランチ既定」前提に更新 6. `effective_base_branch_source` / `effective_base_sha` / `startup_head_sha` / `startup_toplevel` の schema を追加し、成果物に含める -7. `protected_branches` resolver(既定 + `AGENTCORE_PROTECTED_BRANCHES` + `--protected-branches`)を追加 -8. `protected_branches` ガードと明示 override(`AGENTCORE_ALLOW_PROTECTED_BRANCH` / `--allow-protected-branch`)を追加し、ログに理由を残す +7. `protected_branches` resolver(既定 + `ROBOPPI_PROTECTED_BRANCHES` + `--protected-branches`)を追加 +8. `protected_branches` ガードと明示 override(`ROBOPPI_ALLOW_PROTECTED_BRANCH` / `--allow-protected-branch`)を追加し、ログに理由を残す ## 受け入れ条件 @@ -146,10 +146,10 @@ controller loop 実行時に `BASE_BRANCH` の手動指定が前提になって 2. 実行中に branch が想定とずれたら、実装 step に入る前に停止する 3. 起動ログと context から有効 branch 設定(branch 名/由来/SHA)を一意に確認できる 4. `BASE_BRANCH` 明示指定時は override される(必要なら warning を表示) -5. `create_branch=false` で保護ブランチに対して実行しようとすると、明示 override(`AGENTCORE_ALLOW_PROTECTED_BRANCH=1` または `--allow-protected-branch`)がない限り停止する -6. `AGENTCORE_PROTECTED_BRANCHES` / `--protected-branches` 未指定時、`protected_branches` は既定の `main,master,release/*` で確定し、`protected_branches_source=default` になる -7. `AGENTCORE_PROTECTED_BRANCHES` または `--protected-branches` 指定時、`protected_branches` が上書きされ、`protected_branches_source` が `env|cli` として記録される -8. `AGENTCORE_ALLOW_PROTECTED_BRANCH=1` または `--allow-protected-branch` 指定時、`allow_protected_branch=true` がログ/context に記録される +5. `create_branch=false` で保護ブランチに対して実行しようとすると、明示 override(`ROBOPPI_ALLOW_PROTECTED_BRANCH=1` または `--allow-protected-branch`)がない限り停止する +6. `ROBOPPI_PROTECTED_BRANCHES` / `--protected-branches` 未指定時、`protected_branches` は既定の `main,master,release/*` で確定し、`protected_branches_source=default` になる +7. `ROBOPPI_PROTECTED_BRANCHES` または `--protected-branches` 指定時、`protected_branches` が上書きされ、`protected_branches_source` が `env|cli` として記録される +8. `ROBOPPI_ALLOW_PROTECTED_BRANCH=1` または `--allow-protected-branch` 指定時、`allow_protected_branch=true` がログ/context に記録される ## 非目標 @@ -171,4 +171,4 @@ controller loop 実行時に `BASE_BRANCH` の手動指定が前提になって - 緩和: `protected_branches` ガードをデフォルト有効にし、明示 override がない限り fail-fast する 5. `release/*` 既定が運用をブロックする可能性 -- 緩和: `AGENTCORE_PROTECTED_BRANCHES` / `--protected-branches` で `main,master` のみに絞れる。緊急時は `AGENTCORE_ALLOW_PROTECTED_BRANCH=1` / `--allow-protected-branch` を明示し warning 付きで通す +- 緩和: `ROBOPPI_PROTECTED_BRANCHES` / `--protected-branches` で `main,master` のみに絞れる。緊急時は `ROBOPPI_ALLOW_PROTECTED_BRANCH=1` / `--allow-protected-branch` を明示し warning 付きで通す diff --git a/docs/issues/completion-check-decision-stability.ja.md b/docs/issues/completion-check-decision-stability.ja.md index 5d33cc2..f26ea94 100644 --- a/docs/issues/completion-check-decision-stability.ja.md +++ b/docs/issues/completion-check-decision-stability.ja.md @@ -13,7 +13,7 @@ Completion check failed: could not parse completion decision (expected COMPLETE/ 実運用で確認された症状: - Worker 出力に `INCOMPLETE` が存在するのに parse failure になる -- `.agentcore-loop/review.verdict` に `FAIL` が書かれていても判定に使われないケースがある +- `.roboppi-loop/review.verdict` に `FAIL` が書かれていても判定に使われないケースがある - その結果、`max_iterations` 到達前にワークフローが即 `FAILED` になる この問題は workflow 側プロンプト調整では再発し得るため、AgentCore/Supervisor 側の判定実装を根本修正する。 @@ -57,8 +57,8 @@ Completion check failed: could not parse completion decision (expected COMPLETE/ ## 実装メモ(2026-02-14) - `src/workflow/completion-decision.ts` - - `decision_file` を JSON / legacy テキストで解決する共通関数 `resolveCompletionDecision` を追加 - - `AGENTCORE_COMPLETION_CHECK_ID` と照合して `check_id` 安全性を追加 + - `decision_file` を JSON / 互換テキストで解決する共通関数 `resolveCompletionDecision` を追加 + - `ROBOPPI_COMPLETION_CHECK_ID` と照合して `check_id` 安全性を追加 - `PASS/FAIL`/`COMPLETE/INCOMPLETE` と JSON `{"decision":"...","check_id":"..."}` を受け入れる - `src/workflow/core-ipc-step-runner.ts` と `src/workflow/multi-worker-step-runner.ts` - `decision_file` 判定を共通化 @@ -85,7 +85,7 @@ Completion check failed: could not parse completion decision (expected COMPLETE/ 優先順位を明示して一本化する。 1. `decision_file`(structured JSON) -2. `decision_file`(legacy text) +2. `decision_file`(互換テキスト) 3. worker 出力 marker(互換フォールバック) 補足: @@ -113,7 +113,7 @@ Completion check failed: could not parse completion decision (expected COMPLETE/ 判定ログに最低限以下を出す。 -- `decision_source`(file-json / file-legacy / marker / none) +- `decision_source`(file-json / file-text / marker / none) - `check_id` の一致有無 - parse failure 理由("missing decision", "invalid json", など) @@ -138,7 +138,7 @@ Completion check failed: could not parse completion decision (expected COMPLETE/ 5. テスト - 単体テスト: - structured file 判定(complete/incomplete) - - legacy file 判定(PASS/FAIL) + - 互換テキスト判定(PASS/FAIL) - marker fallback - stale file(check_id 不一致) - invalid json @@ -167,11 +167,11 @@ Completion check failed: could not parse completion decision (expected COMPLETE/ 1. parse failure を `INCOMPLETE` にするとループが長引く - 緩和: `max_iterations` と parse failure 閾値ログを明示 -2. legacy/structured 混在で移行時の混乱 +2. 互換/structured 混在で移行時の混乱 - 緩和: file-first の優先順位を固定し、ログに `decision_source` を出す 3. 既存 workflow との互換破壊 -- 緩和: legacy text 判定のサポートを維持 +- 緩和: 互換テキスト判定(PASS/FAIL 等)のサポートを維持 ## 進め方(短期) diff --git a/docs/issues/implement-loop-self-healing.ja.md b/docs/issues/implement-loop-self-healing.ja.md index c1ce8f8..b643758 100644 --- a/docs/issues/implement-loop-self-healing.ja.md +++ b/docs/issues/implement-loop-self-healing.ja.md @@ -4,15 +4,15 @@ ## 問題 -2026-02-14 の実行(`/home/reoring/appthrust/platform/.agentcore-loop/run-local-workflow-20260214-214510.log`)で、以下を確認した。 +2026-02-14 の実行(`/home/reoring/appthrust/platform/.roboppi-loop/run-local-workflow-20260214-214510.log`)で、以下を確認した。 - `completion decision` の parse failure(`expected COMPLETE/INCOMPLETE marker`)はこのログでは発生していない - - `completion decision: incomplete source=file-legacy` を複数回確認 + - `completion decision: incomplete source=file-text` を複数回確認 - parse failure 自体は別 run で発生しており、先行 issue で扱う - `implement` は `INCOMPLETE` 判定で iteration を継続する(収束制御が弱い) - 少なくとも 1 回はテスト失敗が観測されている - `go test ./...` が `template_test.go:104` で FAIL(`gateway-api-crds-template should contain standard install header`) - - その後に `go test ./...` が PASS するケースもあり得るが、判定が `INCOMPLETE` のまま反復し得る(例: legacy verdict が FAIL のまま / スコープ逸脱が残る) + - その後に `go test ./...` が PASS するケースもあり得るが、判定が `INCOMPLETE` のまま反復し得る(例: 互換 verdict が FAIL のまま / スコープ逸脱が残る) つまり、判定の安定性問題(前 issue)は改善されつつあるが、"INCOMPLETE を自律的に収束させる" 制御が不足している。 @@ -54,7 +54,7 @@ completion 判定は LLM 自由文ではなく、**機械生成 JSON を正経 - `decision_file` 例(推奨): - `{"decision":"complete|incomplete","check_id":"...","reasons":[...],"fingerprints":[...]}` - `check_id` により stale file 混入を時刻比較なしで防止 -- legacy 互換(PASS/FAIL, COMPLETE/INCOMPLETE)も当面維持 +- 互換(PASS/FAIL, COMPLETE/INCOMPLETE)も当面維持 この issue の self-healing は、`reasons` / `fingerprints` を主要シグナルとして利用する。 @@ -97,7 +97,7 @@ workflow に `allowed_paths` を定義し、範囲外変更を検出したら `I 実装メモ: - git repo であれば `git status --porcelain` / `git diff --name-only` で変更ファイル集合を取得し、allowed_paths に照合する -- workflow 生成物(例: `context/`, `.agentcore-loop/`)は scope 判定から除外する(誤検知防止) +- workflow 生成物(例: `context/`, `.roboppi-loop/`)は scope 判定から除外する(誤検知防止) ## 6) Completion Check を完全機械判定へ寄せる @@ -117,7 +117,7 @@ workflow に `allowed_paths` を定義し、範囲外変更を検出したら `I 推奨保存先: - workflow workspace の `context//...`(runner が収集しやすい) -- 併用で `.agentcore-loop/` にも置く場合は `.gitignore` 前提 +- 併用で `.roboppi-loop/` にも置く場合は `.gitignore` 前提 ## 実装計画 @@ -149,7 +149,7 @@ workflow に `allowed_paths` を定義し、範囲外変更を検出したら `I 2. stage 上限到達時、`FAILED` は原因(fingerprint/reason)付きで返る 3. baseline 差分で `new_failures == 0` のときのみ `COMPLETE` になる 4. `allowed_paths` 外の変更は検知・記録される -5. 既存 workflow(legacy verdict)との互換を維持する +5. 既存 workflow(互換 verdict)との互換を維持する ## 非目標 diff --git a/docs/issues/supervised-ipc-submit-job-timeout.ja.md b/docs/issues/supervised-ipc-submit-job-timeout.ja.md index 1d21825..3798f69 100644 --- a/docs/issues/supervised-ipc-submit-job-timeout.ja.md +++ b/docs/issues/supervised-ipc-submit-job-timeout.ja.md @@ -22,40 +22,40 @@ IPC トレースで見えた主な症状: つまり壊れているのは Runner -> Core の stdin 経路(Core 側でメッセージが見えない)です。 -対処: socket ベースの supervised IPC transport(`AGENTCORE_SUPERVISED_IPC_TRANSPORT=socket`)を使い、stdio パイプを完全に迂回します。 +対処: socket ベースの supervised IPC transport(`ROBOPPI_SUPERVISED_IPC_TRANSPORT=socket`)を使い、stdio パイプを完全に迂回します。 もし環境側で Unix ドメインソケット作成が禁止されている(例: `listen` が `EPERM` / `EACCES` で失敗する)場合は、TCP ループバックを使います: -- `AGENTCORE_SUPERVISED_IPC_TRANSPORT=tcp` +- `ROBOPPI_SUPERVISED_IPC_TRANSPORT=tcp` ## 再現 典型的な再現手順(stdio を強制。影響を受ける環境では失敗する可能性あり): ```bash -AGENTCORE_SUPERVISED_IPC_TRANSPORT=stdio \ - AGENTCORE_IPC_TRACE=1 AGENTCORE_IPC_REQUEST_TIMEOUT=45s VERBOSE=0 \ +ROBOPPI_SUPERVISED_IPC_TRANSPORT=stdio \ + ROBOPPI_IPC_TRACE=1 ROBOPPI_IPC_REQUEST_TIMEOUT=45s VERBOSE=0 \ bash examples/agent-pr-loop-demo/run-in-tmp.sh ``` Socket transport(成功を想定): ```bash -AGENTCORE_SUPERVISED_IPC_TRANSPORT=socket \ - AGENTCORE_IPC_TRACE=1 AGENTCORE_IPC_REQUEST_TIMEOUT=2m VERBOSE=0 \ +ROBOPPI_SUPERVISED_IPC_TRANSPORT=socket \ + ROBOPPI_IPC_TRACE=1 ROBOPPI_IPC_REQUEST_TIMEOUT=2m VERBOSE=0 \ bash examples/agent-pr-loop-demo/run-in-tmp.sh ``` TCP transport(Unix socket がブロックされている環境向け): ```bash -AGENTCORE_SUPERVISED_IPC_TRANSPORT=tcp \ - AGENTCORE_IPC_TRACE=1 AGENTCORE_IPC_REQUEST_TIMEOUT=2m VERBOSE=0 \ +ROBOPPI_SUPERVISED_IPC_TRANSPORT=tcp \ + ROBOPPI_IPC_TRACE=1 ROBOPPI_IPC_REQUEST_TIMEOUT=2m VERBOSE=0 \ bash examples/agent-pr-loop-demo/run-in-tmp.sh ``` 注記: `src/workflow/run.ts` では非対話の `--supervised` 実行時に -`AGENTCORE_SUPERVISED_IPC_TRANSPORT=socket` がデフォルトになっています(`stdio|socket|tcp` で上書き可)。 +`ROBOPPI_SUPERVISED_IPC_TRANSPORT=socket` がデフォルトになっています(`stdio|socket|tcp` で上書き可)。 補足: @@ -75,7 +75,7 @@ AGENTCORE_SUPERVISED_IPC_TRANSPORT=tcp \ - デフォルトの IPC リクエストタイムアウトが 30 秒(`DEFAULT_REQUEST_TIMEOUT_MS = 30_000`)であることを確認。 - IPC リクエストタイムアウトを設定可能にし、supervised runner の既定値を `2m` に引き上げ: - CLI: `--ipc-request-timeout ` - - 環境変数: `AGENTCORE_IPC_REQUEST_TIMEOUT` / `AGENTCORE_IPC_REQUEST_TIMEOUT_MS` + - 環境変数: `ROBOPPI_IPC_REQUEST_TIMEOUT` / `ROBOPPI_IPC_REQUEST_TIMEOUT_MS` 3) ステップタイムアウトとの関係 @@ -89,10 +89,10 @@ AGENTCORE_SUPERVISED_IPC_TRANSPORT=tcp \ 5) IPC トレース機能 -- `src/ipc/json-lines-transport.ts` に `AGENTCORE_IPC_TRACE=1` のサポートを追加: +- `src/ipc/json-lines-transport.ts` に `ROBOPPI_IPC_TRACE=1` のサポートを追加: - `tx` / `rx` を `pid` / `type` / `requestId` / `jobId` と共に出力。 - デモトレース補助を追加: - - `examples/agent-pr-loop-demo/run-in-tmp.sh` が `AGENTCORE_ROOT` と git SHA を表示。 + - `examples/agent-pr-loop-demo/run-in-tmp.sh` が `ROBOPPI_ROOT` と git SHA を表示。 6) トランスポートレベルのエラー @@ -112,11 +112,11 @@ AGENTCORE_SUPERVISED_IPC_TRANSPORT=tcp \ 8) socket ベースの supervised IPC transport -- supervised IPC に socket transport を追加し、stdio パイプを回避: `AGENTCORE_SUPERVISED_IPC_TRANSPORT=socket`。 +- supervised IPC に socket transport を追加し、stdio パイプを回避: `ROBOPPI_SUPERVISED_IPC_TRANSPORT=socket`。 - 既定は Unix ドメインソケット。Unix socket が許可されない場合は TCP ループバックへフォールバック。 - - 明示的に TCP を使う場合は `AGENTCORE_SUPERVISED_IPC_TRANSPORT=tcp`。 -- `src/workflow/run.ts` は非対話の `--supervised` 実行で既定値を `socket` に設定(`AGENTCORE_SUPERVISED_IPC_TRANSPORT=stdio|socket|tcp` で上書き可)。 -- Core は Supervisor がセットする `AGENTCORE_IPC_SOCKET_PATH`(Unix)または `AGENTCORE_IPC_SOCKET_HOST` + `AGENTCORE_IPC_SOCKET_PORT`(TCP)へ接続します。 + - 明示的に TCP を使う場合は `ROBOPPI_SUPERVISED_IPC_TRANSPORT=tcp`。 +- `src/workflow/run.ts` は非対話の `--supervised` 実行で既定値を `socket` に設定(`ROBOPPI_SUPERVISED_IPC_TRANSPORT=stdio|socket|tcp` で上書き可)。 +- Core は Supervisor がセットする `ROBOPPI_IPC_SOCKET_PATH`(Unix)または `ROBOPPI_IPC_SOCKET_HOST` + `ROBOPPI_IPC_SOCKET_PORT`(TCP)へ接続します。 ## 関連作業(Agent PR Loop デモ) @@ -124,16 +124,16 @@ AGENTCORE_SUPERVISED_IPC_TRANSPORT=tcp \ - `examples/agent-pr-loop.yaml`: review 結果に応じて `implement`(Claude Code)をループするよう変更し、`completion_check` を使用。 - `completion_check` の判断を `decision_file` によるファイルベース判定へ対応し、stdout マーカー依存を排除。 -- `scripts/agent-pr-loop/review-inputs.sh`: `.agentcore-loop/review.untracked.diff` を生成し、レビューに未追跡差分を含める。 +- `scripts/agent-pr-loop/review-inputs.sh`: `.roboppi-loop/review.untracked.diff` を生成し、レビューに未追跡差分を含める。 - `examples/agent-pr-loop-demo/request.md`: 具体的な境界ケースを追加して品質基準を引き上げ。 - `examples/agent-pr-loop-demo/run-in-tmp.sh`: ワークフロー完了後の検証を追加(ブラックボックス確認)。 ## 現在の状況 / 次のステップ - 根本原因: supervised stdio パイプで、特定の非対話環境で Runner -> Core 方向のメッセージが落ちて `submit_job` の ACK タイムアウトにつながる。 -- 実装済み対策: supervised IPC を `AGENTCORE_SUPERVISED_IPC_TRANSPORT=socket` に変更。非対話 `--supervised` 実行では `src/workflow/run.ts` により既定で socket になります。 +- 実装済み対策: supervised IPC を `ROBOPPI_SUPERVISED_IPC_TRANSPORT=socket` に変更。非対話 `--supervised` 実行では `src/workflow/run.ts` により既定で socket になります。 - 旧挙動が必要な場合、または元の問題を再現したい場合は次を指定: - - `AGENTCORE_SUPERVISED_IPC_TRANSPORT=stdio` + - `ROBOPPI_SUPERVISED_IPC_TRANSPORT=stdio` ## 2026-02-14 フォローアップ @@ -151,7 +151,7 @@ AGENTCORE_SUPERVISED_IPC_TRANSPORT=tcp \ ### stdio transport の残タスク -- 再度再現コマンドを `AGENTCORE_IPC_TRACE=1` で実行。 +- 再度再現コマンドを `ROBOPPI_IPC_TRACE=1` で実行。 - トレースに以下が含まれることを確認: - `tx submit_job`(Runner) - `rx submit_job`(Core) @@ -162,15 +162,15 @@ AGENTCORE_SUPERVISED_IPC_TRANSPORT=tcp \ ### 2026-02-14 (stdio) 再実行結果 - 再現コマンド(stdio 強制): - - `AGENTCORE_SUPERVISED_IPC_TRANSPORT=stdio AGENTCORE_IPC_TRACE=1 AGENTCORE_IPC_REQUEST_TIMEOUT=45s VERBOSE=0 bash examples/agent-pr-loop-demo/run-in-tmp.sh` - - `AGENTCORE_SUPERVISED_IPC_TRANSPORT=stdio AGENTCORE_IPC_TRACE=1 AGENTCORE_IPC_REQUEST_TIMEOUT=20s bun run src/workflow/run.ts examples/hello-world.yaml --supervised` + - `ROBOPPI_SUPERVISED_IPC_TRANSPORT=stdio ROBOPPI_IPC_TRACE=1 ROBOPPI_IPC_REQUEST_TIMEOUT=45s VERBOSE=0 bash examples/agent-pr-loop-demo/run-in-tmp.sh` + - `ROBOPPI_SUPERVISED_IPC_TRANSPORT=stdio ROBOPPI_IPC_TRACE=1 ROBOPPI_IPC_REQUEST_TIMEOUT=20s bun run src/workflow/run.ts examples/hello-world.yaml --supervised` - 両方のコマンドで、いずれも以下を確認: - Runner `tx submit_job` - Core 起動ログ(`AgentCore starting`, `AgentCore started, awaiting IPC messages`) - Core 側の `[IPC][rx]` / `ack` は **未観測** - 20〜45 秒でタイムアウトし、ワークフロー失敗 - 追加の分離検証: - - `AGENTCORE_IPC_TRACE=1 bun /home/reoring/roboppi/src/index.ts` をシェルパイプ経由(`printf ... | bun src/index.ts`)で実行すると `Core [IPC][rx] submit_job` と `ack` を確認。 + - `ROBOPPI_IPC_TRACE=1 bun /home/reoring/roboppi/src/index.ts` をシェルパイプ経由(`printf ... | bun src/index.ts`)で実行すると `Core [IPC][rx] submit_job` と `ack` を確認。 - Bun runtime + `node:child_process` で子 bun プロセスへ書き込む supervisor 経路では、最小化した writer スクリプトでも Core の `[IPC][rx]` は得られない。 - stdio に関する最新仮説: - 本環境では Bun runtime のプロセス生成 + pipe transport が子 bun プロセスの stdin をトランスポート層で破棄する可能性。 @@ -179,9 +179,9 @@ AGENTCORE_SUPERVISED_IPC_TRANSPORT=tcp \ ### 2026-02-14 (socket) 再実行結果 - socket transport での問題解消を end-to-end で確認: - - `AGENTCORE_SUPERVISED_IPC_TRANSPORT=socket AGENTCORE_IPC_REQUEST_TIMEOUT=2m VERBOSE=0 bash examples/agent-pr-loop-demo/run-in-tmp.sh` + - `ROBOPPI_SUPERVISED_IPC_TRANSPORT=socket ROBOPPI_IPC_REQUEST_TIMEOUT=2m VERBOSE=0 bash examples/agent-pr-loop-demo/run-in-tmp.sh` -> ワークフロー `SUCCEEDED`、デモの事後チェック完了 - - `AGENTCORE_IPC_TRACE=1 bun run src/workflow/run.ts examples/hello-world.yaml --supervised` + - `ROBOPPI_IPC_TRACE=1 bun run src/workflow/run.ts examples/hello-world.yaml --supervised` -> Core `[IPC][rx] submit_job` と `ack` を確認、ワークフロー `SUCCEEDED` - 全テスト通過: `bun test`(949 件) - 結論: 非対話 supervised 実行では socket transport を利用。stdio はフォールバック / デバッグ用。 diff --git a/docs/issues/supervised-ipc-submit-job-timeout.md b/docs/issues/supervised-ipc-submit-job-timeout.md index 474a7bd..bef4ae1 100644 --- a/docs/issues/supervised-ipc-submit-job-timeout.md +++ b/docs/issues/supervised-ipc-submit-job-timeout.md @@ -20,39 +20,39 @@ Key symptom from IPC tracing: So the break is: Runner -> Core stdin (message never observed by Core). -Mitigation: use the socket-based supervised IPC transport (`AGENTCORE_SUPERVISED_IPC_TRANSPORT=socket`) which bypasses stdio pipes entirely. +Mitigation: use the socket-based supervised IPC transport (`ROBOPPI_SUPERVISED_IPC_TRANSPORT=socket`) which bypasses stdio pipes entirely. If your environment blocks Unix domain sockets (e.g. `listen` fails with `EPERM`/`EACCES`), use the TCP loopback transport instead: -- `AGENTCORE_SUPERVISED_IPC_TRANSPORT=tcp` +- `ROBOPPI_SUPERVISED_IPC_TRANSPORT=tcp` ## Repro Typical demo repro (force stdio; may fail in affected environments): ```bash -AGENTCORE_SUPERVISED_IPC_TRANSPORT=stdio \ - AGENTCORE_IPC_TRACE=1 AGENTCORE_IPC_REQUEST_TIMEOUT=45s VERBOSE=0 \ +ROBOPPI_SUPERVISED_IPC_TRANSPORT=stdio \ + ROBOPPI_IPC_TRACE=1 ROBOPPI_IPC_REQUEST_TIMEOUT=45s VERBOSE=0 \ bash examples/agent-pr-loop-demo/run-in-tmp.sh ``` Socket transport (expected to succeed): ```bash -AGENTCORE_SUPERVISED_IPC_TRANSPORT=socket \ - AGENTCORE_IPC_TRACE=1 AGENTCORE_IPC_REQUEST_TIMEOUT=2m VERBOSE=0 \ +ROBOPPI_SUPERVISED_IPC_TRANSPORT=socket \ + ROBOPPI_IPC_TRACE=1 ROBOPPI_IPC_REQUEST_TIMEOUT=2m VERBOSE=0 \ bash examples/agent-pr-loop-demo/run-in-tmp.sh ``` TCP transport (useful when Unix sockets are blocked): ```bash -AGENTCORE_SUPERVISED_IPC_TRANSPORT=tcp \ - AGENTCORE_IPC_TRACE=1 AGENTCORE_IPC_REQUEST_TIMEOUT=2m VERBOSE=0 \ +ROBOPPI_SUPERVISED_IPC_TRANSPORT=tcp \ + ROBOPPI_IPC_TRACE=1 ROBOPPI_IPC_REQUEST_TIMEOUT=2m VERBOSE=0 \ bash examples/agent-pr-loop-demo/run-in-tmp.sh ``` -Note: `src/workflow/run.ts` defaults `AGENTCORE_SUPERVISED_IPC_TRANSPORT=socket` in non-interactive `--supervised` runs. +Note: `src/workflow/run.ts` defaults `ROBOPPI_SUPERVISED_IPC_TRANSPORT=socket` in non-interactive `--supervised` runs. Notes: @@ -72,7 +72,7 @@ Notes: - Found default IPC request timeout of 30s (`DEFAULT_REQUEST_TIMEOUT_MS = 30_000`). - Made IPC request timeout configurable and raised default for supervised runner to `2m`: - CLI: `--ipc-request-timeout ` - - env: `AGENTCORE_IPC_REQUEST_TIMEOUT` / `AGENTCORE_IPC_REQUEST_TIMEOUT_MS` + - env: `ROBOPPI_IPC_REQUEST_TIMEOUT` / `ROBOPPI_IPC_REQUEST_TIMEOUT_MS` 3) Step timeout interaction @@ -86,10 +86,10 @@ Notes: 5) IPC trace tooling -- Added `AGENTCORE_IPC_TRACE=1` support in `src/ipc/json-lines-transport.ts`: +- Added `ROBOPPI_IPC_TRACE=1` support in `src/ipc/json-lines-transport.ts`: - Logs `tx`/`rx` with pid/type/requestId/jobId. - Added demo trace helpers: - - `examples/agent-pr-loop-demo/run-in-tmp.sh` prints `AGENTCORE_ROOT` and git SHA when tracing. + - `examples/agent-pr-loop-demo/run-in-tmp.sh` prints `ROBOPPI_ROOT` and git SHA when tracing. 6) Transport-level errors @@ -109,11 +109,11 @@ These changes improve supervised IPC in many environments, but some setups still 8) Socket-based supervised IPC transport -- Added a socket transport for supervised IPC to bypass stdio pipes: `AGENTCORE_SUPERVISED_IPC_TRANSPORT=socket`. +- Added a socket transport for supervised IPC to bypass stdio pipes: `ROBOPPI_SUPERVISED_IPC_TRANSPORT=socket`. - Uses a Unix domain socket by default; falls back to TCP loopback when Unix sockets are not permitted. - - You can force TCP with `AGENTCORE_SUPERVISED_IPC_TRANSPORT=tcp`. -- `src/workflow/run.ts` defaults to `socket` in non-interactive `--supervised` runs (override via `AGENTCORE_SUPERVISED_IPC_TRANSPORT=stdio|socket|tcp`). -- Core connects via `AGENTCORE_IPC_SOCKET_PATH` (Unix) or `AGENTCORE_IPC_SOCKET_HOST` + `AGENTCORE_IPC_SOCKET_PORT` (TCP), set by Supervisor. + - You can force TCP with `ROBOPPI_SUPERVISED_IPC_TRANSPORT=tcp`. +- `src/workflow/run.ts` defaults to `socket` in non-interactive `--supervised` runs (override via `ROBOPPI_SUPERVISED_IPC_TRANSPORT=stdio|socket|tcp`). +- Core connects via `ROBOPPI_IPC_SOCKET_PATH` (Unix) or `ROBOPPI_IPC_SOCKET_HOST` + `ROBOPPI_IPC_SOCKET_PORT` (TCP), set by Supervisor. ## Related Work (Agent PR Loop Demo) @@ -121,15 +121,15 @@ While investigating, the agent PR loop demo/workflow was also strengthened: - `examples/agent-pr-loop.yaml`: changed to loop `implement` (Claude Code) based on review verdict, using `completion_check`. - `completion_check` decision now supports file-based decision via `decision_file` to avoid reliance on stdout markers. -- `scripts/agent-pr-loop/review-inputs.sh`: generates `.agentcore-loop/review.untracked.diff` so reviews include untracked file diffs. +- `scripts/agent-pr-loop/review-inputs.sh`: generates `.roboppi-loop/review.untracked.diff` so reviews include untracked file diffs. - `examples/agent-pr-loop-demo/request.md`: raised quality bar with explicit edge cases. - `examples/agent-pr-loop-demo/run-in-tmp.sh`: added black-box verification after workflow completion. ## Current Status / Next Steps - Root issue: supervised stdio pipes can drop Runner -> Core messages in some non-interactive environments, leading to `submit_job` ACK timeouts. -- Mitigation implemented: socket transport for supervised IPC (`AGENTCORE_SUPERVISED_IPC_TRANSPORT=socket`), now the default in non-interactive `--supervised` runs via `src/workflow/run.ts`. -- If you need the old behavior (or want to repro the original failure), force: `AGENTCORE_SUPERVISED_IPC_TRANSPORT=stdio`. +- Mitigation implemented: socket transport for supervised IPC (`ROBOPPI_SUPERVISED_IPC_TRANSPORT=socket`), now the default in non-interactive `--supervised` runs via `src/workflow/run.ts`. +- If you need the old behavior (or want to repro the original failure), force: `ROBOPPI_SUPERVISED_IPC_TRANSPORT=stdio`. ## 2026-02-14 Follow-up @@ -147,7 +147,7 @@ Implemented additional stdio hardening to reduce the chance of Supervisor → Co ### Remaining validation steps (stdio transport) -- Re-run the repro with `AGENTCORE_IPC_TRACE=1`. +- Re-run the repro with `ROBOPPI_IPC_TRACE=1`. - Confirm trace includes: - `tx submit_job` (Runner) - `rx submit_job` (Core) @@ -158,15 +158,15 @@ Implemented additional stdio hardening to reduce the chance of Supervisor → Co ### 2026-02-14 (stdio) 再実行結果 - Repro command (forced stdio): - - `AGENTCORE_SUPERVISED_IPC_TRANSPORT=stdio AGENTCORE_IPC_TRACE=1 AGENTCORE_IPC_REQUEST_TIMEOUT=45s VERBOSE=0 bash examples/agent-pr-loop-demo/run-in-tmp.sh` - - `AGENTCORE_SUPERVISED_IPC_TRANSPORT=stdio AGENTCORE_IPC_TRACE=1 AGENTCORE_IPC_REQUEST_TIMEOUT=20s bun run src/workflow/run.ts examples/hello-world.yaml --supervised` + - `ROBOPPI_SUPERVISED_IPC_TRANSPORT=stdio ROBOPPI_IPC_TRACE=1 ROBOPPI_IPC_REQUEST_TIMEOUT=45s VERBOSE=0 bash examples/agent-pr-loop-demo/run-in-tmp.sh` + - `ROBOPPI_SUPERVISED_IPC_TRANSPORT=stdio ROBOPPI_IPC_TRACE=1 ROBOPPI_IPC_REQUEST_TIMEOUT=20s bun run src/workflow/run.ts examples/hello-world.yaml --supervised` - Both commands still show: - Runner `tx submit_job` - Core startup logs (`AgentCore starting`, `AgentCore started, awaiting IPC messages`) - **No Core `[IPC][rx]` / ack** - 20–45s timeout then workflow fail - Additional isolation check: - - `AGENTCORE_IPC_TRACE=1 bun /home/reoring/roboppi/src/index.ts` with shell pipe (`printf ... | bun src/index.ts`) works and prints Core `[IPC][rx] submit_job` + `ack`. + - `ROBOPPI_IPC_TRACE=1 bun /home/reoring/roboppi/src/index.ts` with shell pipe (`printf ... | bun src/index.ts`) works and prints Core `[IPC][rx] submit_job` + `ack`. - The supervisor-like path that uses Bun runtime + `node:child_process` writing to a child bun process does not elicit Core `[IPC][rx]`, even when using minimal writer scripts. - Current hypothesis update (stdio): - On this machine, Bun runtime process-spawn + pipe transport may discard `stdin` for child bun processes at transport layer. @@ -175,7 +175,7 @@ Implemented additional stdio hardening to reduce the chance of Supervisor → Co ### 2026-02-14 (socket) 再実行結果 - Verified socket transport fixes the issue end-to-end: - - `AGENTCORE_SUPERVISED_IPC_TRANSPORT=socket AGENTCORE_IPC_REQUEST_TIMEOUT=2m VERBOSE=0 bash examples/agent-pr-loop-demo/run-in-tmp.sh` -> workflow `SUCCEEDED` and demo post-checks passed. - - `AGENTCORE_IPC_TRACE=1 bun run src/workflow/run.ts examples/hello-world.yaml --supervised` -> Core `[IPC][rx] submit_job` and `ack` observed; workflow `SUCCEEDED`. + - `ROBOPPI_SUPERVISED_IPC_TRANSPORT=socket ROBOPPI_IPC_REQUEST_TIMEOUT=2m VERBOSE=0 bash examples/agent-pr-loop-demo/run-in-tmp.sh` -> workflow `SUCCEEDED` and demo post-checks passed. + - `ROBOPPI_IPC_TRACE=1 bun run src/workflow/run.ts examples/hello-world.yaml --supervised` -> Core `[IPC][rx] submit_job` and `ack` observed; workflow `SUCCEEDED`. - Full test suite passes: `bun test` (949 tests). - Conclusion: use socket transport for non-interactive supervised runs; keep stdio as a fallback/debug option. diff --git a/docs/workflow-design.md b/docs/workflow-design.md index bbb4520..bfe0e44 100644 --- a/docs/workflow-design.md +++ b/docs/workflow-design.md @@ -173,7 +173,7 @@ interface CompletionCheckDef { instructions: string; capabilities: ("READ" | "EDIT" | "RUN_TESTS" | "RUN_COMMANDS")[]; timeout?: DurationString; - decision_file?: string; // optional; supports JSON {"decision":"complete"/"incomplete","check_id":"...","reasons":[...],"fingerprints":[...]} and legacy PASS/FAIL + decision_file?: string; // required for non-CUSTOM completion checks; JSON {"decision":"complete"/"incomplete","check_id":"...","reasons":[...],"fingerprints":[...]} } interface InputRef { @@ -231,7 +231,7 @@ steps: ### Loading rules -- Explicit: load catalogs from `AGENTCORE_AGENTS_FILE` (colon-separated list) and `--agents ` (repeatable). Catalogs are merged in order; later definitions override earlier ones. +- Explicit: load catalogs from `ROBOPPI_AGENTS_FILE` (colon-separated list) and `--agents ` (repeatable). Catalogs are merged in order; later definitions override earlier ones. - Implicit: if no explicit catalogs are provided, the runner looks for `agents.yaml` / `agents.yml` next to the workflow YAML. ## 3. Examples diff --git a/examples/agent-pr-loop-demo/run-in-tmp.sh b/examples/agent-pr-loop-demo/run-in-tmp.sh index afd9501..1a7c474 100755 --- a/examples/agent-pr-loop-demo/run-in-tmp.sh +++ b/examples/agent-pr-loop-demo/run-in-tmp.sh @@ -4,7 +4,7 @@ set -euo pipefail SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) ROBOPPI_ROOT=$(cd -- "${SCRIPT_DIR}/../.." && pwd) -if [[ "${ROBOPPI_IPC_TRACE:-${AGENTCORE_IPC_TRACE:-}}" == "1" ]]; then +if [[ "${ROBOPPI_IPC_TRACE:-}" == "1" ]]; then echo "[demo] ROBOPPI_ROOT=${ROBOPPI_ROOT}" >&2 if command -v git >/dev/null 2>&1; then SHA=$(git -C "${ROBOPPI_ROOT}" rev-parse --short HEAD 2>/dev/null || true) @@ -70,7 +70,6 @@ if [[ ! -f "${TARGET}/.gitignore" ]]; then cat > "${TARGET}/.gitignore" <<'EOF' # workflow state .roboppi-loop/ -.agentcore-loop/ context/ .daemon-context/ @@ -113,8 +112,8 @@ echo "Verifying generated repo..." if [[ -f "${TARGET}/.roboppi-loop/review.verdict" ]]; then V=$(cat "${TARGET}/.roboppi-loop/review.verdict" 2>/dev/null | tr -d '\r\n\t ') - if [[ "${V}" != "PASS" && "${V}" != *'"decision":"complete"'* ]]; then - echo "Error: expected review.verdict=PASS or decision=complete, got: ${V:-missing}" >&2 + if [[ "${V}" != *'"decision":"complete"'* ]]; then + echo "Error: expected review.verdict decision=complete, got: ${V:-missing}" >&2 exit 1 fi fi diff --git a/examples/agent-pr-loop.yaml b/examples/agent-pr-loop.yaml index 44ee0db..bf10b05 100644 --- a/examples/agent-pr-loop.yaml +++ b/examples/agent-pr-loop.yaml @@ -24,7 +24,7 @@ steps: description: "Validate inputs/tools and initialize loop state" worker: CUSTOM instructions: | - bash "${ROBOPPI_ROOT:-${AGENTCORE_ROOT:-.}}/scripts/agent-pr-loop/bootstrap.sh" + bash "${ROBOPPI_ROOT:-.}/scripts/agent-pr-loop/bootstrap.sh" capabilities: [READ, EDIT, RUN_COMMANDS] timeout: "2m" @@ -33,7 +33,7 @@ steps: worker: CUSTOM depends_on: [bootstrap] instructions: | - bash "${ROBOPPI_ROOT:-${AGENTCORE_ROOT:-.}}/scripts/agent-pr-loop/branch.sh" + bash "${ROBOPPI_ROOT:-.}/scripts/agent-pr-loop/branch.sh" capabilities: [READ, RUN_COMMANDS] timeout: "1m" @@ -103,7 +103,7 @@ steps: completion_check: worker: CUSTOM instructions: | - bash "${ROBOPPI_ROOT:-${AGENTCORE_ROOT:-.}}/scripts/agent-pr-loop/todo-check.sh" + bash "${ROBOPPI_ROOT:-.}/scripts/agent-pr-loop/todo-check.sh" capabilities: [READ, RUN_COMMANDS] timeout: "30s" @@ -170,7 +170,7 @@ steps: - Clear .roboppi-loop/fix.md if it exists (truncate to empty) so stale FAIL instructions cannot leak into future runs. Before reviewing, generate these review inputs (workspace is a git repo): - - bash "${ROBOPPI_ROOT:-${AGENTCORE_ROOT:-.}}/scripts/agent-pr-loop/review-inputs.sh" + - bash "${ROBOPPI_ROOT:-.}/scripts/agent-pr-loop/review-inputs.sh" You must read at least: - .roboppi-loop/request.md @@ -187,8 +187,8 @@ steps: Output rules: - Write .roboppi-loop/review.verdict as a single-line JSON object: - - PASS => {"decision":"complete","check_id":"$ROBOPPI_COMPLETION_CHECK_ID"} - - FAIL => {"decision":"incomplete","check_id":"$ROBOPPI_COMPLETION_CHECK_ID"} + - complete => {"decision":"complete","check_id":"$ROBOPPI_COMPLETION_CHECK_ID"} + - incomplete => {"decision":"incomplete","check_id":"$ROBOPPI_COMPLETION_CHECK_ID"} - When FAIL, also include: - "reasons": ["..."] (human-readable, short) - "fingerprints": ["..."] (stable IDs; used by the Convergence Controller) @@ -196,8 +196,7 @@ steps: - Stable across iterations for the *same* underlying issue. - Prefer file/line/test identifiers (e.g. "test:pkg/foo TestBar", "lint:src/x.ts:42", "scope:charts/..."). - Keep each item short. - - Fallback: if you cannot emit JSON, write legacy text "PASS" or "FAIL" only (no extra words). - - If verdict is PASS, clear .roboppi-loop/fix.md if it exists (truncate to empty). + - If verdict is complete, clear .roboppi-loop/fix.md if it exists (truncate to empty). capabilities: [READ, EDIT, RUN_COMMANDS] timeout: "15m" @@ -219,7 +218,7 @@ steps: worker: CUSTOM depends_on: [implement] instructions: | - bash "${ROBOPPI_ROOT:-${AGENTCORE_ROOT:-.}}/scripts/agent-pr-loop/create-pr.sh" + bash "${ROBOPPI_ROOT:-.}/scripts/agent-pr-loop/create-pr.sh" capabilities: [READ, EDIT, RUN_COMMANDS] timeout: "10m" outputs: diff --git a/examples/daemon/file-watcher.yaml b/examples/daemon/file-watcher.yaml index da6cd13..e922d1b 100644 --- a/examples/daemon/file-watcher.yaml +++ b/examples/daemon/file-watcher.yaml @@ -9,8 +9,8 @@ name: file-watcher version: "1" description: "Run tests automatically when TypeScript files change" -workspace: "/tmp/agentcore-daemon-watcher" -state_dir: "/tmp/agentcore-daemon-watcher/.daemon-state" +workspace: "/tmp/roboppi-daemon-watcher" +state_dir: "/tmp/roboppi-daemon-watcher/.daemon-state" events: src-change: diff --git a/examples/daemon/multi-trigger.yaml b/examples/daemon/multi-trigger.yaml index 9f9f08e..d35dd0d 100644 --- a/examples/daemon/multi-trigger.yaml +++ b/examples/daemon/multi-trigger.yaml @@ -6,8 +6,8 @@ name: multi-trigger version: "1" description: "Combine multiple event sources and triggers" -workspace: "/tmp/agentcore-daemon-multi" -state_dir: "/tmp/agentcore-daemon-multi/.daemon-state" +workspace: "/tmp/roboppi-daemon-multi" +state_dir: "/tmp/roboppi-daemon-multi/.daemon-state" max_concurrent_workflows: 2 events: diff --git a/examples/daemon/simple-cron.yaml b/examples/daemon/simple-cron.yaml index dc8e971..8be8dfc 100644 --- a/examples/daemon/simple-cron.yaml +++ b/examples/daemon/simple-cron.yaml @@ -6,8 +6,8 @@ name: simple-cron version: "1" description: "A simple daemon that runs a health-check every 30 seconds" -workspace: "/tmp/agentcore-daemon-simple" -state_dir: "/tmp/agentcore-daemon-simple/.daemon-state" +workspace: "/tmp/roboppi-daemon-simple" +state_dir: "/tmp/roboppi-daemon-simple/.daemon-state" events: tick: diff --git a/examples/daemon/smart-reviewer.yaml b/examples/daemon/smart-reviewer.yaml index 668cb70..5c8409e 100644 --- a/examples/daemon/smart-reviewer.yaml +++ b/examples/daemon/smart-reviewer.yaml @@ -7,8 +7,8 @@ name: smart-reviewer version: "1" description: "Run code review only when new commits exist" -workspace: "/tmp/agentcore-daemon-reviewer" -state_dir: "/tmp/agentcore-daemon-reviewer/.daemon-state" +workspace: "/tmp/roboppi-daemon-reviewer" +state_dir: "/tmp/roboppi-daemon-reviewer/.daemon-state" max_concurrent_workflows: 1 events: diff --git a/package.json b/package.json index eea0854..594f781 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,9 @@ { "name": "roboppi", - "version": "0.1.2", + "version": "0.1.1", "type": "module", "bin": { - "roboppi": "src/cli.ts", - "agentcore": "src/cli.ts" + "roboppi": "src/cli.ts" }, "scripts": { "start": "bun run src/cli.ts", diff --git a/scripts/agent-pr-loop/bootstrap.sh b/scripts/agent-pr-loop/bootstrap.sh index 5082b37..844463f 100755 --- a/scripts/agent-pr-loop/bootstrap.sh +++ b/scripts/agent-pr-loop/bootstrap.sh @@ -7,21 +7,9 @@ if ! git rev-parse --git-dir > /dev/null 2>&1; then fi LOOP_DIR=.roboppi-loop -LEGACY_DIR=.agentcore-loop mkdir -p "${LOOP_DIR}" -# Best-effort migration from legacy loop directory. -# If the legacy dir exists, copy user-provided inputs into the new dir so the -# updated workflow can run without manual moves. -if [ -d "${LEGACY_DIR}" ]; then - for f in request.md base-branch.txt base-sha.txt branch.txt enable_pr; do - if [ ! -f "${LOOP_DIR}/${f}" ] && [ -f "${LEGACY_DIR}/${f}" ]; then - cp "${LEGACY_DIR}/${f}" "${LOOP_DIR}/${f}" - fi - done -fi - # Clear derived artifacts from prior runs (keep request + branch/base settings). rm -f \ "${LOOP_DIR}/review.md" \ @@ -68,9 +56,6 @@ if [ "${BASE_BRANCH}" = "HEAD" ]; then exit 1 fi -if [ -d "${LEGACY_DIR}" ]; then - echo "${BASE_BRANCH}" > "${LEGACY_DIR}/base-branch.txt" -fi echo "${BASE_BRANCH}" > "${LOOP_DIR}/base-branch.txt" git rev-parse "${BASE_BRANCH}^{commit}" > "${LOOP_DIR}/base-sha.txt" 2>/dev/null || true diff --git a/scripts/agent-pr-loop/branch.sh b/scripts/agent-pr-loop/branch.sh index f9980ad..a85653f 100755 --- a/scripts/agent-pr-loop/branch.sh +++ b/scripts/agent-pr-loop/branch.sh @@ -2,14 +2,10 @@ set -euo pipefail LOOP_DIR=.roboppi-loop -LEGACY_DIR=.agentcore-loop mkdir -p "${LOOP_DIR}" BASE_BRANCH_FILE="${LOOP_DIR}/base-branch.txt" -if [ ! -f "${BASE_BRANCH_FILE}" ] && [ -f "${LEGACY_DIR}/base-branch.txt" ]; then - BASE_BRANCH_FILE="${LEGACY_DIR}/base-branch.txt" -fi BASE_BRANCH=$( cat "${BASE_BRANCH_FILE}" 2>/dev/null \ @@ -17,9 +13,6 @@ BASE_BRANCH=$( ) BRANCH_FILE="${LOOP_DIR}/branch.txt" -if [ ! -f "${BRANCH_FILE}" ] && [ -f "${LEGACY_DIR}/branch.txt" ]; then - cp "${LEGACY_DIR}/branch.txt" "${BRANCH_FILE}" -fi if [ -f "${BRANCH_FILE}" ] && [ -n "$(tr -d ' ' < "${BRANCH_FILE}")" ]; then BRANCH=$(cat "${BRANCH_FILE}") diff --git a/scripts/agent-pr-loop/create-pr.sh b/scripts/agent-pr-loop/create-pr.sh index 10eddd0..3e81fac 100755 --- a/scripts/agent-pr-loop/create-pr.sh +++ b/scripts/agent-pr-loop/create-pr.sh @@ -2,24 +2,9 @@ set -euo pipefail LOOP_DIR=.roboppi-loop -LEGACY_DIR=.agentcore-loop mkdir -p "${LOOP_DIR}" -if [ ! -f "${LOOP_DIR}/request.md" ] && [ -f "${LEGACY_DIR}/request.md" ]; then - cp "${LEGACY_DIR}/request.md" "${LOOP_DIR}/request.md" -fi -if [ ! -f "${LOOP_DIR}/base-branch.txt" ] && [ -f "${LEGACY_DIR}/base-branch.txt" ]; then - cp "${LEGACY_DIR}/base-branch.txt" "${LOOP_DIR}/base-branch.txt" -fi -if [ ! -f "${LOOP_DIR}/branch.txt" ] && [ -f "${LEGACY_DIR}/branch.txt" ]; then - cp "${LEGACY_DIR}/branch.txt" "${LOOP_DIR}/branch.txt" -fi - -if [ ! -f "${LOOP_DIR}/enable_pr" ] && [ -f "${LEGACY_DIR}/enable_pr" ]; then - cp "${LEGACY_DIR}/enable_pr" "${LOOP_DIR}/enable_pr" -fi - if [ ! -f "${LOOP_DIR}/enable_pr" ]; then cat > "${LOOP_DIR}/pr.txt" < { const workspaceDir = process.cwd(); const loopDir = path.join(workspaceDir, ".roboppi-loop"); - const legacyLoopDir = path.join(workspaceDir, ".agentcore-loop"); await mkdir(loopDir, { recursive: true }); - // Best-effort migration from legacy loop directory. - for (const f of ["request.md", "design.md", "todo.md", "base-branch.txt"] as const) { - const dst = path.join(loopDir, f); - const src = path.join(legacyLoopDir, f); - if (!(await exists(dst)) && (await exists(src))) { - await copyFile(src, dst); - } - } - const verbose = process.env.ROBOPPI_VERBOSE === "1" || - process.env.AGENTCORE_VERBOSE === "1" || process.env.VERBOSE === "1" || process.env.DEBUG === "1"; @@ -85,7 +74,7 @@ async function main(): Promise { await writeFile(path.join(loopDir, "review.status"), status.stdout); await writeFile(path.join(loopDir, "review.untracked"), untracked.stdout); - await writeFile(path.join(loopDir, "review.verdict"), "FAIL\n"); + await writeFile(path.join(loopDir, "review.verdict"), '{"decision":"incomplete"}\n'); await writeFile(path.join(loopDir, "review.md"), ""); await writeFile(path.join(loopDir, "fix.md"), ""); @@ -100,8 +89,8 @@ Task: - Review the work against the request. - Use apply_patch to create/overwrite these files: - .roboppi-loop/review.md - - .roboppi-loop/review.verdict (PASS or FAIL only) -- If verdict is FAIL, also create/overwrite: + - .roboppi-loop/review.verdict (structured JSON only: {"decision":"complete"|"incomplete","check_id":"..."} ; include check_id when ROBOPPI_COMPLETION_CHECK_ID is provided) +- If verdict is incomplete, also create/overwrite: - .roboppi-loop/fix.md (non-empty; concrete, actionable steps; include file paths) You must read at least: @@ -144,9 +133,9 @@ Format for .roboppi-loop/review.md: model: "openai/gpt-5.2", instructions: `Use apply_patch to write: - .roboppi-loop/review.md -- .roboppi-loop/review.verdict (PASS or FAIL) +- .roboppi-loop/review.verdict (structured JSON only: {"decision":"complete"|"incomplete","check_id":"..."}) -If verdict is FAIL, also write: +If verdict is incomplete, also write: - .roboppi-loop/fix.md (non-empty) Base your decision on: @@ -170,11 +159,22 @@ Base your decision on: } const verdictRaw = await readText(verdictPath); - const verdict = verdictRaw.trim().toUpperCase(); + const trimmed = verdictRaw.trim(); + let decision: "complete" | "incomplete" | null = null; + try { + const parsed = JSON.parse(trimmed) as unknown; + if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { + const d = (parsed as { decision?: unknown }).decision; + if (d === "complete" || d === "incomplete") decision = d; + } + } catch { + // ignore + } - console.log(`[review-fix-once] verdict: ${verdict || "(missing)"}`); - if (verdict === "PASS") { - return; + console.log(`[review-fix-once] verdict: ${decision ?? "(invalid)"}`); + if (decision === "complete") return; + if (decision !== "incomplete") { + throw new Error("review.verdict must be JSON with decision=complete|incomplete"); } const fixPath = path.join(loopDir, "fix.md"); diff --git a/scripts/agent-pr-loop/review-inputs.sh b/scripts/agent-pr-loop/review-inputs.sh index 51bd467..a7ca1de 100755 --- a/scripts/agent-pr-loop/review-inputs.sh +++ b/scripts/agent-pr-loop/review-inputs.sh @@ -11,14 +11,9 @@ set -euo pipefail # .roboppi-loop/review.untracked.diff - diffs for untracked files (as /dev/null -> file) LOOP_DIR=.roboppi-loop -LEGACY_DIR=.agentcore-loop mkdir -p "${LOOP_DIR}" -if [ ! -f "${LOOP_DIR}/base-branch.txt" ] && [ -f "${LEGACY_DIR}/base-branch.txt" ]; then - cp "${LEGACY_DIR}/base-branch.txt" "${LOOP_DIR}/base-branch.txt" -fi - BASE_BRANCH=$( cat "${LOOP_DIR}/base-branch.txt" 2>/dev/null \ || echo "${BASE_BRANCH:-$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo HEAD)}" @@ -40,9 +35,9 @@ git status --porcelain=v1 > "${LOOP_DIR}/review.status" || true git ls-files --others --exclude-standard > "${LOOP_DIR}/review.untracked" || true # For large repos, keep untracked diffs bounded. -MAX_FILES=${ROBOPPI_REVIEW_UNTRACKED_MAX_FILES:-${AGENTCORE_REVIEW_UNTRACKED_MAX_FILES:-200}} -MAX_FILE_BYTES=${ROBOPPI_REVIEW_UNTRACKED_MAX_FILE_BYTES:-${AGENTCORE_REVIEW_UNTRACKED_MAX_FILE_BYTES:-200000}} -MAX_TOTAL_BYTES=${ROBOPPI_REVIEW_UNTRACKED_MAX_TOTAL_BYTES:-${AGENTCORE_REVIEW_UNTRACKED_MAX_TOTAL_BYTES:-2000000}} +MAX_FILES=${ROBOPPI_REVIEW_UNTRACKED_MAX_FILES:-200} +MAX_FILE_BYTES=${ROBOPPI_REVIEW_UNTRACKED_MAX_FILE_BYTES:-200000} +MAX_TOTAL_BYTES=${ROBOPPI_REVIEW_UNTRACKED_MAX_TOTAL_BYTES:-2000000} out="${LOOP_DIR}/review.untracked.diff" : > "${out}" diff --git a/scripts/agent-pr-loop/todo-check.sh b/scripts/agent-pr-loop/todo-check.sh index 822496f..ddb2af7 100755 --- a/scripts/agent-pr-loop/todo-check.sh +++ b/scripts/agent-pr-loop/todo-check.sh @@ -2,12 +2,8 @@ set -euo pipefail LOOP_DIR=.roboppi-loop -LEGACY_DIR=.agentcore-loop TODO="${LOOP_DIR}/todo.md" -if [ ! -f "${TODO}" ] && [ -f "${LEGACY_DIR}/todo.md" ]; then - TODO="${LEGACY_DIR}/todo.md" -fi test -s "${TODO}" || { echo "CHECK: missing todo"; exit 1; } COUNT=$(grep -c '^- \[ \] ' "${TODO}" || true) diff --git a/scripts/agent-pr-loop/verdict-check.sh b/scripts/agent-pr-loop/verdict-check.sh index 98f7838..f7cc046 100755 --- a/scripts/agent-pr-loop/verdict-check.sh +++ b/scripts/agent-pr-loop/verdict-check.sh @@ -2,12 +2,8 @@ set -euo pipefail LOOP_DIR=.roboppi-loop -LEGACY_DIR=.agentcore-loop VERDICT_FILE="${LOOP_DIR}/review.verdict" -if [ ! -f "${VERDICT_FILE}" ] && [ -f "${LEGACY_DIR}/review.verdict" ]; then - VERDICT_FILE="${LEGACY_DIR}/review.verdict" -fi V=$(cat "${VERDICT_FILE}" 2>/dev/null | tr -d '\r\n\t ') @@ -17,11 +13,5 @@ if [[ "${V}" == *'"decision":"complete"'* ]]; then exit 0 fi -# Legacy fallback: PASS/FAIL text. -if [ "${V}" = "PASS" ]; then - echo "CHECK: PASS" - exit 0 -fi - echo "CHECK: still failing (${V:-missing})" exit 1 diff --git a/scripts/binary-e2e.ts b/scripts/binary-e2e.ts index 82c5d1a..dfbaf9b 100644 --- a/scripts/binary-e2e.ts +++ b/scripts/binary-e2e.ts @@ -1,5 +1,5 @@ /** - * E2E demo: agentcore run subcommand. + * E2E demo: roboppi run subcommand. * * Demonstrates the built binary executing a one-shot worker task * via CLI arguments — no driver script or IPC needed. @@ -7,24 +7,24 @@ export {}; -const WORKSPACE = "/tmp/agentcore-binary-demo"; +const WORKSPACE = "/tmp/roboppi-binary-demo"; const log = (msg: string) => console.log(`[demo] ${msg}`); -log("=== AgentCore Binary E2E Demo ===\n"); +log("=== Roboppi Binary E2E Demo ===\n"); // Clean workspace await Bun.$`rm -rf ${WORKSPACE} && mkdir -p ${WORKSPACE}`.quiet(); log(`Workspace: ${WORKSPACE}`); -// Run agentcore with the "run" subcommand +// Run roboppi with the "run" subcommand const instructions = "Create countdown.ts. It should accept a number N via CLI args, count down from N, then print Liftoff!. Example: bun run countdown.ts 5 -> 5,4,3,2,1,Liftoff!"; -log(`\nRunning: ./agentcore run --worker opencode --workspace ${WORKSPACE} "${instructions.slice(0, 60)}..."\n`); +log(`\nRunning: ./roboppi run --worker opencode --workspace ${WORKSPACE} "${instructions.slice(0, 60)}..."\n`); const startTime = Date.now(); const proc = Bun.spawn( - ["./agentcore", "run", "--worker", "opencode", "--workspace", WORKSPACE, instructions], + ["./roboppi", "run", "--worker", "opencode", "--workspace", WORKSPACE, instructions], { stdout: "pipe", stderr: "inherit" }, ); diff --git a/scripts/manual-test.ts b/scripts/manual-test.ts index 612feb8..c655d5c 100644 --- a/scripts/manual-test.ts +++ b/scripts/manual-test.ts @@ -1,33 +1,33 @@ /** - * Manual E2E test: Spawn AgentCore directly and communicate via JSON Lines. + * Manual E2E test: Spawn Roboppi Core directly and communicate via JSON Lines. */ import { generateId } from "../src/types/common.js"; import { JobType, PriorityClass } from "../src/types/index.js"; const log = (msg: string) => console.error(`[manual-test] ${msg}`); -log("=== AgentCore Manual E2E Test ===\n"); +log("=== Roboppi Core Manual E2E Test ===\n"); -// Spawn AgentCore as child process -log("Spawning AgentCore child process..."); +// Spawn Roboppi Core as child process +log("Spawning Roboppi Core child process..."); const proc = Bun.spawn(["bun", "run", "src/index.ts"], { - cwd: "/home/reoring/agentcore", + cwd: process.cwd(), stdin: "pipe", stdout: "pipe", stderr: "inherit", }); -log(`AgentCore PID: ${proc.pid}`); +log(`Core PID: ${proc.pid}`); -// Helper: send JSON Lines message to AgentCore's stdin -function send(msg: object) { +// Helper: send JSON Lines message to Core's stdin +function send(msg: { type: string } & Record) { const line = JSON.stringify(msg) + "\n"; log(` -> ${msg.type}: ${line.trim()}`); proc.stdin.write(line); proc.stdin.flush(); } -// Helper: read responses from AgentCore's stdout +// Helper: read responses from Core's stdout async function readResponses(maxWaitMs: number): Promise { const messages: unknown[] = []; const decoder = new TextDecoder(); diff --git a/scripts/opencode-e2e.ts b/scripts/opencode-e2e.ts index 8f982d5..2480c09 100644 --- a/scripts/opencode-e2e.ts +++ b/scripts/opencode-e2e.ts @@ -174,7 +174,7 @@ log(`Permit GRANTED: ${permitResult.permitId}\n`); const task: WorkerTask = { workerTaskId: generateId(), workerKind: WorkerKind.OPENCODE, - workspaceRef: "/tmp/agentcore-opencode-test", + workspaceRef: "/tmp/roboppi-opencode-test", instructions: "Create a simple TypeScript project with: 1) hello.ts that exports a greet(name: string) function returning a greeting string, 2) main.ts that imports greet from hello.ts and calls it with 'AgentCore', logging the result, 3) README.md explaining the project. No external dependencies.", capabilities: [WorkerCapability.EDIT, WorkerCapability.RUN_COMMANDS], outputMode: "BATCH" as any, @@ -200,13 +200,13 @@ log(` Wall time: ${elapsed}s`); permitGate.completePermit(permitResult.permitId); // Show what was created -log(`\n=== Files created in /tmp/agentcore-opencode-test ===`); -const ls = Bun.spawn(["find", "/tmp/agentcore-opencode-test", "-type", "f"], { stdout: "pipe" }); +log(`\n=== Files created in /tmp/roboppi-opencode-test ===`); +const ls = Bun.spawn(["find", "/tmp/roboppi-opencode-test", "-type", "f"], { stdout: "pipe" }); const files = (await new Response(ls.stdout).text()).trim(); log(files || "(none)"); for (const filepath of files.split("\n").filter(Boolean)) { - const name = filepath.replace("/tmp/agentcore-opencode-test/", ""); + const name = filepath.replace("/tmp/roboppi-opencode-test/", ""); if (name.startsWith(".")) continue; log(`\n--- ${name} ---`); const content = await Bun.file(filepath).text(); diff --git a/src/cli.ts b/src/cli.ts index 1ee6ec0..08d6482 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,17 +1,13 @@ #!/usr/bin/env bun /** - * AgentCore CLI — execution control runtime for AI agents. + * Roboppi CLI — execution control runtime for AI agents. * - * Modes: - * roboppi [options] Core IPC server (stdin/stdout JSON Lines) - * roboppi run --worker ... One-shot worker task execution - * roboppi workflow ... Workflow runner - * roboppi daemon ... Daemon mode - * - * Compatibility alias: - * agentcore (same CLI) + * Modes: + * roboppi [options] Core IPC server (stdin/stdout JSON Lines) + * roboppi run --worker ... One-shot worker task execution + * roboppi workflow ... Workflow runner + * roboppi daemon ... Daemon mode */ -import { applyEnvPrefixAliases } from "./core/env-aliases.js"; import type { AgentCoreConfig } from "./core/agentcore.js"; import type { LogLevel } from "./core/observability.js"; import { startCoreRuntime } from "./core/core-runtime.js"; @@ -28,8 +24,6 @@ import { generateId } from "./types/common.js"; import { WorkerKind, WorkerCapability, OutputMode, WorkerStatus, JobType, PriorityClass } from "./types/index.js"; import type { WorkerTask, Job } from "./types/index.js"; -applyEnvPrefixAliases(); - // ── Argument parsing ────────────────────────────────────────────── interface SharedOptions { diff --git a/src/core/core-runtime.ts b/src/core/core-runtime.ts index e1f2dfb..133b81b 100644 --- a/src/core/core-runtime.ts +++ b/src/core/core-runtime.ts @@ -1,7 +1,6 @@ import { JsonLinesTransport } from "../ipc/json-lines-transport.js"; import { IpcProtocol } from "../ipc/protocol.js"; import { AgentCore, type AgentCoreConfig } from "./agentcore.js"; -import { applyEnvPrefixAliases } from "./env-aliases.js"; import { Logger } from "./observability.js"; import { ProcessManager } from "../worker/process-manager.js"; @@ -69,15 +68,12 @@ export function startCoreRuntime(options: CoreRuntimeOptions = {}): void { const logger = new Logger(options.loggerComponent ?? "main", config?.logLevel ?? "info"); // Mark this process as the Core runtime for logging/diagnostics. - applyEnvPrefixAliases(); if (!process.env.ROBOPPI_COMPONENT) process.env.ROBOPPI_COMPONENT = "core"; - if (!process.env.AGENTCORE_COMPONENT) process.env.AGENTCORE_COMPONENT = "core"; - applyEnvPrefixAliases(); // IPC transport: JSONL over either stdio or a supervised IPC socket. - const ipcSocketPath = (process.env.AGENTCORE_IPC_SOCKET_PATH ?? process.env.ROBOPPI_IPC_SOCKET_PATH)?.trim(); - const ipcSocketHost = (process.env.AGENTCORE_IPC_SOCKET_HOST ?? process.env.ROBOPPI_IPC_SOCKET_HOST)?.trim(); - const ipcSocketPortRaw = (process.env.AGENTCORE_IPC_SOCKET_PORT ?? process.env.ROBOPPI_IPC_SOCKET_PORT)?.trim(); + const ipcSocketPath = process.env.ROBOPPI_IPC_SOCKET_PATH?.trim(); + const ipcSocketHost = process.env.ROBOPPI_IPC_SOCKET_HOST?.trim(); + const ipcSocketPortRaw = process.env.ROBOPPI_IPC_SOCKET_PORT?.trim(); let ipcSocket: Socket | null = null; let ipcSocketKind: "unix" | "tcp" | null = null; @@ -88,7 +84,7 @@ export function startCoreRuntime(options: CoreRuntimeOptions = {}): void { if (ipcSocketPortRaw) { const port = Number.parseInt(ipcSocketPortRaw, 10); if (!Number.isFinite(port) || port <= 0 || port > 65535) { - throw new Error(`Invalid AGENTCORE_IPC_SOCKET_PORT: "${ipcSocketPortRaw}"`); + throw new Error(`Invalid ROBOPPI_IPC_SOCKET_PORT: "${ipcSocketPortRaw}"`); } const host = ipcSocketHost && ipcSocketHost !== "" ? ipcSocketHost : "127.0.0.1"; ipcSocket = createConnection({ host, port }); @@ -139,9 +135,9 @@ export function startCoreRuntime(options: CoreRuntimeOptions = {}): void { // Keepalive output for environments that kill silent subprocesses. // Enabled by default when non-interactive; can be overridden via env. - const keepaliveEnabled = parseEnvBool(process.env.AGENTCORE_KEEPALIVE) ?? isNonInteractive(); + const keepaliveEnabled = parseEnvBool(process.env.ROBOPPI_KEEPALIVE) ?? isNonInteractive(); const keepaliveIntervalMs = (() => { - const raw = process.env.AGENTCORE_KEEPALIVE_INTERVAL ?? "10s"; + const raw = process.env.ROBOPPI_KEEPALIVE_INTERVAL ?? "10s"; try { return parseDurationMs(raw); } catch { @@ -198,7 +194,7 @@ export function startCoreRuntime(options: CoreRuntimeOptions = {}): void { shutdown("unhandledRejection").catch(() => process.exit(1)); }); - logger.info("AgentCore starting", { + logger.info("Roboppi core starting", { config: { concurrency: config?.budget?.maxConcurrency ?? 10, rps: config?.budget?.maxRps ?? 50, @@ -208,7 +204,7 @@ export function startCoreRuntime(options: CoreRuntimeOptions = {}): void { core.start(); - logger.info("AgentCore started, awaiting IPC messages", { + logger.info("Roboppi core started, awaiting IPC messages", { transport: ipcSocket ? (ipcSocketKind === "tcp" ? "tcp" : "socket") : "stdio", socketPath: ipcSocketKind === "unix" ? (ipcSocketPath || undefined) : undefined, socketHost: ipcSocketKind === "tcp" ? resolvedIpcHost : undefined, diff --git a/src/core/env-aliases.ts b/src/core/env-aliases.ts deleted file mode 100644 index c096041..0000000 --- a/src/core/env-aliases.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Environment variable compatibility layer. - * - * Roboppi is the product name, but this repo historically used the `AGENTCORE_` - * prefix for many environment variables. - * - * To keep backward compatibility while moving docs/examples to `ROBOPPI_`, we - * mirror values across prefixes (without overriding explicitly-set values). - */ - -export function applyEnvPrefixAliases(): void { - const env = process.env as Record; - - for (const [key, value] of Object.entries(env)) { - if (value === undefined) continue; - - if (key.startsWith("ROBOPPI_")) { - const suffix = key.slice("ROBOPPI_".length); - const legacy = `AGENTCORE_${suffix}`; - if (env[legacy] === undefined) env[legacy] = value; - continue; - } - - if (key.startsWith("AGENTCORE_")) { - const suffix = key.slice("AGENTCORE_".length); - const modern = `ROBOPPI_${suffix}`; - if (env[modern] === undefined) env[modern] = value; - continue; - } - } -} diff --git a/src/daemon/cli.ts b/src/daemon/cli.ts index 01b6929..5be65be 100644 --- a/src/daemon/cli.ts +++ b/src/daemon/cli.ts @@ -11,7 +11,6 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import { parseDaemonConfig } from "./parser.js"; import { Daemon } from "./daemon.js"; -import { applyEnvPrefixAliases } from "../core/env-aliases.js"; function isNonInteractive(): boolean { // Treat either stream being a TTY as interactive. @@ -27,7 +26,7 @@ function resolveCoreEntryPointForSupervised(coreEntryPointOverride: string | und const fromCli = coreEntryPointOverride?.trim(); if (fromCli) return fromCli; - const fromEnv = (process.env.ROBOPPI_CORE_ENTRYPOINT ?? process.env.AGENTCORE_CORE_ENTRYPOINT)?.trim(); + const fromEnv = process.env.ROBOPPI_CORE_ENTRYPOINT?.trim(); if (fromEnv) return fromEnv; // Compiled binary: spawn this executable as the Core process. @@ -41,8 +40,6 @@ function resolveCoreEntryPointForSupervised(coreEntryPointOverride: string | und } export async function runDaemonCli(argv: string[]): Promise { - applyEnvPrefixAliases(); - const args = argv; let yamlPath = ""; let verbose = false; @@ -93,20 +90,14 @@ Options: if (verbose) { process.env.ROBOPPI_VERBOSE = "1"; - process.env.AGENTCORE_VERBOSE = "1"; } // Default supervised IPC transport. // Mirror workflow runner behavior: prefer socket transport in non-interactive mode. - // (Override via ROBOPPI_SUPERVISED_IPC_TRANSPORT or AGENTCORE_SUPERVISED_IPC_TRANSPORT.) - if ( - supervised && - process.env.ROBOPPI_SUPERVISED_IPC_TRANSPORT === undefined && - process.env.AGENTCORE_SUPERVISED_IPC_TRANSPORT === undefined - ) { + // (Override via ROBOPPI_SUPERVISED_IPC_TRANSPORT.) + if (supervised && process.env.ROBOPPI_SUPERVISED_IPC_TRANSPORT === undefined) { const val = isNonInteractive() ? "socket" : "stdio"; process.env.ROBOPPI_SUPERVISED_IPC_TRANSPORT = val; - process.env.AGENTCORE_SUPERVISED_IPC_TRANSPORT = val; } if (!yamlPath) { diff --git a/src/daemon/daemon.ts b/src/daemon/daemon.ts index f7358a9..88376dc 100644 --- a/src/daemon/daemon.ts +++ b/src/daemon/daemon.ts @@ -171,7 +171,7 @@ export class Daemon { const merged = mergeEventSources(this.eventSources); // 6b. Initialize step runner - const verbose = process.env.AGENTCORE_VERBOSE === "1"; + const verbose = process.env.ROBOPPI_VERBOSE === "1"; if (this.supervised) { this.coreRunner = new CoreIpcStepRunner({ verbose, @@ -452,14 +452,10 @@ export class Daemon { envBaseBranch: workflowEnv.BASE_BRANCH ?? process.env.BASE_BRANCH, envProtectedBranches: workflowEnv.ROBOPPI_PROTECTED_BRANCHES ?? - workflowEnv.AGENTCORE_PROTECTED_BRANCHES ?? - process.env.ROBOPPI_PROTECTED_BRANCHES ?? - process.env.AGENTCORE_PROTECTED_BRANCHES, + process.env.ROBOPPI_PROTECTED_BRANCHES, envAllowProtectedBranch: workflowEnv.ROBOPPI_ALLOW_PROTECTED_BRANCH ?? - workflowEnv.AGENTCORE_ALLOW_PROTECTED_BRANCH ?? - process.env.ROBOPPI_ALLOW_PROTECTED_BRANCH ?? - process.env.AGENTCORE_ALLOW_PROTECTED_BRANCH, + process.env.ROBOPPI_ALLOW_PROTECTED_BRANCH, createBranch: definition.create_branch ?? false, expectedWorkBranch: definition.expected_work_branch, branchTransitionStep: definition.branch_transition_step, @@ -467,47 +463,35 @@ export class Daemon { }); this.logBranchContext(safeTriggerId, branchContext); - workflowEnv.AGENTCORE_CREATE_BRANCH = branchContext.createBranch ? "1" : "0"; - workflowEnv.ROBOPPI_CREATE_BRANCH = workflowEnv.AGENTCORE_CREATE_BRANCH; - workflowEnv.AGENTCORE_PROTECTED_BRANCHES = branchContext.protectedBranches.join(","); - workflowEnv.ROBOPPI_PROTECTED_BRANCHES = workflowEnv.AGENTCORE_PROTECTED_BRANCHES; - workflowEnv.AGENTCORE_ALLOW_PROTECTED_BRANCH = branchContext.allowProtectedBranch + workflowEnv.ROBOPPI_CREATE_BRANCH = branchContext.createBranch ? "1" : "0"; + workflowEnv.ROBOPPI_PROTECTED_BRANCHES = branchContext.protectedBranches.join(","); + workflowEnv.ROBOPPI_ALLOW_PROTECTED_BRANCH = branchContext.allowProtectedBranch ? "1" : "0"; - workflowEnv.ROBOPPI_ALLOW_PROTECTED_BRANCH = workflowEnv.AGENTCORE_ALLOW_PROTECTED_BRANCH; if (branchContext.effectiveBaseBranch) { workflowEnv.BASE_BRANCH = branchContext.effectiveBaseBranch; - workflowEnv.AGENTCORE_EFFECTIVE_BASE_BRANCH = branchContext.effectiveBaseBranch; workflowEnv.ROBOPPI_EFFECTIVE_BASE_BRANCH = branchContext.effectiveBaseBranch; } if (branchContext.effectiveBaseBranchSource) { - workflowEnv.AGENTCORE_EFFECTIVE_BASE_BRANCH_SOURCE = - branchContext.effectiveBaseBranchSource; workflowEnv.ROBOPPI_EFFECTIVE_BASE_BRANCH_SOURCE = branchContext.effectiveBaseBranchSource; } if (branchContext.effectiveBaseSha) { - workflowEnv.AGENTCORE_EFFECTIVE_BASE_SHA = branchContext.effectiveBaseSha; workflowEnv.ROBOPPI_EFFECTIVE_BASE_SHA = branchContext.effectiveBaseSha; } if (branchContext.startupBranch) { - workflowEnv.AGENTCORE_STARTUP_BRANCH = branchContext.startupBranch; workflowEnv.ROBOPPI_STARTUP_BRANCH = branchContext.startupBranch; } if (branchContext.startupHeadSha) { - workflowEnv.AGENTCORE_STARTUP_HEAD_SHA = branchContext.startupHeadSha; workflowEnv.ROBOPPI_STARTUP_HEAD_SHA = branchContext.startupHeadSha; } if (branchContext.startupToplevel) { - workflowEnv.AGENTCORE_STARTUP_TOPLEVEL = branchContext.startupToplevel; workflowEnv.ROBOPPI_STARTUP_TOPLEVEL = branchContext.startupToplevel; } if (branchContext.expectedWorkBranch) { - workflowEnv.AGENTCORE_EXPECTED_WORK_BRANCH = branchContext.expectedWorkBranch; workflowEnv.ROBOPPI_EXPECTED_WORK_BRANCH = branchContext.expectedWorkBranch; } if (branchContext.expectedCurrentBranch) { - workflowEnv.AGENTCORE_EXPECTED_CURRENT_BRANCH = branchContext.expectedCurrentBranch; workflowEnv.ROBOPPI_EXPECTED_CURRENT_BRANCH = branchContext.expectedCurrentBranch; } const executorEnv = @@ -567,11 +551,11 @@ export class Daemon { workflowEnv: Record, ): Promise { const fromProcessEnv = splitPathList( - process.env.ROBOPPI_AGENTS_FILE ?? process.env.AGENTCORE_AGENTS_FILE, + process.env.ROBOPPI_AGENTS_FILE, ); const fromConfig = this.config.agents_file ? [this.config.agents_file] : []; const fromWorkflowEnv = splitPathList( - workflowEnv.ROBOPPI_AGENTS_FILE ?? workflowEnv.AGENTCORE_AGENTS_FILE, + workflowEnv.ROBOPPI_AGENTS_FILE, ); // Precedence: process env (lowest) -> daemon config -> trigger env (highest). diff --git a/src/daemon/evaluate-gate.ts b/src/daemon/evaluate-gate.ts index 54dcf68..e148bd2 100644 --- a/src/daemon/evaluate-gate.ts +++ b/src/daemon/evaluate-gate.ts @@ -80,12 +80,10 @@ export class EvaluateGate { } // Pass template variables as environment variables to prevent shell injection. - // The shell command can reference them as $ROBOPPI_EVENT/$AGENTCORE_EVENT, - // $ROBOPPI_TRIGGER_ID/$AGENTCORE_TRIGGER_ID, etc. + // The shell command can reference them as $ROBOPPI_EVENT, $ROBOPPI_TRIGGER_ID, etc. const env: Record = { ...process.env as Record }; for (const [key, value] of Object.entries(vars)) { const upper = key.toUpperCase(); - env[`AGENTCORE_${upper}`] = value; env[`ROBOPPI_${upper}`] = value; } @@ -167,7 +165,6 @@ export class EvaluateGate { const env: Record = {}; for (const [key, value] of Object.entries(vars)) { const upper = key.toUpperCase(); - env[`AGENTCORE_${upper}`] = value; env[`ROBOPPI_${upper}`] = value; } diff --git a/src/daemon/result-analyzer.ts b/src/daemon/result-analyzer.ts index 3589ef0..e2e9e86 100644 --- a/src/daemon/result-analyzer.ts +++ b/src/daemon/result-analyzer.ts @@ -56,7 +56,6 @@ export class ResultAnalyzer { const varsEnv: Record = {}; for (const [key, value] of Object.entries(vars)) { const upper = key.toUpperCase(); - varsEnv[`AGENTCORE_${upper}`] = value; varsEnv[`ROBOPPI_${upper}`] = value; } const env: Record = { ...process.env as Record, ...varsEnv }; diff --git a/src/ipc/json-lines-transport.ts b/src/ipc/json-lines-transport.ts index 56dcdd2..ae05f41 100644 --- a/src/ipc/json-lines-transport.ts +++ b/src/ipc/json-lines-transport.ts @@ -26,7 +26,7 @@ export class JsonLinesTransport { private closed = false; private readLoopPromise: Promise | null = null; - private readonly traceEnabled = process.env.AGENTCORE_IPC_TRACE === "1"; + private readonly traceEnabled = process.env.ROBOPPI_IPC_TRACE === "1"; constructor( input: ReadableStream, diff --git a/src/scheduler/supervisor.ts b/src/scheduler/supervisor.ts index dac125d..72b3cc2 100644 --- a/src/scheduler/supervisor.ts +++ b/src/scheduler/supervisor.ts @@ -106,7 +106,7 @@ export class Supervisor { throw new Error("Failed to spawn Core process: missing stdio handles"); } - if ((process.env.ROBOPPI_IPC_TRACE ?? process.env.AGENTCORE_IPC_TRACE) === "1") { + if (process.env.ROBOPPI_IPC_TRACE === "1") { try { process.stderr.write( `[IPC][spawn] impl=node_child_process transport=stdio core pid=${proc.pid} cmd=${core.command} args=${core.args.join(" ") || "(none)"} entry=${this.config.coreEntryPoint}\n`, @@ -207,7 +207,7 @@ export class Supervisor { await this.cleanupSocketArtifacts(); - const socketDir = await mkdtemp(path.join(os.tmpdir(), "agentcore-ipc-")); + const socketDir = await mkdtemp(path.join(os.tmpdir(), "roboppi-ipc-")); const socketPath = path.join(socketDir, "core.sock"); this.ipcSocketDir = socketDir; this.ipcSocketPath = socketPath; @@ -236,7 +236,7 @@ export class Supervisor { } catch (err) { // Some sandboxed environments disallow Unix domain sockets entirely (listen EPERM/EACCES). if (isUnixSocketListenUnsupported(err)) { - if ((process.env.ROBOPPI_IPC_TRACE ?? process.env.AGENTCORE_IPC_TRACE) === "1") { + if (process.env.ROBOPPI_IPC_TRACE === "1") { try { const code = getErrCode(err) ?? "unknown"; process.stderr.write( @@ -253,12 +253,12 @@ export class Supervisor { } // Ensure Core selects the Unix socket path mode. - delete childEnv.AGENTCORE_IPC_SOCKET_HOST; - delete childEnv.AGENTCORE_IPC_SOCKET_PORT; delete childEnv.ROBOPPI_IPC_SOCKET_HOST; delete childEnv.ROBOPPI_IPC_SOCKET_PORT; + delete childEnv.ROBOPPI_IPC_SOCKET_HOST; + delete childEnv.ROBOPPI_IPC_SOCKET_PORT; + childEnv.ROBOPPI_IPC_SOCKET_PATH = socketPath; childEnv.ROBOPPI_IPC_SOCKET_PATH = socketPath; - childEnv.AGENTCORE_IPC_SOCKET_PATH = socketPath; const core = this.resolveCoreCommand(); const proc = nodeSpawn(core.command, core.args, { @@ -270,7 +270,7 @@ export class Supervisor { throw new Error("Failed to spawn Core process: missing stdio handles"); } - if ((process.env.ROBOPPI_IPC_TRACE ?? process.env.AGENTCORE_IPC_TRACE) === "1") { + if (process.env.ROBOPPI_IPC_TRACE === "1") { try { process.stderr.write( `[IPC][spawn] impl=node_child_process transport=socket core pid=${proc.pid} cmd=${core.command} args=${core.args.join(" ") || "(none)"} entry=${this.config.coreEntryPoint} socket=${socketPath}\n`, @@ -384,12 +384,12 @@ export class Supervisor { } // Ensure Core selects the TCP mode. - delete childEnv.AGENTCORE_IPC_SOCKET_PATH; delete childEnv.ROBOPPI_IPC_SOCKET_PATH; + delete childEnv.ROBOPPI_IPC_SOCKET_PATH; + childEnv.ROBOPPI_IPC_SOCKET_HOST = addr.address || host; + childEnv.ROBOPPI_IPC_SOCKET_PORT = String(addr.port); childEnv.ROBOPPI_IPC_SOCKET_HOST = addr.address || host; childEnv.ROBOPPI_IPC_SOCKET_PORT = String(addr.port); - childEnv.AGENTCORE_IPC_SOCKET_HOST = addr.address || host; - childEnv.AGENTCORE_IPC_SOCKET_PORT = String(addr.port); const core = this.resolveCoreCommand(); const proc = nodeSpawn(core.command, core.args, { @@ -401,7 +401,7 @@ export class Supervisor { throw new Error("Failed to spawn Core process: missing stdio handles"); } - if ((process.env.ROBOPPI_IPC_TRACE ?? process.env.AGENTCORE_IPC_TRACE) === "1") { + if (process.env.ROBOPPI_IPC_TRACE === "1") { try { process.stderr.write( `[IPC][spawn] impl=node_child_process transport=tcp core pid=${proc.pid} cmd=${core.command} args=${core.args.join(" ") || "(none)"} entry=${this.config.coreEntryPoint} addr=${addr.address}:${addr.port}\n`, @@ -665,7 +665,6 @@ function resolveSupervisedIpcTransport(config: SupervisorConfig): "stdio" | "soc const raw = ( config.ipcTransport ?? process.env.ROBOPPI_SUPERVISED_IPC_TRANSPORT ?? - process.env.AGENTCORE_SUPERVISED_IPC_TRANSPORT ?? "stdio" ).toLowerCase(); diff --git a/src/worker/adapters/base-process-adapter.ts b/src/worker/adapters/base-process-adapter.ts index cd3f65a..fc59738 100644 --- a/src/worker/adapters/base-process-adapter.ts +++ b/src/worker/adapters/base-process-adapter.ts @@ -84,7 +84,7 @@ export abstract class BaseProcessAdapter implements WorkerAdapter { processGroup: true, }); - if (process.env.AGENTCORE_VERBOSE === "1" && process.env.AGENTCORE_COMPONENT === "core") { + if (process.env.ROBOPPI_VERBOSE === "1" && process.env.ROBOPPI_COMPONENT === "core") { const rendered = renderCommandForLogs(command, task); process.stderr.write( `[core][worker:${this.kind}] spawn pid=${managed.pid} cwd=${task.workspaceRef} cmd=${rendered}\n`, @@ -269,7 +269,7 @@ function toStderrObservation(stderr: string): Observation | null { } function renderCommandForLogs(cmd: string[], task: WorkerTask): string { - const showPrompts = process.env.AGENTCORE_SHOW_WORKER_PROMPTS === "1"; + const showPrompts = process.env.ROBOPPI_SHOW_WORKER_PROMPTS === "1"; const instr = task.instructions; const renderedArgs = cmd.map((arg, i) => { diff --git a/src/worker/process-manager.ts b/src/worker/process-manager.ts index d0932b3..a0222bd 100644 --- a/src/worker/process-manager.ts +++ b/src/worker/process-manager.ts @@ -182,7 +182,7 @@ function resolveSetSidPath(): string | null { if (process.platform === "win32") return null; // Prefer fixed locations to avoid PATH scans that may be slow/hang on some systems. - const override = process.env.AGENTCORE_SETSID_PATH; + const override = process.env.ROBOPPI_SETSID_PATH; const candidates = [ override, "/usr/bin/setsid", @@ -199,7 +199,7 @@ function resolveSetSidPath(): string | null { } // Optional fallback to Bun.which when explicitly enabled. - if (process.env.AGENTCORE_SETSID_USE_WHICH === "1") { + if (process.env.ROBOPPI_SETSID_USE_WHICH === "1") { try { return Bun.which("setsid"); } catch { diff --git a/src/workflow/branch-context.ts b/src/workflow/branch-context.ts index 693f92f..b1f98bd 100644 --- a/src/workflow/branch-context.ts +++ b/src/workflow/branch-context.ts @@ -164,7 +164,7 @@ export async function resolveBranchRuntimeContext( throw new Error( `blocked: expected_work_branch "${expectedWorkBranch}" is protected ` + `(protected_branches=${protectedResolved.protectedBranches.join(",")}). ` + - `Use --allow-protected-branch or AGENTCORE_ALLOW_PROTECTED_BRANCH=1 to override.`, + `Use --allow-protected-branch or ROBOPPI_ALLOW_PROTECTED_BRANCH=1 to override.`, ); } diff --git a/src/workflow/completion-decision.ts b/src/workflow/completion-decision.ts index ef12a30..66546f3 100644 --- a/src/workflow/completion-decision.ts +++ b/src/workflow/completion-decision.ts @@ -12,7 +12,7 @@ export function extractWorkerText(result: WorkerResult): string { } export type CompletionDecision = "complete" | "incomplete" | "fail"; -export type CompletionDecisionSource = "file-json" | "file-legacy" | "marker" | "none"; +export type CompletionDecisionSource = "file-json" | "none"; export interface CompletionDecisionResolution { decision: CompletionDecision; @@ -26,20 +26,11 @@ export interface CompletionDecisionResolution { } // Completion-check correlation id. -// -// Legacy name is kept for compatibility; the runner also sets ROBOPPI_COMPLETION_CHECK_ID. -export const COMPLETION_CHECK_ID_ENV = "AGENTCORE_COMPLETION_CHECK_ID"; -export const COMPLETION_CHECK_ID_ENV_ROBOPPI = "ROBOPPI_COMPLETION_CHECK_ID"; +export const COMPLETION_CHECK_ID_ENV = "ROBOPPI_COMPLETION_CHECK_ID"; const STALE_DECISION_FILE_GRACE_MS = 2_000; -const RE_COMPLETE = /\bCOMPLETE\b/i; -const RE_INCOMPLETE = /\bINCOMPLETE\b/i; -const RE_FAIL = /\bFAIL(?:ED)?\b/i; - type StructuredDecisionFile = { decision?: unknown; - status?: unknown; - result?: unknown; check_id?: unknown; reasons?: unknown; fingerprints?: unknown; @@ -51,71 +42,31 @@ const MAX_STRUCTURED_ITEM_LEN = 240; function parseDecisionValue(value: unknown): CompletionDecision | null { if (typeof value !== "string") return null; const normalized = value.trim().toLowerCase(); - if (normalized === "complete" || normalized === "pass") return "complete"; - if (normalized === "incomplete" || normalized === "fail" || normalized === "failed") return "incomplete"; + if (normalized === "complete") return "complete"; + if (normalized === "incomplete") return "incomplete"; return null; } -export function parseCompletionDecision(text: string): CompletionDecision { - const lines = text.split("\n").map((l) => l.trim()).filter(Boolean); - if (lines.length === 0) return "fail"; - - // Prefer explicit markers anywhere in the output. - // Use word-boundary checks to avoid false positives like "turn.completed". - for (let i = lines.length - 1; i >= 0; i--) { - const line = lines[i]!; - if (RE_INCOMPLETE.test(line)) return "incomplete"; - if (RE_COMPLETE.test(line)) return "complete"; - if (RE_FAIL.test(line)) return "incomplete"; - } - - return "fail"; -} - -export function parseCompletionDecisionFromFile(content: string): CompletionDecision { - const v = content.trim().toUpperCase(); - if (v === "COMPLETE" || v === "PASS") return "complete"; - if (v === "INCOMPLETE" || v === "FAIL") return "incomplete"; - return "fail"; -} - export async function resolveCompletionDecision( check: CompletionCheckDef, workspaceDir: string, checkStartedAt: number, - result: WorkerResult, checkId?: string, ): Promise { - const text = extractWorkerText(result); - - let fileFailureReason: string | undefined; - if (check.decision_file) { - const fileDecision = await tryDecisionFromFile( - check.decision_file, - workspaceDir, - checkStartedAt, - checkId, - ); - if (fileDecision.decision === "complete" || fileDecision.decision === "incomplete") { - return fileDecision; - } - fileFailureReason = fileDecision.reason; - } - - const markerDecision = parseCompletionDecision(text); - if (markerDecision !== "fail") { + if (!check.decision_file) { return { - decision: markerDecision, - source: "marker", + decision: "fail", + source: "none", + reason: "completion_check.decision_file is required", }; } - return { - decision: "fail", - source: "none", - reason: fileFailureReason - ?? "could not parse completion decision (expected COMPLETE/INCOMPLETE marker)", - }; + return await tryDecisionFromFile( + check.decision_file, + workspaceDir, + checkStartedAt, + checkId, + ); } async function tryDecisionFromFile( @@ -176,27 +127,10 @@ async function tryDecisionFromFile( } } - const legacy = parseCompletionDecisionFromFile(content); - if (legacy !== "fail") { - if (checkId && st.mtimeMs + STALE_DECISION_FILE_GRACE_MS < checkStartedAt) { - return { - decision: "fail", - source: "file-legacy", - reason: "decision_file is stale", - checkIdMatch: false, - }; - } - - return { - decision: legacy, - source: "file-legacy", - }; - } - return { decision: "fail", source: "file-json", - reason: "decision_file has unsupported format", + reason: "decision_file must be a JSON object with {decision, check_id, reasons?, fingerprints?}", }; } @@ -225,9 +159,7 @@ function parseStructuredDecisionFromFile(content: string): { } const d = parsed as StructuredDecisionFile; - const value = parseDecisionValue( - d.decision ?? d.status ?? d.result, - ); + const value = parseDecisionValue(d.decision); if (value === null) return null; const checkId = typeof d.check_id === "string" && d.check_id.trim().length > 0 diff --git a/src/workflow/core-ipc-step-runner.ts b/src/workflow/core-ipc-step-runner.ts index 9925b98..56c5fbf 100644 --- a/src/workflow/core-ipc-step-runner.ts +++ b/src/workflow/core-ipc-step-runner.ts @@ -4,7 +4,6 @@ import { extractWorkerText, resolveCompletionDecision, COMPLETION_CHECK_ID_ENV, - COMPLETION_CHECK_ID_ENV_ROBOPPI, } from "./completion-decision.js"; import { resolveTaskLike, type ResolvedWorkerTaskDef } from "./resolve-worker-task.js"; @@ -131,7 +130,6 @@ export class CoreIpcStepRunner implements StepRunner { ? { ...(env ?? {}), [COMPLETION_CHECK_ID_ENV]: checkId, - [COMPLETION_CHECK_ID_ENV_ROBOPPI]: checkId, } : env; const task = resolveTaskLike(check, workspaceDir, checkEnv); @@ -169,7 +167,6 @@ export class CoreIpcStepRunner implements StepRunner { check, workspaceDir, checkStartedAt, - result, checkId, ); @@ -200,7 +197,7 @@ export class CoreIpcStepRunner implements StepRunner { complete: false, failed: false, errorClass: ErrorClass.RETRYABLE_TRANSIENT, - reason: decision.reason ?? "could not parse completion decision (expected COMPLETE/INCOMPLETE marker)", + reason: decision.reason ?? "could not parse completion decision (expected decision_file JSON)", decisionSource: decision.source, decisionCheckIdMatch: decision.checkIdMatch, ...(decision.reasons ? { reasons: decision.reasons } : {}), diff --git a/src/workflow/executor.ts b/src/workflow/executor.ts index 53f10d0..462848e 100644 --- a/src/workflow/executor.ts +++ b/src/workflow/executor.ts @@ -1020,7 +1020,6 @@ export class WorkflowExecutor { const ignoredPatterns = [ "context/**", - ".agentcore-loop/**", ".roboppi-loop/**", ".git/**", ...(cfg.ignored_paths ?? []), diff --git a/src/workflow/multi-worker-step-runner.ts b/src/workflow/multi-worker-step-runner.ts index 48b6f15..ddfb945 100644 --- a/src/workflow/multi-worker-step-runner.ts +++ b/src/workflow/multi-worker-step-runner.ts @@ -14,7 +14,6 @@ import { extractWorkerText, resolveCompletionDecision, COMPLETION_CHECK_ID_ENV, - COMPLETION_CHECK_ID_ENV_ROBOPPI, } from "./completion-decision.js"; import { resolveTaskLike, buildWorkerTask } from "./resolve-worker-task.js"; @@ -94,7 +93,6 @@ export class MultiWorkerStepRunner implements StepRunner { ? { ...(env ?? {}), [COMPLETION_CHECK_ID_ENV]: checkId, - [COMPLETION_CHECK_ID_ENV_ROBOPPI]: checkId, } : env; const resolved = resolveTaskLike(check, workspaceDir, checkEnv); @@ -115,7 +113,6 @@ export class MultiWorkerStepRunner implements StepRunner { check, workspaceDir, checkStartedAt, - result, checkId, ); if (this.verbose) { @@ -304,7 +301,7 @@ function createScopedAbort(parent: AbortSignal, deadlineAt: number): { signal: A } function renderCommandForLogs(cmd: string[], task: WorkerTask): string { - const showPrompts = process.env.AGENTCORE_SHOW_WORKER_PROMPTS === "1"; + const showPrompts = process.env.ROBOPPI_SHOW_WORKER_PROMPTS === "1"; const instr = task.instructions; const renderedArgs = cmd.map((arg, i) => { diff --git a/src/workflow/parser.ts b/src/workflow/parser.ts index 7e17a28..11aed5c 100644 --- a/src/workflow/parser.ts +++ b/src/workflow/parser.ts @@ -140,7 +140,7 @@ function resolveTaskLikeWithAgent( const agentId = normalizeAgentId(obj["agent"], `${fieldPrefix}.agent`); if (!agents) { throw new WorkflowParseError( - `${fieldPrefix}.agent is set to "${agentId}", but no agent catalog was provided (use --agents or set AGENTCORE_AGENTS_FILE)`, + `${fieldPrefix}.agent is set to "${agentId}", but no agent catalog was provided (use --agents or set ROBOPPI_AGENTS_FILE)`, ); } @@ -204,7 +204,13 @@ function validateCompletionCheck(check: unknown, stepId: string, agents?: AgentC validateWorker(obj["worker"], `steps.${stepId}.completion_check.worker`); validateCapabilities(obj["capabilities"], `steps.${stepId}.completion_check.capabilities`); validateOptionalString(obj["model"], `steps.${stepId}.completion_check.model`); + + // For non-shell completion checks, a machine-readable decision file is required. + // CUSTOM checks use exit-code semantics and do not need decision_file. validateOptionalString(obj["decision_file"], `steps.${stepId}.completion_check.decision_file`); + if (obj["worker"] !== "CUSTOM") { + assertString(obj["decision_file"], `steps.${stepId}.completion_check.decision_file`); + } return obj as unknown as CompletionCheckDef; } diff --git a/src/workflow/run.ts b/src/workflow/run.ts index 18b0071..32ba0a0 100644 --- a/src/workflow/run.ts +++ b/src/workflow/run.ts @@ -20,7 +20,6 @@ import { MultiWorkerStepRunner } from "./multi-worker-step-runner.js"; import { CoreIpcStepRunner } from "./core-ipc-step-runner.js"; import { WorkflowStatus, StepStatus } from "./types.js"; import { parseDuration } from "./duration.js"; -import { applyEnvPrefixAliases } from "../core/env-aliases.js"; import { resolveBranchRuntimeContext, type BranchRuntimeContext, @@ -35,7 +34,7 @@ function resolveCoreEntryPointForSupervised(coreEntryPointOverride: string | und const fromCli = coreEntryPointOverride?.trim(); if (fromCli) return fromCli; - const fromEnv = (process.env.ROBOPPI_CORE_ENTRYPOINT ?? process.env.AGENTCORE_CORE_ENTRYPOINT)?.trim(); + const fromEnv = process.env.ROBOPPI_CORE_ENTRYPOINT?.trim(); if (fromEnv) return fromEnv; // Compiled binary: spawn this executable as the Core process. @@ -55,8 +54,8 @@ function resolveIpcRequestTimeoutMs(supervisedMode: boolean, ipcRequestTimeout: if (!supervisedMode) return 0; const fromCli = ipcRequestTimeout; - const fromEnv = process.env.AGENTCORE_IPC_REQUEST_TIMEOUT; - const fromEnvMs = process.env.AGENTCORE_IPC_REQUEST_TIMEOUT_MS; + const fromEnv = process.env.ROBOPPI_IPC_REQUEST_TIMEOUT; + const fromEnvMs = process.env.ROBOPPI_IPC_REQUEST_TIMEOUT_MS; if (fromCli !== undefined) { return parseDurationOrThrow(fromCli, "IPC request timeout"); @@ -97,7 +96,7 @@ async function loadAgentCatalogForWorkflow( verboseMode: boolean, agentsFiles: string[], ): Promise { - const fromEnv = splitPathList(process.env.AGENTCORE_AGENTS_FILE); + const fromEnv = splitPathList(process.env.ROBOPPI_AGENTS_FILE); const fromCli = agentsFiles; // If any explicit path is provided, only load those (env first, then CLI overrides). @@ -155,7 +154,7 @@ function parseEnvBool(value: string | undefined): boolean | undefined { function resolveKeepaliveEnabled(keepalive: boolean | undefined): boolean { if (keepalive !== undefined) return keepalive; - const env = parseEnvBool(process.env.AGENTCORE_KEEPALIVE); + const env = parseEnvBool(process.env.ROBOPPI_KEEPALIVE); if (env !== undefined) return env; return isNonInteractive(); } @@ -163,7 +162,7 @@ function resolveKeepaliveEnabled(keepalive: boolean | undefined): boolean { function resolveKeepaliveIntervalMs(keepaliveInterval: string | undefined): number { const raw = keepaliveInterval ?? - process.env.AGENTCORE_KEEPALIVE_INTERVAL ?? + process.env.ROBOPPI_KEEPALIVE_INTERVAL ?? "10s"; try { return parseDuration(raw); @@ -226,8 +225,6 @@ function printBranchContext(context: BranchRuntimeContext): void { } export async function runWorkflowCli(argv: string[]): Promise { - applyEnvPrefixAliases(); - const args = argv; let yamlPath = ""; let workspaceDir = ""; @@ -301,7 +298,7 @@ Options: --base-branch Explicit base branch (overrides BASE_BRANCH env) --protected-branches Protected work branches (default: main,master,release/*) --allow-protected-branch Allow execution on protected branch (dangerous) - --agents Agent catalog YAML (repeatable; merged with AGENTCORE_AGENTS_FILE; CLI wins on conflicts) + --agents Agent catalog YAML (repeatable; merged with ROBOPPI_AGENTS_FILE; CLI wins on conflicts) --core Core entrypoint for supervised mode (default: auto) --help, -h Show help`); process.exit(0); @@ -311,15 +308,15 @@ Options: } if (verbose) { - process.env.AGENTCORE_VERBOSE = "1"; + process.env.ROBOPPI_VERBOSE = "1"; } // Keepalive flags should propagate to any supervised child processes (Core). if (keepalive !== undefined) { - process.env.AGENTCORE_KEEPALIVE = keepalive ? "1" : "0"; + process.env.ROBOPPI_KEEPALIVE = keepalive ? "1" : "0"; } if (keepaliveInterval !== undefined && keepaliveInterval !== "") { - process.env.AGENTCORE_KEEPALIVE_INTERVAL = keepaliveInterval; + process.env.ROBOPPI_KEEPALIVE_INTERVAL = keepaliveInterval; } if (!yamlPath) { @@ -354,7 +351,7 @@ Options: // 3. Setup workspace const ws = workspaceDir ? path.resolve(workspaceDir) - : await mkdtemp(path.join(tmpdir(), "agentcore-wf-")); + : await mkdtemp(path.join(tmpdir(), "roboppi-wf-")); // Context directory can be overridden by workflow YAML (context_dir). // Resolve relative paths from the workspace root. @@ -368,9 +365,9 @@ Options: cliBaseBranch, envBaseBranch: process.env.BASE_BRANCH, cliProtectedBranches, - envProtectedBranches: process.env.AGENTCORE_PROTECTED_BRANCHES, + envProtectedBranches: process.env.ROBOPPI_PROTECTED_BRANCHES, cliAllowProtectedBranch, - envAllowProtectedBranch: process.env.AGENTCORE_ALLOW_PROTECTED_BRANCH, + envAllowProtectedBranch: process.env.ROBOPPI_ALLOW_PROTECTED_BRANCH, createBranch: definition.create_branch ?? false, expectedWorkBranch: definition.expected_work_branch, branchTransitionStep: definition.branch_transition_step, @@ -383,9 +380,9 @@ Options: // // Some non-interactive runners exhibit broken stdio pipes between parent/child // processes. Use the socket transport by default in non-interactive mode. - // (Override via AGENTCORE_SUPERVISED_IPC_TRANSPORT=stdio|socket|tcp.) - if (supervised && process.env.AGENTCORE_SUPERVISED_IPC_TRANSPORT === undefined) { - process.env.AGENTCORE_SUPERVISED_IPC_TRANSPORT = isNonInteractive() ? "socket" : "stdio"; + // (Override via ROBOPPI_SUPERVISED_IPC_TRANSPORT=stdio|socket|tcp.) + if (supervised && process.env.ROBOPPI_SUPERVISED_IPC_TRANSPORT === undefined) { + process.env.ROBOPPI_SUPERVISED_IPC_TRANSPORT = isNonInteractive() ? "socket" : "stdio"; } // 4. Execute @@ -424,38 +421,38 @@ Options: const ctx = new ContextManager(contextDir); const workflowEnv: Record = {}; if (verbose) { - workflowEnv.AGENTCORE_VERBOSE = "1"; + workflowEnv.ROBOPPI_VERBOSE = "1"; } - workflowEnv.AGENTCORE_CREATE_BRANCH = branchContext.createBranch ? "1" : "0"; - workflowEnv.AGENTCORE_PROTECTED_BRANCHES = branchContext.protectedBranches.join(","); - workflowEnv.AGENTCORE_ALLOW_PROTECTED_BRANCH = branchContext.allowProtectedBranch + workflowEnv.ROBOPPI_CREATE_BRANCH = branchContext.createBranch ? "1" : "0"; + workflowEnv.ROBOPPI_PROTECTED_BRANCHES = branchContext.protectedBranches.join(","); + workflowEnv.ROBOPPI_ALLOW_PROTECTED_BRANCH = branchContext.allowProtectedBranch ? "1" : "0"; if (branchContext.effectiveBaseBranch) { workflowEnv.BASE_BRANCH = branchContext.effectiveBaseBranch; - workflowEnv.AGENTCORE_EFFECTIVE_BASE_BRANCH = branchContext.effectiveBaseBranch; + workflowEnv.ROBOPPI_EFFECTIVE_BASE_BRANCH = branchContext.effectiveBaseBranch; } if (branchContext.effectiveBaseBranchSource) { - workflowEnv.AGENTCORE_EFFECTIVE_BASE_BRANCH_SOURCE = + workflowEnv.ROBOPPI_EFFECTIVE_BASE_BRANCH_SOURCE = branchContext.effectiveBaseBranchSource; } if (branchContext.effectiveBaseSha) { - workflowEnv.AGENTCORE_EFFECTIVE_BASE_SHA = branchContext.effectiveBaseSha; + workflowEnv.ROBOPPI_EFFECTIVE_BASE_SHA = branchContext.effectiveBaseSha; } if (branchContext.startupBranch) { - workflowEnv.AGENTCORE_STARTUP_BRANCH = branchContext.startupBranch; + workflowEnv.ROBOPPI_STARTUP_BRANCH = branchContext.startupBranch; } if (branchContext.startupHeadSha) { - workflowEnv.AGENTCORE_STARTUP_HEAD_SHA = branchContext.startupHeadSha; + workflowEnv.ROBOPPI_STARTUP_HEAD_SHA = branchContext.startupHeadSha; } if (branchContext.startupToplevel) { - workflowEnv.AGENTCORE_STARTUP_TOPLEVEL = branchContext.startupToplevel; + workflowEnv.ROBOPPI_STARTUP_TOPLEVEL = branchContext.startupToplevel; } if (branchContext.expectedWorkBranch) { - workflowEnv.AGENTCORE_EXPECTED_WORK_BRANCH = branchContext.expectedWorkBranch; + workflowEnv.ROBOPPI_EXPECTED_WORK_BRANCH = branchContext.expectedWorkBranch; } if (branchContext.expectedCurrentBranch) { - workflowEnv.AGENTCORE_EXPECTED_CURRENT_BRANCH = branchContext.expectedCurrentBranch; + workflowEnv.ROBOPPI_EXPECTED_CURRENT_BRANCH = branchContext.expectedCurrentBranch; } const executorEnv = Object.keys(workflowEnv).length > 0 ? workflowEnv : undefined; diff --git a/test/integration/workflow-execution.test.ts b/test/integration/workflow-execution.test.ts index 677f3b0..23b5165 100644 --- a/test/integration/workflow-execution.test.ts +++ b/test/integration/workflow-execution.test.ts @@ -301,6 +301,7 @@ steps: on_failure: retry completion_check: worker: CLAUDE_CODE + decision_file: ".roboppi-loop/decision.json" instructions: "Check if all todos are done" capabilities: [READ] timeout: "2m" diff --git a/test/unit/daemon/security.test.ts b/test/unit/daemon/security.test.ts index 052fc60..9254555 100644 --- a/test/unit/daemon/security.test.ts +++ b/test/unit/daemon/security.test.ts @@ -64,7 +64,7 @@ describe("Shell injection prevention", () => { const result = await gate.shouldRun( { worker: "CUSTOM", - instructions: 'test "$AGENTCORE_TRIGGER_ID" = "my-trigger"', + instructions: 'test "$ROBOPPI_TRIGGER_ID" = "my-trigger"', capabilities: ["RUN_COMMANDS"], }, makeEvent("test"), diff --git a/test/unit/workflow/completion-decision.test.ts b/test/unit/workflow/completion-decision.test.ts index 052ee09..124c579 100644 --- a/test/unit/workflow/completion-decision.test.ts +++ b/test/unit/workflow/completion-decision.test.ts @@ -1,58 +1,78 @@ -import { describe, test, expect } from "bun:test"; +import { describe, test, expect, afterEach } from "bun:test"; +import { mkdtemp, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import path from "node:path"; -import { - parseCompletionDecision, - parseCompletionDecisionFromFile, -} from "../../../src/workflow/completion-decision.js"; +import { resolveCompletionDecision } from "../../../src/workflow/completion-decision.js"; +import type { CompletionCheckDef } from "../../../src/workflow/types.js"; -describe("parseCompletionDecision", () => { - test("returns fail on empty", () => { - expect(parseCompletionDecision("")).toBe("fail"); - expect(parseCompletionDecision("\n\n")).toBe("fail"); - }); +let lastTmpDir: string | null = null; - test("detects COMPLETE marker", () => { - expect(parseCompletionDecision("COMPLETE\n")).toBe("complete"); - expect(parseCompletionDecision("some text\nCOMPLETE\nmore\n")).toBe("complete"); - }); +afterEach(async () => { + if (!lastTmpDir) return; + await rm(lastTmpDir, { recursive: true, force: true }); + lastTmpDir = null; +}); - test("detects INCOMPLETE marker", () => { - expect(parseCompletionDecision("INCOMPLETE\n")).toBe("incomplete"); - expect(parseCompletionDecision("foo\nINCOMPLETE\nbar\n")).toBe("incomplete"); - }); +function makeCheck(decisionFile: string): CompletionCheckDef { + return { + worker: "OPENCODE", + model: "openai/gpt-5.2", + instructions: "Write a decision_file", + capabilities: ["READ"], + decision_file: decisionFile, + } as unknown as CompletionCheckDef; +} - test("does not confuse completed with COMPLETE", () => { - expect(parseCompletionDecision('{"type":"turn.completed"}\n')).toBe("fail"); - expect(parseCompletionDecision('{"type":"item.completed"}\n')).toBe("fail"); - }); +describe("resolveCompletionDecision (decision_file JSON)", () => { + test("fails when decision_file is missing", async () => { + const dir = await mkdtemp(path.join(tmpdir(), "roboppi-decision-")); + lastTmpDir = dir; - test("detects markers inside JSON output", () => { - const text = [ - '{"type":"item.completed","item":{"type":"agent_message","text":"INCOMPLETE"}}', - '{"type":"turn.completed"}', - ].join("\n"); - expect(parseCompletionDecision(text)).toBe("incomplete"); - - const text2 = [ - '{"type":"item.completed","item":{"type":"agent_message","text":"COMPLETE"}}', - '{"type":"turn.completed"}', - ].join("\n"); - expect(parseCompletionDecision(text2)).toBe("complete"); + const check = makeCheck("decision.json"); + const res = await resolveCompletionDecision(check, dir, Date.now(), "check-1"); + expect(res.decision).toBe("fail"); + expect(res.source).toBe("none"); + expect(res.reason).toBe("decision_file missing"); }); -}); -describe("parseCompletionDecisionFromFile", () => { - test("maps PASS/FAIL", () => { - expect(parseCompletionDecisionFromFile("PASS\n")).toBe("complete"); - expect(parseCompletionDecisionFromFile("FAIL\n")).toBe("incomplete"); - }); + test("reads decision=complete with matching check_id", async () => { + const dir = await mkdtemp(path.join(tmpdir(), "roboppi-decision-")); + lastTmpDir = dir; + + const check = makeCheck("decision.json"); + const checkId = "check-123"; + const startedAt = Date.now(); + await writeFile( + path.join(dir, "decision.json"), + JSON.stringify({ decision: "complete", check_id: checkId, reasons: ["ok"], fingerprints: ["t:1"] }), + "utf-8", + ); - test("maps COMPLETE/INCOMPLETE", () => { - expect(parseCompletionDecisionFromFile("COMPLETE\n")).toBe("complete"); - expect(parseCompletionDecisionFromFile("INCOMPLETE\n")).toBe("incomplete"); + const res = await resolveCompletionDecision(check, dir, startedAt, checkId); + expect(res.decision).toBe("complete"); + expect(res.source).toBe("file-json"); + expect(res.checkIdMatch).toBe(true); + expect(res.reasons).toEqual(["ok"]); + expect(res.fingerprints).toEqual(["t:1"]); }); - test("returns fail on unknown", () => { - expect(parseCompletionDecisionFromFile("maybe")).toBe("fail"); + test("fails when check_id mismatches", async () => { + const dir = await mkdtemp(path.join(tmpdir(), "roboppi-decision-")); + lastTmpDir = dir; + + const check = makeCheck("decision.json"); + const startedAt = Date.now(); + await writeFile( + path.join(dir, "decision.json"), + JSON.stringify({ decision: "complete", check_id: "wrong" }), + "utf-8", + ); + + const res = await resolveCompletionDecision(check, dir, startedAt, "expected"); + expect(res.decision).toBe("fail"); + expect(res.source).toBe("file-json"); + expect(res.checkIdMatch).toBe(false); + expect(res.reason).toContain("check_id mismatch"); }); }); diff --git a/test/unit/workflow/parser.test.ts b/test/unit/workflow/parser.test.ts index 8cdea5f..cb45760 100644 --- a/test/unit/workflow/parser.test.ts +++ b/test/unit/workflow/parser.test.ts @@ -392,6 +392,7 @@ steps: capabilities: [READ] completion_check: worker: CLAUDE_CODE + decision_file: ".roboppi-loop/decision.json" instructions: "check" capabilities: [READ] `; @@ -410,6 +411,7 @@ steps: capabilities: [READ] completion_check: worker: CLAUDE_CODE + decision_file: ".roboppi-loop/decision.json" instructions: "check" capabilities: [READ] max_iterations: 1 @@ -444,6 +446,7 @@ steps: capabilities: [READ] completion_check: worker: CLAUDE_CODE + decision_file: ".roboppi-loop/decision.json" instructions: "check" capabilities: [READ] max_iterations: 5 diff --git a/tests/at/cli-subcommands.test.ts b/tests/at/cli-subcommands.test.ts index 2c52b77..3afd663 100644 --- a/tests/at/cli-subcommands.test.ts +++ b/tests/at/cli-subcommands.test.ts @@ -33,15 +33,11 @@ function createCleanEnv(extra?: Record): NodeJS.Proc // Keep tests deterministic and avoid accidental dependency on dev shells. for (const key of [ - "AGENTCORE_AGENTS_FILE", "ROBOPPI_AGENTS_FILE", - "AGENTCORE_CORE_ENTRYPOINT", "ROBOPPI_CORE_ENTRYPOINT", - "AGENTCORE_SUPERVISED_IPC_TRANSPORT", "ROBOPPI_SUPERVISED_IPC_TRANSPORT", - "AGENTCORE_KEEPALIVE", - "AGENTCORE_KEEPALIVE_INTERVAL", - "AGENTCORE_IPC_TRACE", + "ROBOPPI_KEEPALIVE", + "ROBOPPI_KEEPALIVE_INTERVAL", "ROBOPPI_IPC_TRACE", ]) { delete env[key]; diff --git a/tests/at/workflow-pipeline.test.ts b/tests/at/workflow-pipeline.test.ts index cb4b3a2..56595fa 100644 --- a/tests/at/workflow-pipeline.test.ts +++ b/tests/at/workflow-pipeline.test.ts @@ -206,6 +206,7 @@ steps: on_failure: retry completion_check: worker: CLAUDE_CODE + decision_file: ".roboppi-loop/decision.json" instructions: | Check todo.md. If any - [ ] remains, mark as incomplete. @@ -388,6 +389,7 @@ steps: capabilities: [READ] completion_check: worker: CLAUDE_CODE + decision_file: ".roboppi-loop/decision.json" instructions: "check" capabilities: [READ] `; @@ -406,6 +408,7 @@ steps: capabilities: [READ] completion_check: worker: CLAUDE_CODE + decision_file: ".roboppi-loop/decision.json" instructions: "check" capabilities: [READ] max_iterations: 1 diff --git a/tests/at/workflow-timeout.test.ts b/tests/at/workflow-timeout.test.ts index 37e349b..ed0f320 100644 --- a/tests/at/workflow-timeout.test.ts +++ b/tests/at/workflow-timeout.test.ts @@ -130,6 +130,7 @@ steps: on_iterations_exhausted: abort completion_check: worker: CODEX_CLI + decision_file: ".roboppi-loop/decision.json" instructions: "check A" capabilities: [READ] `; diff --git a/tests/branch/run-branch-verification.sh b/tests/branch/run-branch-verification.sh index 5c9ec58..90d2ece 100755 --- a/tests/branch/run-branch-verification.sh +++ b/tests/branch/run-branch-verification.sh @@ -18,7 +18,7 @@ if command -v rg >/dev/null 2>&1; then HAS_RG=1 fi -VERIFY_DIR=${1:-"$(mktemp -d /tmp/agentcore-branch-verify-XXXXXX)"} +VERIFY_DIR=${1:-"$(mktemp -d /tmp/roboppi-branch-verify-XXXXXX)"} mkdir -p "${VERIFY_DIR}" LOG_DIR="${VERIFY_DIR}/logs" mkdir -p "${LOG_DIR}" diff --git a/tests/branch/test-results.md b/tests/branch/test-results.md index 3fbb216..9e35560 100644 --- a/tests/branch/test-results.md +++ b/tests/branch/test-results.md @@ -9,9 +9,9 @@ - 実行コマンド: - `bash tests/branch/run-branch-verification.sh` - 検証ディレクトリ: - - `/tmp/agentcore-branch-verify-rT3LQh` +- `/tmp/roboppi-branch-verify-rT3LQh` - ログディレクトリ: - - `/tmp/agentcore-branch-verify-rT3LQh/logs` +- `/tmp/roboppi-branch-verify-rT3LQh/logs` ## 2. 結果サマリ diff --git a/tests/cli-test-plan.md b/tests/cli-test-plan.md index 2b96fd3..9342015 100644 --- a/tests/cli-test-plan.md +++ b/tests/cli-test-plan.md @@ -36,7 +36,7 @@ - サブコマンド分岐: `roboppi workflow`/`roboppi daemon`/`roboppi run`/`roboppi agent` - `agent` は `run` のエイリアスとして同等のエラー/終了コードになる - Core IPC: stdout(または socket)には JSON Lines のみ、ログは stderr -- supervised transport: `AGENTCORE_SUPERVISED_IPC_TRANSPORT` / `ROBOPPI_SUPERVISED_IPC_TRANSPORT` の解決 +- supervised transport: `ROBOPPI_SUPERVISED_IPC_TRANSPORT` の解決 - compiled 時の Core 起動: Core entrypoint が `process.execPath`(=同一バイナリ)でも動作する - OOM 対策: `BUN_FLAGS=--smol`(ビルド/テスト時)、`BUN_BUILD_FLAGS=--compile-exec-argv=--smol`(compiled の子 Core)