open-tui-orchestrator 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +205 -0
- package/INSTALL-zh.md +96 -0
- package/INSTALL.md +96 -0
- package/LICENSE +48 -0
- package/README-zh.md +181 -0
- package/README.md +181 -0
- package/cli.mjs +37 -0
- package/docs/adapt.md +103 -0
- package/docs/assets/kimicode-agent-swarm-10-subagents.png +0 -0
- package/docs/auto-recovery.md +23 -0
- package/docs/caller-driven.md +121 -0
- package/docs/claude-adapter.md +25 -0
- package/docs/execution-contract.md +70 -0
- package/docs/inactive-windows.md +11 -0
- package/docs/kimi-adapter.md +27 -0
- package/docs/kimi-integration.md +56 -0
- package/docs/maintenance-lock.md +32 -0
- package/docs/openclaw-adapter.md +59 -0
- package/docs/openclaw-assessment-2026-09-06.md +59 -0
- package/docs/opencode-adapter.md +25 -0
- package/docs/pi-adapter.md +58 -0
- package/docs/public-readiness.md +63 -0
- package/docs/release-policy.md +39 -0
- package/docs/security-audit-2026-09-09.md +41 -0
- package/docs/trust-and-safety.md +64 -0
- package/docs/verification-2026-09-06.md +22 -0
- package/docs/verification-recovery-2026-09-06.md +36 -0
- package/orch.mjs +20 -0
- package/package.json +36 -0
- package/release.json +116 -0
- package/repair.mjs +228 -0
- package/scripts/adapt.mjs +35 -0
- package/scripts/agent-auth-prompt.txt +10 -0
- package/scripts/agent.mjs +1 -0
- package/scripts/core/adapt-lib.mjs +219 -0
- package/scripts/core/agent-auth-prompt.txt +10 -0
- package/scripts/core/agent-profiles/hermes.json +59 -0
- package/scripts/core/agent.mjs +1 -0
- package/scripts/core/checkpoint.mjs +38 -0
- package/scripts/core/claude-host.mjs +50 -0
- package/scripts/core/claude-runtime.mjs +111 -0
- package/scripts/core/contracts.mjs +161 -0
- package/scripts/core/host-cli.mjs +204 -0
- package/scripts/core/host-model.mjs +323 -0
- package/scripts/core/host-probe.mjs +16 -0
- package/scripts/core/inactive-window.mjs +32 -0
- package/scripts/core/inactive-window.ps1 +36 -0
- package/scripts/core/kimi-host.mjs +41 -0
- package/scripts/core/kimi-runtime.mjs +140 -0
- package/scripts/core/lease-lock.ps1 +32 -0
- package/scripts/core/leases.mjs +176 -0
- package/scripts/core/maintenance-lock.mjs +77 -0
- package/scripts/core/native-argv.mjs +9 -0
- package/scripts/core/network-policy.mjs +18 -0
- package/scripts/core/openclaw-bootstrap.mjs +25 -0
- package/scripts/core/openclaw-config.mjs +35 -0
- package/scripts/core/openclaw-host.mjs +29 -0
- package/scripts/core/openclaw-runtime.mjs +33 -0
- package/scripts/core/openclaw-window.mjs +44 -0
- package/scripts/core/opencode-host.mjs +80 -0
- package/scripts/core/opencode-runtime.mjs +131 -0
- package/scripts/core/orchestrate-sdk.mjs +2595 -0
- package/scripts/core/pi-host.mjs +29 -0
- package/scripts/core/pi-runtime.mjs +54 -0
- package/scripts/core/pi-shutdown.mjs +16 -0
- package/scripts/core/poll-windows.mjs +48 -0
- package/scripts/core/print-profile.mjs +79 -0
- package/scripts/core/print-runtime.mjs +106 -0
- package/scripts/core/pty-host.mjs +38 -0
- package/scripts/core/recovery.mjs +75 -0
- package/scripts/core/run-board.mjs +155 -0
- package/scripts/core/run-guardian.mjs +130 -0
- package/scripts/core/runner.mjs +274 -0
- package/scripts/core/runtime-context.mjs +23 -0
- package/scripts/core/unit-carrier.mjs +55 -0
- package/scripts/core/unit-command.mjs +96 -0
- package/scripts/core/unit-runtime.mjs +107 -0
- package/scripts/gate.mjs +162 -0
- package/scripts/host-cli.mjs +2 -0
- package/scripts/install-deps.mjs +58 -0
- package/scripts/maintenance-lock.mjs +46 -0
- package/scripts/network-policy.mjs +2 -0
- package/scripts/open-tui-orchestrator-force.mjs +239 -0
- package/scripts/open-tui-orchestrator-preflight.mjs +85 -0
- package/scripts/orchestrate-sdk.mjs +59 -0
- package/scripts/package-lock.json +242 -0
- package/scripts/package.json +9 -0
- package/scripts/platform-guard.mjs +23 -0
- package/scripts/poll-windows.mjs +8 -0
- package/scripts/release-integrity.mjs +94 -0
- package/scripts/runtime-context.mjs +2 -0
- package/scripts/sdk-dependency-check.mjs +32 -0
- package/scripts/todo-list.mjs +89 -0
package/README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# open-tui-orchestrator
|
|
2
|
+
|
|
3
|
+
A local Node.js orchestrator for Agents (tools that receive tasks and act through a CLI, or command-line interface).
|
|
4
|
+
|
|
5
|
+
**Windows-only today.** The launchers, the invisible ConPTY carrier and the machine-wide coordinator are Windows-specific; other platforms get a diagnostic instead of a half-working install. Before you run it, read [docs/trust-and-safety.md](docs/trust-and-safety.md) — it states plainly what this tool does on your machine (hidden agents running with your credentials, bypassed approvals for Codex-hosted runs, the exact files it writes). Security reports: [SECURITY.md](SECURITY.md). Contributing: [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
open-tui-orchestrator is designed for complex, long-running requests with several parts that can be advanced independently. It first creates a complete TODO LIST, an authoritative task ledger and a dependency graph. Semantic work stays with the planner: the model may classify ownership, resources and dependencies, while deterministic code validates task coverage, detects conflicts, groups work and schedules dependency waves.
|
|
10
|
+
|
|
11
|
+
When the plan contains genuinely parallel work, the orchestrator spawns one execution unit per block and runs those blocks at the same time. Units default to headless: the same launcher runs under a hidden carrier, so nothing pops up and focus is never stolen — pass `--mode window` when you want the visible TUI (terminal user interface) windows. Resource keys, dependency edges and capacity leases (shared records that limit the number of active units) coordinate the units. Task start, completion, result verification and unit closure all leave durable evidence, and a live run board (`[board]` stdout lines, run `events.jsonl`, read-only `--watch <runId>`) mirrors block and task state in real time; the run is complete only after every task is independently verified.
|
|
12
|
+
|
|
13
|
+
The orchestrator is for complex work, not for wrapping every small command in a window. A single task, a single-block plan or a plan whose dependencies force serial execution stays inline: the CLI returns the complete original task inventory and the reason, and the initiating Agent executes it in the current conversation. A visible run starts only when at least two blocks are ready in the same dependency wave, so skipping a window never means dropping a task.
|
|
14
|
+
|
|
15
|
+
Window capacity is computed from local memory first: convert the reported memory GiB into the manufacturer's nominal memory GB, then take three quarters of that for the local maximum window count.
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
nominal memory GB = round(memory GiB × 1024 ÷ 1000)
|
|
19
|
+
maximum windows = round(nominal memory GB × 3 ÷ 4)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`round` converts the result to an integer. For example, a nominal 16 GB machine yields 12 windows, an 8 GB machine yields 6, and a 4 GB machine yields 3. A cross-run, cross-workspace coordinator then confirms the number of active windows; both active windows and reservations that are still starting consume capacity. When there are more tasks than the capacity, extra tasks are merged into existing blocks and run serially inside those blocks. They are not moved to a later capacity batch and they are never removed from the original ledger.
|
|
23
|
+
|
|
24
|
+
For example, a nominal 16 GB machine gives a capacity of 12. Nine independent tasks become nine one-task blocks. At most nine windows open at once, while all nine tasks remain in the ledger and must pass verification.
|
|
25
|
+
|
|
26
|
+
Windows Terminal is the default host. Windows inherit the initiating conversation's working directory and start visibly without deliberately taking focus, so the current window is less likely to be interrupted. Launchers, results, locks, decomposition state and recovery ledgers live under `temp/orchestrator` in that workspace. The host Agent keeps its own configuration, sessions and credentials in its normal location. PowerShell is used only for Windows lifecycle, encoding and process-tree helpers; Agent adapters and visible hosting prefer Node.js and Windows Terminal.
|
|
27
|
+
|
|
28
|
+
Codex, pi, opencode, Kimi Code, Claude Code and a version-pinned OpenClaw adapter (a translation layer for each host CLI's arguments, output and shutdown behavior) are connected. The host is selected with `--agent`/`ORCH_AGENT`, host environment markers, or auto-detection of the initiating agent from the Windows ancestor process chain — whichever agent starts a run drives its own CLI in the windows, and the windows inherit the initiating window's current model and thinking effort (explicit `ORCH_MODEL`/`ORCH_EFFORT` win; otherwise detected per host — Codex rollout, Kimi Code session wire log, pi settings, opencode and Claude settings, and for print-class profiled agents the profile's `modelDetection` spec — with the CLI's own configuration as the last fallback). An unknown Agent, an unvalidated CLI or a missing executable produces a diagnostic; the tool does not install software or silently fall back to another Agent. An already-authorized execution context starts work as soon as its TUI window launches. The orchestrator carries the caller's existing permissions and does not widen them.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```powershell
|
|
33
|
+
npm install -g open-tui-orchestrator
|
|
34
|
+
open-tui-orchestrator --install-deps # fetch this package's locked SDK dependencies (network, once)
|
|
35
|
+
open-tui-orchestrator --agent codex --doctor # verify: hostError null, carrierAvailable true
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Requirements: Windows 10/11 (x64), Node.js 22+, Windows Terminal (recommended) and one agent CLI you already use and are logged into — Codex, pi, opencode, Kimi Code, Claude Code, or the version-pinned OpenClaw. The tool never installs or substitutes an agent CLI. Tarball install, source install, upgrade/rollback, uninstall and troubleshooting: **[INSTALL.md](INSTALL.md)** (中文: [INSTALL-zh.md](INSTALL-zh.md)). Public-readiness status and open gaps: [docs/public-readiness.md](docs/public-readiness.md).
|
|
39
|
+
|
|
40
|
+
## When to use it
|
|
41
|
+
|
|
42
|
+
Use it for a request that touches several projects or directories, contains independent fixes, or needs prerequisites followed by a long verification and cleanup phase. The orchestrator preserves the full task inventory before it analyzes ownership, resource conflicts and dependencies. Read-only analysis, design discussion and changes to the orchestrator itself stay in the initiating conversation.
|
|
43
|
+
|
|
44
|
+
For a small action, a single operation, a purely serial chain or a read-only request, the current Agent is the better execution surface. `--spawn` is the explicit entry point for one visible window; it does not make ordinary single-task requests open a window.
|
|
45
|
+
|
|
46
|
+
## Quick start
|
|
47
|
+
|
|
48
|
+
Run commands from the initiating conversation's workspace; do not change into the installation directory. The examples assume `cli.mjs` is in the current command path; replace it with the absolute installation path when needed:
|
|
49
|
+
|
|
50
|
+
```powershell
|
|
51
|
+
node cli.mjs --agent codex --doctor
|
|
52
|
+
node cli.mjs --agent pi --doctor
|
|
53
|
+
node cli.mjs --agent claude --doctor
|
|
54
|
+
node cli.mjs --agent openclaw --doctor
|
|
55
|
+
node cli.mjs --agent codex --workspace 'D:\your-project' --plan 'Fix project 81 login page and optimize project 82 cart'
|
|
56
|
+
node cli.mjs --agent codex --workspace 'D:\your-project' --run-windows 'Fix project 81 login page and optimize project 82 cart'
|
|
57
|
+
node cli.mjs --tasks 'Fix project 81 login page; optimize project 82 cart'
|
|
58
|
+
node cli.mjs --plan-file plan.json --unit-cmd-file unit.json --run-windows 'Fix project 81 login page; optimize project 82 cart'
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`--plan` only plans; a fresh `--run-windows` opens windows only when a parallel wave exists; `--resume-run <id>` adopts a saved run after interruption; `--verify-run <id>` rechecks a run without executing tasks; `--status --readonly` reports windows, leases and active-run board rows; `--watch <id> [--jsonl]` streams the run board read-only; and the `orch` command is the force-execute entry — it runs `--run-windows` with every inline fallback bypassed (single block, serial plan, read-only classification, self-modification), so the orchestrator is invoked 100% of the time except when the maintenance lock is held. The fixed pattern for mirroring the board stream into Kimi Code's native TodoList / background-task mechanism lives in `docs/kimi-integration.md`; unadapted print-class agent CLIs are onboarded with `node scripts/adapt.mjs` (or `ORCH_AUTO_ADAPT=1` automatic mode) — probe, profile, live verify, reseal, auto-commit — see `docs/adapt.md`. Any agent can also call the orchestrator with no adapter at all: `--plan-file` takes the caller's own ledger/blocks (zero model calls in the whole run) and `--unit-cmd`/`--unit-cmd-file` supplies the unit command, bypassing host resolution, probing and the adapter list; `--tasks "<request>"` prints the authoritative `T001…` ledger to align ids first — see `docs/caller-driven.md`. Install the existing dependency with `npm run setup` and run the offline regression with `npm test`.
|
|
62
|
+
|
|
63
|
+
## Why this exists
|
|
64
|
+
|
|
65
|
+
There are two roots for this project.
|
|
66
|
+
|
|
67
|
+
The first was the desire to let Kimi K3 use its full capability. I installed KimiCode to use Kimi K3, and during a very large code-review task Kimi K3 told KimiCode to launch a swarm (a group of parallel agents) and dispatch 10 coder subagents (coding sub-agents). That experience made the throughput benefit of parallel execution on large tasks very clear.
|
|
68
|
+
|
|
69
|
+

|
|
70
|
+
|
|
71
|
+
*Figure: KimiCode, driven by Kimi K3, dispatching 10 coder subagents at once. This is a reference image for the motivation behind parallel windows; it is not a screenshot of open-tui-orchestrator.*
|
|
72
|
+
|
|
73
|
+
The second was watching Claude Fable 5.1 begin work by writing a TODO LIST. For a complex, long-running task that can easily miss a step, recording goals, dependencies, actions, verification and cleanup before execution makes the process more reliable.
|
|
74
|
+
|
|
75
|
+
Not every Agent can freely launch many subagents. A general substitute is to call the CLI already installed for that Agent and open several TUI windows in parallel. Each window runs one independent block, which is close in effect to launching several subagents without requiring the host Agent to provide its own sub-agent scheduler. The TODO LIST is enforced by the skill and execution contract: the controller keeps the complete original inventory, and each window lists, checkpoints and updates only its assigned block.
|
|
76
|
+
|
|
77
|
+
These two capabilities are overkill for a small task, so the orchestrator is not meant to open windows for every request. Its purpose is stable TODO-driven execution for complex work: safe parallel parts run in at least two TUI windows, with a maximum of three quarters of the device's nominal memory GB (nominal GB = round(memory GiB × 1024 ÷ 1000)). A one-task request, a one-block plan and a dependency-only serial plan stay in the initiating conversation. If the task count exceeds the window limit, the excess is merged into an existing block and executed serially; the final ledger must still cover every original task.
|
|
78
|
+
|
|
79
|
+
## Implementation
|
|
80
|
+
|
|
81
|
+
### 1. Resolve the workspace and capacity
|
|
82
|
+
|
|
83
|
+
The orchestrator resolves the workspace from the caller's current directory or `--workspace`, then creates `temp/orchestrator` there as the home for run state. It reads local memory, converts it to the nominal GB (`round(memory GiB × 1024 ÷ 1000)`) and computes the capacity as `round(nominal GB × 3 ÷ 4)`. This capacity is shared across the user's runs and workspaces, and both active windows and startup reservations count toward it.
|
|
84
|
+
|
|
85
|
+
### 2. Create a lossless task ledger
|
|
86
|
+
|
|
87
|
+
The controller assigns stable IDs such as `T001` and `T002` at the request boundary, preserves each original instruction and treats that list as the authoritative TODO LIST. The planner may add project ownership, resource hints, dependencies and feasibility notes. It may not delete a task because it is marked completed or infeasible, and a summary cannot replace the original wording. Missing, duplicated, malformed or unassignable model output causes the system to retain the complete original list and use a safe serial plan.
|
|
88
|
+
|
|
89
|
+
### 3. Separate semantic classification from deterministic grouping
|
|
90
|
+
|
|
91
|
+
The model classifies a flat inventory: what each task is, which project owns it, which resources it touches and which tasks it depends on. Code performs the actual grouping:
|
|
92
|
+
|
|
93
|
+
- Tasks for the same project or resource directory share a block to avoid overwriting one another.
|
|
94
|
+
- Same-project tasks are then split fine-grained: when the classifier gives precise resource keywords (conflict_hints) for every task, they are clustered by resource-key overlap — disjoint tasks become separate blocks that open parallel windows (carrying only their precise resource keys, not the shared project directory key); only when any task's resources are unclear or all are entangled does the group stay one serial block (project directory key as the fallback).
|
|
95
|
+
- Tasks with no known conflict or dependency remain independent blocks.
|
|
96
|
+
- Unknown ownership goes to a conservative serial block instead of an unsafe parallel wave.
|
|
97
|
+
- Unknown, self-referential and cyclic dependencies are rejected before launch, then valid edges are mapped to block dependencies.
|
|
98
|
+
- A block starts when its own prerequisites are verified; unrelated blocks do not wait for one another.
|
|
99
|
+
|
|
100
|
+
### 4. Apply the capacity ceiling without creating a hidden queue
|
|
101
|
+
|
|
102
|
+
After deterministic planning, the scheduler compacts the blocks to the capacity. Compaction merges tasks into existing blocks and marks a merged block `linked` (strictly serial inside the block). It never creates a ninth window and never silently defers the rest to another capacity batch. The set of original task IDs is checked before and after compaction for count, uniqueness and traceability.
|
|
103
|
+
|
|
104
|
+
### 5. Gate windows by a real parallel wave
|
|
105
|
+
|
|
106
|
+
The plan is converted into dependency waves and passed through an explicit window policy:
|
|
107
|
+
|
|
108
|
+
- One block returns `mode: inline` and creates no run window.
|
|
109
|
+
- Multiple blocks with only one ready block in every wave return `mode: inline`; a serial dependency chain does not get a decorative window.
|
|
110
|
+
- A wave with at least two ready blocks returns `mode: windows` and starts at least two visible TUI windows. The active count still cannot exceed the memory-derived window capacity (nominal GB × 3 ÷ 4).
|
|
111
|
+
|
|
112
|
+
An inline response contains the original request, complete task ledger, block titles, dependencies and the next-step hint. Inline execution therefore keeps the work visible to the initiating Agent instead of turning “no window” into “no task.”
|
|
113
|
+
|
|
114
|
+
### 6. Launch unfocused windows in Windows Terminal
|
|
115
|
+
|
|
116
|
+
Each block gets a unique run ID, attempt number and token (the unique completion identity for that window). Windows Terminal hosts the visible interface. The detached launcher inherits the initiating workspace and authorized host parameters without deliberately bringing the new window to the foreground. If Windows Terminal is unavailable, the tool uses a traditional console host; it never substitutes another Agent. Closing uses the verified launcher identity and its process tree, without terminating a shared Windows Terminal host that may carry other windows.
|
|
117
|
+
|
|
118
|
+
### 7. Enforce a TODO discipline inside every window
|
|
119
|
+
|
|
120
|
+
After launch, a window checks its environment and prints the complete TODO LIST for its block, including original task IDs, goals, dependencies, actions, verification and cleanup. It writes a `started` checkpoint before each task and a `completed` checkpoint after local verification. Failures preserve evidence and are not converted into success by a verbal report. A window cannot invoke the orchestrator again, recursively open windows or absorb another block.
|
|
121
|
+
|
|
122
|
+
### 8. Require multiple forms of completion evidence
|
|
123
|
+
|
|
124
|
+
At the end, the window prints its token-specific `__ORCH_DONE__` marker, and a watchdog (a completion monitor that observes the window's final evidence) begins cleanup. The marker proves only that the window performed its closing step; it is not proof that the business result is correct. The controller also:
|
|
125
|
+
|
|
126
|
+
- reads every block's checkpoints and result files;
|
|
127
|
+
- checks real files, JSON or command output against the parent's read-only acceptance conditions;
|
|
128
|
+
- confirms a terminal state for every original task;
|
|
129
|
+
- confirms that window processes have closed and leases have been released; and
|
|
130
|
+
- marks the whole run successful only when every task is `verified`.
|
|
131
|
+
|
|
132
|
+
### 9. Recover interruptions without repeating uncertain side effects
|
|
133
|
+
|
|
134
|
+
Each attempt records remaining task IDs, workspace, token, PID (process identifier) and launcher files in the run ledger. Recovery first adopts a live window by PID, creation time, executable and command line, so it does not open a replacement while the original survives. Verified tasks are not repeated. A started task whose side effect cannot be proved becomes `needs-reconciliation` (the controller must check the real state) instead of being replayed blindly. Dependents of a failed prerequisite remain in the ledger with an explicit blocking reason.
|
|
135
|
+
|
|
136
|
+
### 10. Isolate host-specific behavior behind adapters
|
|
137
|
+
|
|
138
|
+
The generic entry point selects a host with `--agent` or `ORCH_AGENT`; absent both, it detects the initiating agent from host env markers (`CODEX_THREAD_ID`/`CODEX_SESSION_ID`) and then the Windows ancestor process chain (kimi.exe / codex.exe / claude.exe / pi-coding-agent / opencode / openclaw, nearest match wins). Codex, pi, opencode, Kimi Code, Claude Code and version-pinned OpenClaw each implement argument construction, session/result reading, completion detection and shutdown. The scheduler depends only on the shared contract. A new Agent must pass CLI discovery, planning, interactive launch, output parsing, completion and cleanup checks; adding only a command name is insufficient.
|
|
139
|
+
|
|
140
|
+
### 11. Keep weak-network behavior and authorization explicit
|
|
141
|
+
|
|
142
|
+
Codex planning and windows use the existing HTTPS Responses configuration instead of waiting for a WebSocket attempt before falling back. The host CLI owns retry and deadline behavior during a temporary outage, while the workspace ledger preserves the state. An authorized window starts immediately without another human confirmation. The orchestrator forwards the caller's permissions and never acts as a privilege-escalation layer.
|
|
143
|
+
|
|
144
|
+
### 12. Clean up and seal releases
|
|
145
|
+
|
|
146
|
+
After a run, the controller removes only launchers, logs, results and temporary files owned by that completed run, while retaining the run ledger and required acceptance evidence. Active windows and other workspaces are left untouched. `release.json` protects the execution core with a SHA-256 release seal (an integrity record for the exact release inventory); every core or runtime change requires a new seal and verification.
|
|
147
|
+
|
|
148
|
+
### 13. Maintenance lock: pause orchestration while maintaining itself
|
|
149
|
+
|
|
150
|
+
Before upgrading/optimizing/maintaining/updating the orchestrator itself, acquire the maintenance lock: `node scripts/maintenance-lock.mjs lock --reason "why" [--ttl-minutes N]`. While locked, every action entry point (`orch`, `--run-windows`/`--spawn`/`--resume-run`/`--run`/`--exec`/`--plan`, `open-tui-orchestrator-preflight.mjs`, `open-tui-orchestrator-force.mjs --assert/--verify`) returns exactly one line — `open-tui-orchestrator正在维护更新,本轮跳过orchestrator直接开始执行` — and exits 0, so the initiating conversation skips the orchestrator and executes directly for that round. Read-only entries (`--status`/`--doctor`/`--verify-run`/`--clear-cache`/`--tasks`) are unaffected, and `--status` also reports the lock state. The lock is global per user (default `~/.open-tui-orchestrator/maintenance-lock.json`, overridable via `ORCH_MAINTENANCE_LOCK`), expires automatically after 180 minutes by default (24h cap), and fails open when expired or corrupt so the orchestrator can never be wedged by a forgotten lock; `repair.mjs --clean`/`--target` self-lock for their duration when no lock is held. Release it with `node scripts/maintenance-lock.mjs unlock` when maintenance is done.
|
|
151
|
+
|
|
152
|
+
## Related documentation
|
|
153
|
+
|
|
154
|
+
- [Execution and acceptance contract](docs/execution-contract.md)
|
|
155
|
+
- [Release policy](docs/release-policy.md)
|
|
156
|
+
- [Pi adapter](docs/pi-adapter.md)
|
|
157
|
+
- [OpenClaw execution and isolation](docs/openclaw-adapter.md)
|
|
158
|
+
- [Kimi Code adapter](docs/kimi-adapter.md)
|
|
159
|
+
- [opencode adapter](docs/opencode-adapter.md)
|
|
160
|
+
- [Claude Code adapter](docs/claude-adapter.md)
|
|
161
|
+
- [Automatic adaptation of new agent CLIs (print-class profiles)](docs/adapt.md)
|
|
162
|
+
- [Caller-driven execution: plan file + unit command (any agent)](docs/caller-driven.md)
|
|
163
|
+
- [Kimi Code integration: board stream → native TodoList](docs/kimi-integration.md)
|
|
164
|
+
- [Unfocused window validation](docs/inactive-windows.md)
|
|
165
|
+
- [Automatic recovery after an abnormal interruption](docs/auto-recovery.md)
|
|
166
|
+
- [Maintenance lock](docs/maintenance-lock.md)
|
|
167
|
+
|
|
168
|
+
## Supported hosts and validation boundary
|
|
169
|
+
|
|
170
|
+
Codex CLI, pi CLI, opencode CLI, Kimi Code CLI, Claude Code CLI and OpenClaw 2026.9.2 CLI have adapters. Run diagnostics with:
|
|
171
|
+
|
|
172
|
+
```powershell
|
|
173
|
+
node cli.mjs --agent codex --doctor
|
|
174
|
+
node cli.mjs --agent pi --doctor
|
|
175
|
+
node cli.mjs --agent claude --doctor
|
|
176
|
+
node cli.mjs --agent openclaw --doctor
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The current generic core is 1.14.6, with 292/292 offline tests passing. Coverage includes headless spawn mode as the default (--mode window keeps the visible Windows Terminal popups; the launcher/identity/lease/recovery contract is shared, and the hidden carrier cannot steal focus; TUI-class agents run on a ConPTY pseudo-terminal carrier — a real console that stays fully invisible, while print-class CLIs keep the no-console carrier), a live run board (per-transition [board] stdout lines, run events.jsonl, read-only --watch <runId> streaming with --jsonl, and --status active-run rows), lossless task accounting, the memory-derived block-capacity ceiling, dependency waves, fine-grained same-project splitting by resource keys, global capacity leases with exponential + jittered wait backoff for queued blocks (ORCH_WAIT_BACKOFF_MS / ORCH_WAIT_BACKOFF_MAX_MS) and a tunable, retried coordinator-mutex wait (ORCH_MUTEX_WAIT_MS / ORCH_MUTEX_RETRIES) with age-based ticket reclamation (ORCH_MUTEX_TICKET_TTL_MS), Windows Terminal launch (synchronous and reliable, without stealing the initiating window's focus), unfocused-start regressions, process identity, checkpoint recovery, recovery-ledger hardening (launcher-files workspace containment, acceptance-contract hash binding, registry-lock stale reclaim), guardian-driven automatic resume after controller loss (test/guardian-live-result.json) plus manual --resume-run adoption of surviving windows (test/recovery-live-result.json), initiating-agent detection (env markers first, then the process chain: kimi/codex/claude/pi/opencode/openclaw/hermes), the `orch` force-execute entry, print-class data-driven profile adaptation (surface probe → profile → resolution self-check → live verification → reseal → auto-commit, rolled back on failure; profile `modelDetection` lets profiled agents follow the initiating window's model/effort; an adapter-less agent with a landed profile (hermes) is a first-class auto host — the initiating agent is detected and drives its own CLI as the executor with `--no-restore-cwd` guarding against stale-session CWD writes), the caller-driven channel (a caller-supplied plan file kept under the unchanged coverage/dependency/fallback guarantees with zero model calls in the run, caller-supplied unit commands driving any CLI in the same launcher/lease/recovery/acceptance contract, the read-only --tasks ledger helper, resume identity rules for unit commands, and the two-path diagnostic for unknown agents), model/effort traceability (summary records model/effort plus the source: explicit / host session / host-default; machine-readable stdout stays pure data), the machine-caller failure contract (every public entry fails closed with one `[orchestrator] FATAL: <reason>` line and no Node stack unless `ORCH_DEBUG=1`; an unknown run id never dumps `ENOENT` and never hangs — `--verify-run`/`--resume-run` refuse immediately with `Run not found: <id>`, and `--watch` honours a bounded `ORCH_WATCH_GRACE_MS` grace (default 60 s, covering the window in which `--run-windows` is still planning) before refusing, reporting a summary that disappears mid-watch instead of watching forever), the maintenance lock, a current-core security audit with fixes landed (docs/security-audit-2026-09-09.md), weak-network configuration, SDK diagnostics, host CLI probe timeout retry with per-candidate rejection diagnostics, OpenClaw version-gate diagnostics (the runtime tui interface probe runs first; unverified versions fail closed carrying the probe result and the last-verified version — the constant records "last verified", it is not an admission gate), recovery-ledger containment that covers drive-root workspaces, and clean package publication. Test-env requirement: the offline suite assumes the initiating agent CLI (default `codex`) is resolvable on PATH — on Windows prepend the codex vendor bin (the documented regression convention). Without it, agent-resolution-dependent test files fail fast by design (fail-closed); the two runtime-CLI-dependent cases are annotated `{skip}`. Real model quotas, installation under a different Windows user, prolonged offline recovery and field compatibility for future Agents also require separate acceptance.
|
|
180
|
+
|
|
181
|
+
An unknown host, missing CLI or invalid explicit path fails with a diagnostic. The tool does not install software, guess a replacement path or switch to another Agent.
|
package/cli.mjs
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { resolveRuntimeContext } from './scripts/runtime-context.mjs';
|
|
6
|
+
import { enforceWindowsOnly } from './scripts/platform-guard.mjs';
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
// 只读版本面:纯元数据,不解析 agent CLI、不落任何状态(发行副本验收的版本痕迹检查用)。
|
|
9
|
+
if (args.includes('--version')) {
|
|
10
|
+
const pkg = JSON.parse(fs.readFileSync(fileURLToPath(new URL('./package.json', import.meta.url)), 'utf8'));
|
|
11
|
+
const core = /CORE_VERSION\s*=\s*'([^']+)'/.exec(fs.readFileSync(fileURLToPath(new URL('./scripts/core/contracts.mjs', import.meta.url)), 'utf8'))?.[1] ?? 'unknown';
|
|
12
|
+
console.log(`open-tui-orchestrator ${pkg.version} (core ${core})`);
|
|
13
|
+
process.exit(0);
|
|
14
|
+
}
|
|
15
|
+
// Windows-only 承诺在入口强制(见 scripts/platform-guard.mjs):拒绝要发生在解析宿主、调用模型、落任何状态之前。
|
|
16
|
+
if (enforceWindowsOnly(args)) process.exit(1);
|
|
17
|
+
const env = { ...process.env };
|
|
18
|
+
for (const [flag, key] of [['--agent', 'ORCH_AGENT'], ['--cli', 'ORCH_CLI'], ['--workspace', 'WORKSPACE_DIR']]) {
|
|
19
|
+
const index = args.indexOf(flag);
|
|
20
|
+
if (index < 0) continue;
|
|
21
|
+
if (!args[index + 1] || args[index + 1].startsWith('--')) {
|
|
22
|
+
console.error(`${flag} needs a value`); process.exit(2);
|
|
23
|
+
}
|
|
24
|
+
env[key] = args[index + 1]; args.splice(index, 2);
|
|
25
|
+
}
|
|
26
|
+
const context = resolveRuntimeContext(env);
|
|
27
|
+
env.WORKSPACE_DIR = context.workspace;
|
|
28
|
+
env.ORCH_STATE_DIR = context.state;
|
|
29
|
+
if (!args.length || args.includes('--help')) {
|
|
30
|
+
console.log('open-tui-orchestrator --agent codex|pi|openclaw|opencode|kimi [--cli executable-or-supported-entry] [--workspace directory] [--acceptance file] [--mode headless|exec|window] --install-deps|--doctor|--plan request|--tasks request|--run-windows request|--resume-run id|--verify-run id|--spawn title prompt|--status|--watch id|--version\nInstall: npm i -g open-tui-orchestrator && open-tui-orchestrator --install-deps (fetches the locked SDK dependencies; see INSTALL.md).\nCaller-driven channel (any agent): --plan-file <plan.json> (caller-supplied ledger/blocks; zero LLM) and --unit-cmd/--unit-cmd-file <unit.json> (your own unit command template; no adapter needed). --tasks <request> prints the authoritative T001... ledger so any caller can align ids before writing a plan file. See docs/caller-driven.md.\nHost identity comes from --agent/ORCH_AGENT, then host env markers, then the initiating process chain; an explicit unit command overrides host resolution entirely. Fresh --run-windows opens TUI windows only for a genuinely parallel wave; single-block and dependency-only serial plans return an inline task inventory, and an explicit --plan-file skips the read-only classification model call. Spawn mode defaults to headless (hidden carrier); --mode window keeps the visible Windows Terminal popups. OpenClaw support is pinned to 2026.9.2. Missing or unsupported CLIs fail without fallback.');
|
|
31
|
+
} else {
|
|
32
|
+
const child = spawn(process.execPath, [fileURLToPath(new URL('./scripts/orchestrate-sdk.mjs', import.meta.url)), ...args], {
|
|
33
|
+
cwd: context.workspace, env, stdio: 'inherit', windowsHide: true,
|
|
34
|
+
});
|
|
35
|
+
child.on('error', error => { console.error(error.message); process.exitCode = 1; });
|
|
36
|
+
child.on('exit', code => { process.exitCode = code ?? 1; });
|
|
37
|
+
}
|
package/docs/adapt.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# 自动适配(print-class 画像管线)
|
|
2
|
+
|
|
3
|
+
open-tui-orchestrator 对"一次性提示词 → stdout 终文本 → 自行退出"形态的 agent CLI(如 kimi/claude/ aider)
|
|
4
|
+
提供数据驱动的通用适配:**一份 JSON 画像**即可接入,不必手写专用适配器。
|
|
5
|
+
|
|
6
|
+
## 命令
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
node scripts/adapt.mjs [--agent <id>] [--cli <path>] [--profiles-dir <dir>]
|
|
10
|
+
[--no-live] [--no-commit] [--suite] [--smoke] [--force] [--json]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
流程(任何一步失败都**回滚画像**,仓库保持原状):
|
|
14
|
+
|
|
15
|
+
1. **定位**:`--agent`(缺省用调用方探测结果)→ `--cli`/`ORCH_CLI` → PATH 上的 `<id>.exe`。
|
|
16
|
+
`.cmd`/shell shim 无法被直接 spawn(Windows 限制),会自动拒绝并提示指向真实可执行文件;
|
|
17
|
+
`.mjs`/`.cjs` 形态自动以 node + 脚本运行。
|
|
18
|
+
2. **表面探测**:`--version` 取语义化版本;`--help` 里找一次性提示词旗标(候选
|
|
19
|
+
`-p/--print/--message/-m`)、自动批准旗标(候选 `--yes-always/--dangerously-skip-permissions/
|
|
20
|
+
--yolo/--auto/--approve/--full-auto/--dangerously-bypass-approvals-and-sandbox`)、`--model`、`--effort`。
|
|
21
|
+
3. **生成画像**:`scripts/core/agent-profiles/<id>.json`(schema 见文件头注释;含 adaptedFrom 审计字段)。
|
|
22
|
+
4. **解析自检**:用画像走一遍 `resolveProfileHost`,版本必须与探测一致。
|
|
23
|
+
5. **活体验证(默认开)**:真实调用一次该 CLI,要求它只回一个随机探活令牌
|
|
24
|
+
(`ADAPTOK-xxxxxxxx`);未回、超时或非零退出即失败回滚。`--no-live` 关闭(不推荐)。
|
|
25
|
+
6. **块级真实验收(可选 `--smoke`)**:用画像 agent 走一遍真实的 `orch` 单块执行
|
|
26
|
+
(checkpoint 协议 + 验收契约 + 结果文件),把"能接上"验证到"跑得通"。
|
|
27
|
+
⚠️ 会消耗该 agent 的真实额度,且要求它已登录/有模型配置。
|
|
28
|
+
7. **重封 + 自动提交(默认开)**:`release-integrity --seal` 后,仅提交画像文件与 `release.json`
|
|
29
|
+
(显式路径,绝不用 `git add -A`)。`--no-commit` 关闭。
|
|
30
|
+
|
|
31
|
+
## 对新 agent 的完整度对照(拿"完整功能"逐项核对)
|
|
32
|
+
|
|
33
|
+
| 能力 | 画像 agent 是否自动具备 |
|
|
34
|
+
|---|---|
|
|
35
|
+
| 双模式派发(headless 默认 / `--mode window`) | ✅ 引擎层,与 agent 无关 |
|
|
36
|
+
| 执行看板(`[board]`/events.jsonl/`--watch`/`--status`) | ✅ 引擎层 |
|
|
37
|
+
| 全局租约/波次/容量/准入/错峰、恢复/守护/`--resume-run` | ✅ 引擎层(启动器契约同构) |
|
|
38
|
+
| `orch` 强制入口、维护锁、验收契约、checkpoint 协议 | ✅ 引擎层 |
|
|
39
|
+
| CLI 解析 / argv(批准/模型/提示词旗标)/ 启动器 / 等待 / 规划器 | ✅ 画像路径全量覆盖 |
|
|
40
|
+
| 宿主识别(谁调用我) | ✅ 进程名自动(`^<id>(\.exe)?$`);⚠️ env 标记自动探测为空,可手填 `envMarkers` |
|
|
41
|
+
| **model/effort 跟随发起窗** | ✅ 引擎按画像的 `modelDetection` 规格继承发起窗 model/effort:adapt 自动精确发现(env `<ID>_MODEL/_DEFAULT_MODEL/_EFFORT/_THINKING` 优先,其次 `~/.<id>.conf.{yml,yaml}`、`~/.<id>.json`、`~/.<id>/{settings,config}.json`、`~/.config/<id>/{config.json,config.jsonc,settings.json}`,键名限 `model/defaultModel/default_model` 与 `effort/defaultEffort/thinking/defaultThinkingLevel`);缺失即回退该 CLI 自身默认(或显式 `ORCH_MODEL/ORCH_EFFORT`),只读不猜值 |
|
|
42
|
+
| 活体验收深度 | 默认=令牌探活;`--smoke` = 块级真实执行验收 |
|
|
43
|
+
| 纯交互 TUI(无一次性提示词旗标) | ❌ 硬边界:明确拒绝,需手写专用适配器 |
|
|
44
|
+
|
|
45
|
+
结论:**print-class 的 agent 经自动适配后继承引擎的全部横切功能与适配器全链路**;
|
|
46
|
+
已知差异只剩非 print-class 的手写边界(model/effort 跟随已由 `modelDetection` 规格覆盖:
|
|
47
|
+
adapt 自动精确发现,引擎端 env 优先/配置回退/缺失回退 CLI 默认,绝不猜值)。
|
|
48
|
+
`ORCH_AUTO_ADAPT=1` 自动模式只做"令牌探活"级验证(不跑 `--smoke`,避免未经同意消耗额度)。
|
|
49
|
+
|
|
50
|
+
## 自动模式(环境检测即适配)
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
ORCH_AUTO_ADAPT=1 node <cli> --agent <未适配的 id> --run-windows "<请求>"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
薄壳 wrapper 在加载引擎**之前**检查:检测到未适配 agent 且仓库未上锁 → 直接跑完整适配管线
|
|
57
|
+
(表面+活验+重封+提交)→ 成功则继续本轮调用;失败则 **fail-closed** 报
|
|
58
|
+
`AUTO_ADAPT_FAILED`(不静默降级)。维护锁持有期间跳过适配,由引擎照常返回维护话术。
|
|
59
|
+
|
|
60
|
+
## 安全护栏
|
|
61
|
+
|
|
62
|
+
- **维护锁**:锁持有期间拒绝写入(`--force` 例外);适配自身的写入/重封/提交窗口内也会短暂持锁。
|
|
63
|
+
- **失败回滚**:画像写入后任何一步失败都会恢复原状(含旧画像的备份还原)。
|
|
64
|
+
- **可审计**:画像携带 `adaptedAt/adaptedFrom{cli,version,prefixArgs}`;提交信息标注
|
|
65
|
+
`[auto-generated]`。
|
|
66
|
+
- **不改核心**:适配产物只有画像 JSON 一笔;引擎代码零改动(除非该 CLI 不属于 print-class)。
|
|
67
|
+
|
|
68
|
+
## 适配成功的判定
|
|
69
|
+
|
|
70
|
+
表面探测 + 解析自检 + 活体验证三步全过即"适配成功"并自动提交。**块级真实执行的验收仍建议人工复测**
|
|
71
|
+
(用 `orch` 起一两轮真实任务确认),这也是设计上的分工:自动管线保证"能接上",人工复测保证"跑得好"。
|
|
72
|
+
|
|
73
|
+
## 实弹记录(2026-09-11)
|
|
74
|
+
|
|
75
|
+
- **假 CLI(离线全链)**:`test/print-profile.test.mjs` 八条全绿——探测生成、解析/argv、PowerShell
|
|
76
|
+
启动器回环(checkpoint 协议)、结果等待、`orch` 端到端 success、`--smoke` 块级真实验收(假 CLI)、
|
|
77
|
+
model/effort 跟随(`discoverModelDetection` 发现 + 引擎端 env 优先/配置回退/无规格回退 null)、内置 agent 跳过。
|
|
78
|
+
- **真 CLI:aider 0.86.2**(本机安装、未被适配过):
|
|
79
|
+
- 表面探测 ✓(`--version`=0.86.2;`--message`/`--yes-always`/`--model` 词边界匹配);
|
|
80
|
+
- 画像生成 ✓ + 解析自检 ✓;
|
|
81
|
+
- **活体验证两次实操抓到真问题**:①首轮把 `--auto-commits` 子串误配成 `--auto`,aider 报
|
|
82
|
+
`ambiguous option`——活验守门当场拒绝并回滚(零提交);修复为词边界匹配后 ②aider 因**未配置
|
|
83
|
+
登录**弹出 OpenRouter OAuth 等待人工授权——活验按时超时、回滚、零提交(符合"未授权 CLI
|
|
84
|
+
fail-closed"的边界设计)。该机无 aider 配置也无任何 API key 环境变量,登录完成后重跑
|
|
85
|
+
`node scripts/adapt.mjs --agent aider` 即可自动完成最后一跳(活验→重封→提交)。
|
|
86
|
+
|
|
87
|
+
## 边界(明确不支持)
|
|
88
|
+
|
|
89
|
+
- **纯交互 TUI 且无一次性提示词旗标的 CLI**:探测会明确报告 `not a print-class CLI`,需要手写专用适配器
|
|
90
|
+
(参考 kimi/claude 适配器的形态)。
|
|
91
|
+
- 需要**交互式登录/设备码授权**的 CLI:活体验证会因未授权失败(fail-closed),请先在该 CLI 里完成登录。
|
|
92
|
+
- 沙箱/审批语义:画像类 agent 只支持 `danger-full-access`(编排器已归一化授权),要求受限沙箱会被明确拒绝。
|
|
93
|
+
|
|
94
|
+
## 验证入口
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
node --test test/print-profile.test.mjs
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
覆盖:假 print-class CLI 的表面探测与画像生成、解析与 argv 形状、启动器 PowerShell 回环
|
|
101
|
+
(引号/中文/checkpoint 协议/结果文件)、结果等待、`orch` 端到端(画像 agent 强制派发 + 验收 verified)、
|
|
102
|
+
`--smoke` 块级真实验收、model/effort 探测规格发现与引擎继承(env/配置/缺失三路)、内置 agent 跳过。
|
|
103
|
+
活体记录见 `docs/` 提交历史与本文件更新。
|
|
Binary file
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Automatic recovery after an abnormal interruption
|
|
2
|
+
|
|
3
|
+
Every fresh `--run-windows` run arms a detached **run guardian** (`scripts/core/run-guardian.mjs`, wired through `runner.mjs` and the engine's `guardian` hook). The guardian is a tiny hidden Node process that outlives the controller and watches two things: the controller's process identity and the run ledger (`summary.json`).
|
|
4
|
+
|
|
5
|
+
When the controller process disappears while the ledger is still unfinalized — host CLI crashed, initiating terminal closed, Node killed — the guardian relaunches `node orchestrate-sdk.mjs --resume-run <runId>` on its own. The resumed controller passes the same ledger validation as a manual resume, adopts still-living windows by identity instead of duplicating them (`adopting live ...`), and drives the remaining tasks to a finalized summary. No user re-invocation is involved.
|
|
6
|
+
|
|
7
|
+
## Bounds (fail-closed)
|
|
8
|
+
|
|
9
|
+
- Restart budget: at most `ORCH_GUARDIAN_MAX_RESTARTS` resumes (default 3) with 5s/10s/20s backoff, persisted in `guardian.json` inside the run directory. Exhaustion stops respawning; the run stays manually resumable with `--resume-run`.
|
|
10
|
+
- The guardian never resumes past the run deadline (`startedAt + timeoutMs`), never respawns when the process snapshot is unavailable (a blind respawn risks a duplicate controller), and exits as soon as the ledger is finalized — success or failure.
|
|
11
|
+
- Run ownership is still serialized by `claimRun`: a crashed controller's claim is pruned by identity, so exactly one controller drives the run at any time, whether resumed by the guardian or by hand.
|
|
12
|
+
- Uncertain tasks keep the existing policy: only independently verified or retry-safe tasks are re-executed; anything else stops at `needs-reconciliation` for the initiating conversation to review.
|
|
13
|
+
- Resumed runs do not spawn a second guardian; the original guardian keeps watching every engine it started. If the guardian process itself dies, automatic recovery stops (manual `--resume-run` still works).
|
|
14
|
+
|
|
15
|
+
## Scope limits
|
|
16
|
+
|
|
17
|
+
- The guardian covers controller loss on a running machine. A full OS reboot kills the guardian too; after a reboot, resume manually with `--resume-run <runId>` — window adoption and task checkpoints still apply.
|
|
18
|
+
- `ORCH_GUARDIAN=0` opts out per environment; the offline test suite (`NODE_TEST_CONTEXT`) never starts a guardian.
|
|
19
|
+
- Per-launch telemetry lives in the run directory: `guardian.json` (state), `guardian.log` (guardian output), `resumed-engine-<n>.log` (output of each guardian-started controller).
|
|
20
|
+
|
|
21
|
+
## Verification
|
|
22
|
+
|
|
23
|
+
Offline coverage in `test/run-guardian.test.mjs` (11 cases: single auto-resume with run identity, restart-budget exhaustion, run-deadline refusal, blind-respawn refusal on snapshot failure, PID-reuse immunity, missing/corrupt state fail-closed, engine-side wiring for fresh runs only, guardian failure never failing the run). Live acceptance in `test/live-guardian.mjs` injects a real controller kill mid-run with a surviving window and asserts the guardian's resume adopts it, the first task executes exactly once, and the run finalizes successfully.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Caller-driven execution (any agent)
|
|
2
|
+
|
|
3
|
+
open-tui-orchestrator is a system tool: **any agent that can run a shell command can call it.** Nothing below needs a new per-agent adapter — the calling agent supplies the plan and the unit command, and the orchestrator keeps only the deterministic mechanics (validation, grouping, capacity, units/windows, leases, run board, guardian, recovery, acceptance).
|
|
4
|
+
|
|
5
|
+
## Two switches
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
--plan-file <plan.json> the caller's own ledger / block plan (zero model calls)
|
|
9
|
+
--unit-cmd '<json>' | --unit-cmd-file <unit.json> how one execution unit is launched
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
An explicit unit command overrides host resolution entirely: no process-chain detection, no `--help`/`--version` probe, no allow-list. `--agent <id>` stays optional and only labels the ledger — `--doctor` reports `unit: caller`, `probe: none`.
|
|
13
|
+
|
|
14
|
+
## 0. Align IDs first (read-only)
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
node cli.mjs --tasks "<original request>"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
prints the authoritative ledger (`T001`, `T002`, …) that a plan file must cover exactly. This entry needs no host identity at all.
|
|
21
|
+
|
|
22
|
+
## 1. Plan file
|
|
23
|
+
|
|
24
|
+
One shape per file — either `{"tasks":[…]}`, or `{"blocks":[…]}`.
|
|
25
|
+
|
|
26
|
+
Tasks-level (a flat classification; the deterministic engine still does all the grouping):
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{"schema":1,"tasks":[
|
|
30
|
+
{"id":"T001","project":"81","kind":"project",
|
|
31
|
+
"conflict_hints":["file:src/login.js"],"depends_on":[],"feasible":true,"already_done":false}]}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Block-level (same shape as `--plan` output):
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{"schema":1,"blocks":[
|
|
38
|
+
{"type":"independent","title":"proj-81","dependsOn":[],
|
|
39
|
+
"conflictKeys":["file:src/login.js"],
|
|
40
|
+
"tasks":[{"id":"t1","prompt":"仅处理项目 proj-81。任务:<original task text, verbatim>;"}]}]}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Hard rules (the existing core, unchanged):
|
|
44
|
+
|
|
45
|
+
- ids must match `--tasks` exactly — a missing, duplicated or invented id is refused;
|
|
46
|
+
- every block task prompt must contain its original task text **verbatim**, otherwise the whole plan collapses into one serial `complete-inventory` block (nothing is silently dropped);
|
|
47
|
+
- unknown, self-referential and cyclic dependencies are refused;
|
|
48
|
+
- capacity compaction and dependency waves apply exactly as with the model planner.
|
|
49
|
+
|
|
50
|
+
**Zero model calls:** with `--plan-file` the two model stages (planning, read-only classification) and the decompose cache are all skipped. `--plan --plan-file` is a pure dry run that prints the effective blocks.
|
|
51
|
+
|
|
52
|
+
## 2. Unit command
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{"schema":1,"id":"myagent","label":"My Agent","bin":"C:/path/agent.exe",
|
|
56
|
+
"args":["run","--prompt-file","{promptFile}"],
|
|
57
|
+
"promptDelivery":"file",
|
|
58
|
+
"modelArgs":["--model"],"effortArgs":["--effort"]}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- `{promptFile}` — absolute path of the prompt file (recommended); `{prompt}` — the prompt as a single argv element; `{workspace}` — the run workspace.
|
|
62
|
+
- Exactly one delivery: `file` (needs `{promptFile}`), `arg` (prompt appended as the last element), `stdin` (no prompt placeholder).
|
|
63
|
+
- `bin` must be a real executable — Windows cannot spawn `.cmd`/`.bat` directly.
|
|
64
|
+
- argv is built element-by-element (`shell:false`); the PowerShell launcher keeps quotes, Chinese and spaces intact.
|
|
65
|
+
- No probe, no version assertion: the caller vouches for the command.
|
|
66
|
+
- Completion uses the same contract as every host: the unit writes its checkpoints, prints `__ORCH_DONE__ <token>` as its last line, and the parent re-checks the real results with its own acceptance file. Output text is never parsed for success.
|
|
67
|
+
- `unitProfile` is stored in the run ledger; `--resume-run` reuses it, and re-supplying a different one is refused.
|
|
68
|
+
|
|
69
|
+
## 3. Run
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
node cli.mjs --plan-file plan.json --unit-cmd-file unit.json \
|
|
73
|
+
--run-id my-run --acceptance-file acceptance.json \
|
|
74
|
+
--run-windows "<the original request>"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The original request string is still required — it is the authoritative ledger the plan file has to cover. Default spawn mode is headless; `--mode window` keeps the visible Windows Terminal popups. Acceptance checks (`{T001:[{"type":"file"|"json"|"command"|"absent", …}]}`) are the only proof of success; missing checks leave tasks at `awaiting-verification`.
|
|
78
|
+
|
|
79
|
+
## 4. Any agent, in four lines
|
|
80
|
+
|
|
81
|
+
Paste into any agent's global instruction file (`AGENTS.md`, `CLAUDE.md`, …):
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
## system tool: open-tui-orchestrator (parallel TUI units)
|
|
85
|
+
Complex request with ≥2 independent workstreams? Run: orch "<request>" (or open-tui-orchestrator --run-windows "<request>")
|
|
86
|
+
No adapter for your CLI? Bring your own: --plan-file plan.json --unit-cmd-file unit.json (align ids with --tasks; see docs/caller-driven.md)
|
|
87
|
+
Watch: --status --readonly | --watch <runId> Result ledger: <workspace>/temp/orchestrator/runs/<runId>/summary.json
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 5. What the caller still owns
|
|
91
|
+
|
|
92
|
+
- the acceptance checks — a run is successful only when every task is independently verified;
|
|
93
|
+
- a plan that covers every task of the request;
|
|
94
|
+
- a unit command that can actually do the work (and is authorized in the caller's own context).
|
|
95
|
+
|
|
96
|
+
## Failure contract (machine callers)
|
|
97
|
+
|
|
98
|
+
Every public entry fails closed the same way — one line, no Node stack:
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
[orchestrator] FATAL: Run not found: <id> (no summary.json in <dir>)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
- data goes to stdout, diagnostics to stderr; a read-only entry's stdout stays pure data;
|
|
105
|
+
- exit codes: `0` success · `1` refused/failed (unknown run, tampered acceptance, missing host, verification failed) · `2` usage;
|
|
106
|
+
- full stacks are opt-in: `ORCH_DEBUG=1`;
|
|
107
|
+
- an unknown run never hangs and never dumps `ENOENT`: `--verify-run` / `--resume-run` refuse immediately, `--watch` allows a bounded grace first (`ORCH_WATCH_GRACE_MS`, default 60000 ms — covering the window in which `--run-windows` is still planning and has not created the run directory), then refuses; a run that loses its `summary.json` mid-watch is reported instead of being watched forever.
|
|
108
|
+
|
|
109
|
+
## Boundaries
|
|
110
|
+
|
|
111
|
+
- read-only entries (`--tasks`, `--status`, `--watch`, `--doctor`, `--verify-run`, `--clear-cache`) need no host identity;
|
|
112
|
+
- action entries still respect the maintenance lock, and the inline gate still returns single-block / serial plans to the initiating conversation unless `orch` / `--force-exec` is used deliberately;
|
|
113
|
+
- an explicit unit command is arbitrary code execution under the caller's existing authorization: the orchestrator forwards permissions and never widens them.
|
|
114
|
+
|
|
115
|
+
## Verification
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
node --test test/caller-driven.test.mjs
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
covers: the `--tasks` ledger, plan-file schemas and coverage fail-closed, the "no model is called" guarantee, unit-profile validation and argv expansion, a full caller-driven run (two parallel units, acceptance verified), resume identity rules, the fail-closed negatives, the two-path diagnostic for unknown agents, and the maintenance lock.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Claude Code 适配器
|
|
2
|
+
|
|
3
|
+
open-tui-orchestrator 支持已安装的 Claude Code CLI(claude.exe,2.x)。发起方为 Claude Code 会话时可省略 `--agent`:宿主检测会优先识别 `CLAUDECODE` / `CLAUDE_CODE_ENTRYPOINT` 环境标记,其次沿 Windows 祖先进程链匹配 claude.exe 或 `@anthropic-ai\claude-code` 命令行并自动绑定。
|
|
4
|
+
|
|
5
|
+
## 使用
|
|
6
|
+
|
|
7
|
+
```powershell
|
|
8
|
+
open-tui-orchestrator --agent claude --doctor
|
|
9
|
+
open-tui-orchestrator --run-id my-run --acceptance-file D:/work/temp/acceptance.json --run-windows '项目91:修改页面,运行测试;项目92:修改接口,检查结果'
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## CLI 与运行协议
|
|
13
|
+
|
|
14
|
+
- 解析 PATH 上的 claude.exe / claude.cmd / claude 启动入口:npm 风格 shim 会按内容解析到 `node_modules\@anthropic-ai\claude-code\bin\claude.exe`;`--help` 必须提供 `--print` / `--dangerously-skip-permissions` / `--model`,版本取 `--version`(`2.x.y (Claude Code)`)的语义化前缀。显式路径错误时不回退、不安装。
|
|
15
|
+
- 窗口采用 `claude -p`(print 模式):编排器已完成授权归一化,`--dangerously-skip-permissions` 跳过逐条权限询问;model/effort 继承发起窗(显式 ORCH_MODEL/ORCH_EFFORT 优先,否则读 `~/.claude/settings.json` 的 model/effort,缺失即不传,绝不猜测)。
|
|
16
|
+
- 提示词并入数组后再做 Windows PowerShell 5.1 原生命令行修正,嵌入引号、中文与多行内容不丢失;argv 形状为 `--dangerously-skip-permissions [--model X] [--effort Y] -p <提示词>`。
|
|
17
|
+
- 窗口把输出 tee 到日志,启动器按【本块唯一标识】token 行写结果文件(首行 JSON + `__EXIT__=N` 尾行);等待方只解析首行 JSON。claude `-p` 在回合结束后自行退出,无需扩展机制。stderr 经 ErrorRecord 还原管道以普通文本并入日志。
|
|
18
|
+
- 规划走 `claude -p` 的 stdout 终文本(240s 默认超时,`ORCH_DECOMPOSE_TIMEOUT_MS` 显式设置时优先)。不支持的沙箱要求明确拒绝(CLAUDE_SANDBOX_UNSUPPORTED)。
|
|
19
|
+
|
|
20
|
+
## 验证入口
|
|
21
|
+
|
|
22
|
+
- `node --test test/claude-adapter.test.mjs test/claude-runtime.test.mjs`:真实 CLI 解析、argv 形状(model 在 `-p` 前、提示词居末)、PowerShell 启动器引号/中文/多行回环、结果等待、规划超时默认值。
|
|
23
|
+
- 本机实测(2026-09-10,Claude Code 2.1.267,core 1.8.0):headless 双块实跑 `success=true`(收据逐键核对通过、零弹窗),window 模式实跑 `success=true`(可见窗 + popup CONFIRMED)。
|
|
24
|
+
|
|
25
|
+
真实调用消耗本机 Claude 配额;其余 agent 的兼容性以各自的验收记录为准。
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Shared execution contract (core 1.3.1, run protocol 2)
|
|
2
|
+
|
|
3
|
+
Generic development uses scripts/core. The dedicated Codex plugin now owns a separately verified physical release snapshot; changes are promoted explicitly, not through directory junctions. The generic entry implements Codex, pi and version-pinned OpenClaw adapters; the dedicated plugin accepts Codex only. See pi-adapter.md for Pi-specific CLI, planning, session and shutdown behavior.
|
|
4
|
+
|
|
5
|
+
## Planning and capacity
|
|
6
|
+
|
|
7
|
+
Detect memory first: nominal GB = round(memoryGiB * 1024 / 1000); maximum windows = round(nominal GB * 3 / 4). The complete plan contains at most this many blocks. Overflow tasks run serially inside existing blocks; no additional capacity waves. Each block starts as soon as its own prerequisites are verified and a capacity/resource lease is available. A dependent of A does not wait for unrelated B. Reject unknown, self and cyclic dependencies before launching. Merge transitive resource conflicts and preserve dependency order. Windows file locks normalize case, separators and existing junction ancestors, and account for directory containment.
|
|
8
|
+
|
|
9
|
+
The fresh `--run-windows` path applies a spawn gate after planning. A single block, or a plan whose dependency waves never contain two ready blocks, returns `mode: inline` with the complete original task inventory and does not spawn an execution unit; the initiating agent executes that request in its current conversation. Only a wave with at least two ready blocks enters parallel execution. Units default to headless (the launcher runs under a hidden carrier; `--mode window` switches to visible TUI windows; the mode is fixed in the run ledger and inherited by `--resume-run`). This gate prevents a serial request from being presented as a one-unit parallel run while keeping every original task available to the caller.
|
|
10
|
+
|
|
11
|
+
The parent persists manifest.json with the original request and T001... task identities before asking the planner. Model already_done/feasible flags never authorize omission. Uncertain model coverage falls back to the complete original inventory in a serial block.
|
|
12
|
+
|
|
13
|
+
Task parsing preserves ordinary comma continuations and quoted command separators. Deterministic planning uses the same boundaries as the original ledger. Semantic classification must return the original IDs; the parent restores original text by ID before grouping. A paraphrased summary cannot silently collapse otherwise valid parallel work into the coverage fallback.
|
|
14
|
+
|
|
15
|
+
## Execution and verification
|
|
16
|
+
|
|
17
|
+
The initiating agent chooses a unique run ID and defines independent acceptance checks before launch. This is automatic parent preparation, not a user approval prompt.
|
|
18
|
+
|
|
19
|
+
- Execute: node <absolute-entry> --run-id <id> --acceptance-file <absolute-json> --run-windows <request>
|
|
20
|
+
- Poll: node <absolute-scripts>/poll-windows.mjs --run-id <id> --require-all-success
|
|
21
|
+
- Recheck outputs without rerunning tasks: node <absolute-entry> --verify-run <id> --acceptance-file <absolute-json>
|
|
22
|
+
- Recover the saved plan after interruption: node <absolute-entry> --resume-run <id>
|
|
23
|
+
|
|
24
|
+
Each run owns workspace/temp/orchestrator/runs/<id>/{manifest.json,summary.json,acceptance.json,attempts/}. Every attempt records its exact remaining task IDs and a unique token. The TUI runs the supplied checkpoint command before and immediately after each task. The start record is created exclusively; a duplicate start fails. Poll requires the exact run ID, every planned block, every original task, verified statuses and closed launcher identities. An unrelated or previous run never satisfies this check. Failed prerequisites block dependants and remain in the task ledger. A completion marker or window closure alone is not success. Missing acceptance checks produce awaiting-verification, never a false pass. The parent can supply checks and recheck the same run without another TUI execution.
|
|
25
|
+
|
|
26
|
+
Acceptance JSON maps T001, T002... to arrays of parent-defined checks. Supported checks:
|
|
27
|
+
|
|
28
|
+
{"T001":[{"type":"json","path":"temp/result.json","equals":{"answer":42}}]}
|
|
29
|
+
|
|
30
|
+
file checks require a regular file and optionally sha256; json checks compare specified top-level values; absent checks confirm deletion. command checks run an explicit executable with an args array, shell disabled, from the initiating workspace; they check exitCode (default 0) and optional stdout. Checks may run repeatedly, so they must be read-only. The parent must choose checks that establish the intended result: file existence alone is insufficient for semantic correctness. Checkpoint tokens identify this execution; they are not a security boundary against an intentionally malicious agent.
|
|
31
|
+
|
|
32
|
+
## Interruption and recovery
|
|
33
|
+
|
|
34
|
+
The launcher invokes the CLI once. It preserves failure evidence instead of blindly replaying a whole block. The controller independently rechecks every checkpoint and retries only remaining work, with a bounded attempt count (default two per block per controller) and backoff. Replacement windows use the same planned block and cannot exceed global concurrent capacity; verified tasks are omitted from their prompts.
|
|
35
|
+
|
|
36
|
+
If a task started but its output cannot be proven, automatic retry requires an explicit parent-defined policy:
|
|
37
|
+
|
|
38
|
+
{"T002":{"retrySafe":true,"checks":[{"type":"json","path":"temp/result.json","equals":{"done":true}}]}}
|
|
39
|
+
|
|
40
|
+
retrySafe means repeating the operation cannot create an unintended duplicate effect; it is not a user approval switch. It defaults to false. Tasks with uncertain effects stay in the ledger as needs-reconciliation, and their dependants do not run. The parent investigates actual state and supplies meaningful verification; it must not mark arbitrary commands retry-safe to force progress. Exactly-once execution of arbitrary external effects cannot be guaranteed without an external idempotency mechanism.
|
|
41
|
+
|
|
42
|
+
--resume-run reuses the saved task inventory, acceptance checks and original workspace. It never calls the planner again. A live launcher is adopted by PID, creation time, executable and command line; no replacement is opened while it survives. A dead controller's ownership is reclaimed under the coordinator mutex, and a second live controller is rejected. Completed tasks are rechecked; successful finalized runs resume without opening any window. Recovery of protocol-1 runs is not supported; their original explicit acceptance checks can still be used with --verify-run. After a controller crash, the initiating agent invokes --resume-run; there is no background service that restarts controllers automatically.
|
|
43
|
+
|
|
44
|
+
## Global coordination and process identity
|
|
45
|
+
|
|
46
|
+
Runs in both tools share a window coordinator across this Windows user session, including different workspaces. The first workspace owns temp/orchestrator/coordinator; HKCU Software/TuiOrchestrator stores only its locator. A Windows named mutex serializes lease changes and is released on crashes. Its wait is tunable and retried, because a loaded machine with many contenders can legitimately queue past a fixed value: `ORCH_MUTEX_WAIT_MS` (default 60000 ms), `ORCH_MUTEX_RETRIES` (default 2, exponential + jittered backoff). Ticket files left behind by a killed helper process are reclaimed by age at the start of every transaction (`ORCH_MUTEX_TICKET_TTL_MS`, default 600000 ms). Other workspaces keep their own run outputs in their own temp directories. ORCH_COORDINATOR_DIR is an explicit advanced override; all related callers must use the same value to share the cap.
|
|
47
|
+
|
|
48
|
+
A lease is held before launch and records owner and launcher identity. Capacity counts pending and active windows across runs. Busy resources wait without dropping tasks. Only resource/dependency waits and pre-existing global occupancy delay execution; overflow tasks never create extra windows. Launcher identity includes PID, creation time, executable name and command line. Close operations recheck identity and kill descendants deepest first; they do not kill the shared Windows Terminal host.
|
|
49
|
+
|
|
50
|
+
## Runtime policy
|
|
51
|
+
|
|
52
|
+
Windows inherit the initiating directory; do not derive cwd from a target filename. Orchestrator-owned transient files stay under that workspace/temp. Codex-owned configuration and sessions remain under CODEX_HOME. Approval stays never; there is no manual TUI approval step. When the effective policy is never + danger-full-access, the Codex TUI launcher carries `--dangerously-bypass-approvals-and-sandbox` so the project trust confirmation cannot pause an already-authorized window; restricted sandbox values remain unchanged. Existing external host restrictions still apply. The current Codex adapter uses the existing OpenAI login with HTTPS Responses and disables WebSocket probing. Network loss can still cause HTTPS retries; other model providers require a separately tested adapter policy. Windows PowerShell native argv escaping preserves JSON, quotes, Chinese and paths with spaces.
|
|
53
|
+
|
|
54
|
+
## Caller-driven execution (any agent)
|
|
55
|
+
|
|
56
|
+
Host identity is optional. A caller may supply both the plan and the execution surface itself:
|
|
57
|
+
|
|
58
|
+
- `--plan-file <json>` replaces the model planner with the caller's own ledger or block plan: `{"tasks":[…]}` (flat classification, then the deterministic engine groups it) or `{"blocks":[…]}` (final blocks). The authoritative inventory still comes from `requestTasks(<original request>)`; the plan file must cover its `T001…` ids exactly (the read-only `--tasks` entry prints that ledger), every block task prompt must contain its original task text verbatim or the plan collapses into one serial `complete-inventory` block, and unknown/self/cyclic dependencies are refused. With a plan file the run performs no model calls at all (planner, read-only classification and the decompose cache are skipped), and `--plan --plan-file` is a deterministic dry run.
|
|
59
|
+
- `--unit-cmd '<json>'` / `--unit-cmd-file <json>` replaces host resolution with the caller's own unit command (`bin` + `args` template with `{promptFile}`/`{prompt}`/`{workspace}` placeholders, one delivery mode `file|arg|stdin`, optional `modelArgs`/`effortArgs`). No `--help`/`--version` probe and no version assertion is performed — the caller vouches for the command. Execution keeps the identical contract: launcher identity, leases, run board, guardian, recovery, checkpoints, the token marker and the parent's acceptance re-check; the unit's output is never parsed for success.
|
|
60
|
+
- The unit profile is persisted in the run ledger; `--resume-run` reuses it and refuses a different one. An unknown agent with neither half fails closed with a diagnostic that names both paths (supply a unit command, or adapt the CLI).
|
|
61
|
+
|
|
62
|
+
Nothing else changes: capacity, waves, the inline gate, the maintenance lock and the acceptance contract stay exactly as described above.
|
|
63
|
+
|
|
64
|
+
## Failure contract
|
|
65
|
+
|
|
66
|
+
The public entries answer failures the same way for every caller: one `[orchestrator] FATAL: <reason>` line on stderr, a non-zero exit code, and no Node stack (stacks only with `ORCH_DEBUG=1`). An unknown run ID never produces a raw `ENOENT` and never hangs: `--verify-run`/`--resume-run` refuse immediately with `Run not found: <id>`, and `--watch` applies a bounded grace (`ORCH_WATCH_GRACE_MS`, default 60000 ms — the run directory may legitimately not exist yet while `--run-windows` is still planning) before refusing; a run that loses its `summary.json` mid-watch is reported rather than watched forever. Exit codes: `0` success, `1` refused/failed, `2` usage.
|
|
67
|
+
|
|
68
|
+
## Regression commands
|
|
69
|
+
|
|
70
|
+
Run npm test in the generic repository. Invoke test/live-codex.mjs by absolute path from the desired workspace for the opt-in 8-window/9-task real-model acceptance. ORCH_LIVE_ENTRY can select the Codex-specific scripts/orchestrate-sdk.mjs entry to run the same acceptance contract. Existing historical result files are not evidence for a changed core version; retain newly generated run IDs and summaries.
|