open-tui-orchestrator 0.9.6 → 0.9.16
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 +122 -0
- package/README-zh.md +10 -9
- package/README.md +11 -10
- package/docs/adapt.md +1 -0
- package/docs/caller-driven.md +1 -1
- package/docs/execution-contract.md +67 -1
- package/docs/pi-adapter.md +1 -1
- package/docs/public-readiness.md +14 -15
- package/docs/release-policy.md +16 -1
- package/package.json +1 -1
- package/release.json +32 -27
- package/scripts/core/adapt-lib.mjs +3 -1
- package/scripts/core/contracts.mjs +28 -7
- package/scripts/core/failure-report.mjs +130 -0
- package/scripts/core/host-cli.mjs +2 -2
- package/scripts/core/host-model.mjs +6 -1
- package/scripts/core/inactive-window.mjs +47 -32
- package/scripts/core/inactive-window.ps1 +110 -8
- package/scripts/core/leases.mjs +34 -8
- package/scripts/core/network-policy.mjs +30 -0
- package/scripts/core/orchestrate-sdk.mjs +137 -23
- package/scripts/core/reboot-recovery.mjs +54 -0
- package/scripts/core/recovery.mjs +8 -2
- package/scripts/core/runner.mjs +108 -12
- package/scripts/gate.mjs +151 -22
- package/scripts/orchestrate-sdk.mjs +4 -2
- package/scripts/recover-stale.mjs +46 -0
- package/scripts/release-integrity.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,130 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.16 — core 1.16.3
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Meta-only sentences are dropped deterministically (0.9.15's prompt rule was not enough).** The 0.9.15 live acceptance re-ran the exact dependency request and the real model *still* minted T003 from "必须按上述顺序执行:项目82的产物依赖项目81的产物。" — a prompt rule alone cannot guarantee this. `requestTasks` (the one splitter every downstream consumer derives from: classify prompt, `bindClassifiedTasks`, `ledgerPlan`) now filters meta-only sentences outright: first segment must match a meta prefix (按上述/以上…顺序|先后|要求|依赖, or 以上任务请依次…) **and** carry no concrete payload (no path/file token, no action or business verb). The inventory is never emptied (an all-meta request keeps its single task). Red-capable criterion: `test/meta-task-filter.test.mjs`.
|
|
8
|
+
- **The gate is robust on a busy machine (2026-09-18, user directive: never wait for free RAM).** Two knobs and one honest fallback: `ORCH_GATE_TEST_CONCURRENCY` (default 3; lower ⇒ less memory pressure ⇒ fewer capacity-sensitive reds) and `ORCH_GATE_SUITE_TIMEOUT_MS` (default 900000). When the suite reports reds *with a complete summary*, the gate re-runs each red title's file **in isolation**: all green ⇒ recorded as an environment flake (`[gate] L1 environment flake recorded: …`, observed pass/fail logged; L4's measured total = pass+fail) and L1 passes; any file still red ⇒ real regression, the gate fails. A truncated suite (missing summary, e.g. timeout) can never be flaked green.
|
|
9
|
+
|
|
10
|
+
## 0.9.15 — core 1.16.2
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **Finished windows are now closed at finalize (measured on the pi battery 2026-09-18).** A successful `--mode window` pi run left its panels on screen: the unit processes and the launcher had exited, but the Windows Terminal window stayed — the only sweep (`sweepLeftoverRunWindows`) ran *before* a run, and its corpse criterion ("the Windows Terminal host has no children at all") never fires while the user keeps any other WT window open (the shared host always has children). The sweep now runs both before the runner **and after it returns**, with a per-unit criterion: a recorded window is closed only when no live process references that unit's unique stem (`win-launch-<stem>.ps1` launcher path or the unit's pi session path) — i.e. the unit is really gone — plus the existing "visible window owned by Windows Terminal" checks. Red-capable criteria: `test/leftover-windows.test.mjs` (stem pairing; the post-run sweep wiring).
|
|
15
|
+
- **Pure meta/process sentences no longer become tasks.** Measured in the dependency drill: the sentence "必须按上述顺序执行:项目82的产物依赖项目81的产物" became its own task (and its own block), wasting a unit. The classify prompt now states that meta sentences (constraints without a concrete action or artifact) must be merged into the task they constrain or dropped, never given their own task id.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **`[env]` telemetry on the inline paths.** The capacity gate and the policy fallback now emit `env:{platform,node,freeMB,totalMB,capacity}` inside their JSON, so "no windows: capacity below two-windows" carries the machine state that produced it (measured: free RAM swung 0.3–3.2 GB on this box, flipping capacity 1↔4 between probes). Red-capable criterion: `test/inline-env.test.mjs`.
|
|
20
|
+
- **Foreground activation retries.** With several windows spawning back-to-back one window was measured ending `foreground=false` while its siblings reached the front (the pi battery's 4-window leg). The activation helper now retries the foreground switch up to 3 times (350 ms settle + 900 ms gap between tries) before falling back to the taskbar flash.
|
|
21
|
+
|
|
22
|
+
## 0.9.14 — core 1.16.1
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **Progress deadline for unproductive units (first-checkpoint deadline).** Measured 2026-09-18 in a live Codex `--mode window` run: an interrupted attempt (the user pressed ESC in the TUI) was retried correctly within seconds — the verdict came from the on-disk checkpoints, not from trying to detect the interrupt, which leaves no trace in the session rollout — but the retry then spent its whole turn analysing the prompt and the orchestrator's own sources instead of doing the task, so the task never reached its first (`started`) checkpoint. The watchdog now enforces the contract's own first step: if the attempt's `checkpoints/` directory has not moved past the spawn moment within `ORCH_UNIT_FIRST_CHECKPOINT_MS` (default 300000 ms; `0` disables), the attempt is closed early with an `unproductive: no task checkpoint within Ns …` reason and retried inside the attempt budget. The stall channel is untouched (a silently frozen unit still ends on `ORCH_UNIT_STALL_MS`). Red-capable criterion: `test/unit-stall.test.mjs` ("… trips the first-checkpoint deadline" — a live unit with a ticking session file that never checkpoints).
|
|
27
|
+
- **Unproductive units are auto-retried once beyond the plain attempt budget.** When every remaining task still has *no* checkpoint at the budget gate — the unit "talked but did not act" — the runner grants one extra attempt (`ORCH_UNPRODUCTIVE_RETRIES`, default 1; `0` disables) and records `unproductiveRetries` in the ledger; once that allowance is spent the budget exhaustion is reported honestly, exactly as before. Red-capable criterion: `test/transient-retry.test.mjs` ("… never writes ANY checkpoint …" with `maxAttempts: 1`).
|
|
28
|
+
- **`[env]` observability lines.** Every run now logs `[env] phase=start|final platform=… node=… free=…MB total=…MB capacity=…`, so environment monitoring (memory headroom and the admission capacity derived from it) is visible in the run's own stdout next to the `[board]` lines.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **Prompt hygiene in the unit body.** The `已独立验收的任务禁止重做:` line is emitted only when there are verified tasks — with an empty list it left a dangling colon that a live model treated as a clue about the harness — and a new hard rule instructs the unit to execute the given commands as-is instead of analysing the prompt's provenance or the orchestrator's implementation (`runner.mjs`).
|
|
33
|
+
|
|
34
|
+
## 0.9.13 — core 1.16.0
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- **Transient unit failures are absorbed, not reported (auto-detect + auto-retry).** Measured 2026-09-18: in a window-mode resume two units exited with code 1 seconds after launch (a transient shared hiccup — the identical command succeeded moments later), and because a unit that wrote a `started` checkpoint but no terminal one was classified `uncertain`, one block surfaced `needs-reconciliation` to the caller while its sibling recovered on the next attempt. The runner now treats "quick death with a started-only checkpoint" as a transient signature and auto-retries the block once — `ORCH_TRANSIENT_RETRIES` (default 1), `ORCH_TRANSIENT_WINDOW_MS` (default 180000 ms), `ORCH_TRANSIENT_BACKOFF_MS` (default 2500 ms). `"retrySafe": false` vetoes the automatic retry (the escape hatch for non-idempotent work); `"retrySafe": true` keeps its "always safe to re-run" meaning. A failure that ran past the window is never masked. Red-capable criterion: `test/transient-retry.test.mjs` (before the change the run ends `success=false` with `Uncertain tasks`).
|
|
39
|
+
- **Power-cut posture: a deterministic bootId, durable state and a boot-time recovery scan.** A real power cut kills everything at once, so the response is boot-time convergence: (1) `atomicJson` now writes `tmp -> fsync -> rename`, and readers treat a missing/unreadable ledger as *unknown*, never "finished"; (2) every run and lease record carries `bootId` (boot time from `os.uptime()`, seconds) and a lease/run-owner from a previous boot is reclaimed even when its PID matches a live process (PIDs are reused across reboots); (3) `scripts/core/reboot-recovery.mjs` + `scripts/recover-stale.mjs` list interrupted runs and (opt-in `--resume`) converge them through the one `--resume-run` path, which is itself interruptible and converging. Red-capable criterion: `test/reboot-recovery.test.mjs`.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- **The Codex network policy no longer clobbers a user-declared provider.** The OpenAI HTTPS policy (`model_provider=orchestrator-openai-https`, ChatGPT auth + no WebSocket probe) used to be injected unconditionally into every Codex launch and into the SDK classify path, overriding a provider configured in the user's `config.toml`. Measured 2026-09-18 while moving Codex onto a DeepSeek provider: the injected provider produced `API key login is required, but ChatGPT is currently being used`. `codexNetworkArgs()` / `codexNetworkConfig()` now gate on the effective provider — `auto` (default) injects only while it is the default OpenAI one, `ORCH_CODEX_NETWORK_POLICY=on|off` forces either way. Red-capable criteria: `test/network-policy.test.mjs` (custom provider ⇒ no injection) and `test/host-cli.test.mjs` (argv identity pinned to the OpenAI path).
|
|
44
|
+
- **Resume no longer fails because the machine got busier.** `runWindows` threw `Current memory capacity is below the saved block count` when free memory at resume time dropped below the block count captured at run creation (measured 2026-09-18: created with capacity 4, resume computed 2 → the whole resume was refused, `pi` window power-cut leg). Admission is now clamped to the current capacity and remaining blocks queue in waves; the ledger's block-count upper bound is still validated against total memory, so tampering is still caught.
|
|
45
|
+
|
|
46
|
+
## 0.9.12 — core 1.15.2
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- **A live-but-silent unit no longer waits out the run deadline (and no manual Ctrl-D).** Measured 2026-09-17: with the codex account out of quota the TUI sat on its usage-limit banner for 22+ minutes while the engine kept waiting (default run deadline is 6 h), and the only way out was a manual interrupt. The runner now watches *progress* (checkpoint file, result file, launcher `out`/`pty` logs, host session file — mtimes only, no host-format parsing): no movement for `ORCH_UNIT_STALL_MS` (default 600000 ms) means `stalled: no unit progress for Ns`, the unit is closed through the normal teardown path (descendants killed so the launcher still reaches its own `exit 0`; the launcher itself is never force-killed, which would leave a `[process exited, code 1]` pane), and the block retries inside its attempt budget. `ORCH_UNIT_TIMEOUT_MS` (default 2700000 ms) caps one unit's lifetime; the exhausted-budget reason now carries the last attempt's failure text. Red-capable criterion: `test/unit-stall.test.mjs` (a fake launcher that sleeps for 600 s plus a never-resolving wait hook must end as `stalled`, be closed out and retried — before the change the run hangs).
|
|
51
|
+
- **Leftover corpse windows are swept.** After a killed unit or a power cut, Windows Terminal keeps dead-pane windows ("[process exited …]") open. The engine now closes the windows it recorded for this run (`win-*.window.json` handles) when they are still owned by Windows Terminal and that host has no live children — never touching other windows. `leftoverWindowHandles()` is the pure, red-capable criterion (`test/leftover-windows.test.mjs`).
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## 0.9.11 — core 1.15.1
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- **pi host died on an ambiguous model name.** pi's `settings.json` keeps `defaultProvider` and `defaultModel` apart; the detector only read `defaultModel`, so a bare `--model deepseek-flash` reached a pi install with several authenticated providers for that alias and pi refused with `Model "deepseek-flash" is ambiguous across providers: … Use --provider or provider/model` (exit 1). In `--mode window` the TUI therefore exited immediately and every block was marked failed while the windows themselves popped correctly (measured 2026-09-17, pi 0.85.1). `detectPi` now composes `provider/model` when `defaultProvider` is set and the value is not already qualified, and passes an already-qualified spec through untouched. Red-capable criterion: `test/host-model.test.mjs` (`defaultProvider=opencode-go` + `defaultModel=glm-5.3-flash` must yield `opencode-go/glm-5.3-flash`; before the fix it yields the bare name).
|
|
59
|
+
|
|
60
|
+
|
|
3
61
|
Notable changes, newest first. Every entry pairs a package version with a core version —
|
|
4
62
|
`open-tui-orchestrator --version` prints both (`open-tui-orchestrator <version> (core <core>)`).
|
|
5
63
|
|
|
64
|
+
## 0.9.10 — core 1.15.0
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- **An L1 red now names the failing tests.** Measured 2026-09-17: a gate round went red with one failing case (plus the L4 cross-check cascade) and the name turned out to be *unrecoverable* — L1 printed counts only, and the re-judgement of the same head overwrote both `gate-<head>.log` and `judged-<head>.json`. `scripts/gate.mjs` now extracts the failing tests from the reporter's `✖ failing tests:` section (`suiteReds`, with an inline-marker fallback for interrupted runs; it never fabricates), prints them inside the L1 block, prints one machine line `[gate] suite reds (N): name @ file:line:col | …`, and dumps the full suite output to `temp/gate-suite-<stamp>.log`. `tools/orch-patrol.py` parses that line into the judgement record as `suite_reds` and echoes `PATROL_GATE_RED:` lines when a judgement fails. New red-capable criterion: `test/gate.test.mjs` (the parser must name the red test from a reporter fixture, must stay empty on marker-less and green output; before the change `suiteReds` does not exist ⇒ red). Regression 296/296.
|
|
69
|
+
|
|
70
|
+
### Fixed
|
|
71
|
+
|
|
72
|
+
- **`--mode window` now actually shows windows.** Measured 2026-09-17 with a top-level-window probe: the spawned Windows Terminal window was `visible=True showCmd=1(normal)` but was *never activated*, and the launcher helper spent 2.5 s pushing the previous foreground window back to the front — so windows opened *behind* whatever the user was looking at, and two full test rounds produced zero visible popups. The popup criterion (pidf + launcher process alive) reported `popup CONFIRMED` throughout, i.e. it was a false green. Now `inactive-window.ps1` takes `-Activate` (default in window mode): it snapshots top-level windows, waits up to 9 s for the fresh sane one, restores it with `ShowWindow(SW_SHOWNORMAL)`, raises it (topmost → not-topmost) and asks for the foreground, then prints a machine-readable verdict (`ok/hwnd/rect/foreground/raised/minimized`). The engine stores that verdict next to the pidf (`win-<stem>.window.json`), logs it on the spawn line, and `windowGenuinelyPopped` now **requires** `ok:true`, so a window sitting behind other windows can no longer pass as a popup. `ORCH_WINDOW_NOACTIVATE=1` restores the old background behavior. Red-capable criteria: `test/inactive-window.test.mjs` (`-Activate` flag + verdict plumbing, `ok:false`/exit-3 and no-verdict failures, helper stays ASCII+LF+PowerShell-parseable) and the rewritten `windowGenuinelyPopped` case (live launcher with no verdict ⇒ false).
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
75
|
+
|
|
76
|
+
- **Window capacity now comes from *available* memory**: `maximum windows = floor(available memory MB ÷ 1000) × 2` (never below 1), replacing `round(nominal GB × 3 ÷ 4)` computed from *total* memory — the old form counted memory other processes already held as spare capacity. Read per decision via `os.freemem()`; README, README-zh and `docs/pi-adapter.md` state the new rule and the regression numbers were updated for it.
|
|
77
|
+
|
|
78
|
+
## 0.9.9 — core 1.14.9
|
|
79
|
+
|
|
80
|
+
### Fixed
|
|
81
|
+
|
|
82
|
+
- **Resume adoption of a surviving unit no longer mis-reads one transient identity-snapshot miss as a dead launcher.** Measured 2026-09-16 with the L5 recovery drill re-run on the headless carrier: after the controller was killed, the guardian's resume engine read the live unit's identity with a *single* process snapshot; when that read missed (a known under-load form), the started-but-unproven task was cascaded into `needs-reconciliation` and the run finalized `success=false` about a second after resume — the drill's second kill hit a dead PID and the "stays unfinalized → guardian visibly exhausts → manual re-supply finalizes" scenario could not reproduce (it had passed on the window carrier, a timing accident of the same cause). On a resumed generation (>1) whose unit has no result file yet, the adoption read now uses the same bounded grace-retry as the spawn read (`identityWithGrace`, the default 10 s / 800 ms poll; if the identity still cannot be read the block falls back to recovery, and the engine stays alive through the grace so a re-kill still has a target): a live unit is adopted (`[resume] adopting live <block>; no duplicate window`) and the run keeps waiting on it. Fresh runs and the result-file-present path keep their single-read legacy semantics (the result is read from `rf` anyway, so identity is not required there), so existing fresh-run criteria are untouched. Acceptance: the drill's fail leg re-run goes rc=0 (`passed=true`, `stayedUnfinalized`/`manualResumeFinalized` both true, salvage rc=0) and the auto leg rc=0 — zero popups on both (WindowsTerminal count 0→0); `gate.mjs --all` PASS, regression 295/295.
|
|
83
|
+
|
|
84
|
+
## 0.9.8 — core 1.14.8
|
|
85
|
+
|
|
86
|
+
### Fixed
|
|
87
|
+
|
|
88
|
+
- **The regression and the gate no longer change with the shell's `ORCH_SPAWN_MODE`.** Measured
|
|
89
|
+
2026-09-16: on a machine whose user-level `ORCH_SPAWN_MODE=window` (kept on purpose so manual runs
|
|
90
|
+
show windows), `gate.mjs --all` went rc=1 and the suite went 289/294 — *and really popped visible
|
|
91
|
+
windows*. Four headless-default assertions (`caller-driven` / `orch-force` / `print-profile` /
|
|
92
|
+
`spawn-mode`) inherited the variable from the shell, `queue-backoff` silently ran on the window
|
|
93
|
+
carrier, and `release-package` flipped green/red with the shell's `ORCH_AGENT` (a published copy's
|
|
94
|
+
`--doctor` only diagnoses the missing SDK when a codex host is requested — `gate.mjs` forces
|
|
95
|
+
`ORCH_AGENT=codex`, a bare suite run inherits the shell). The user-level variable stays; the fix is
|
|
96
|
+
on the consumer side: `scripts/gate.mjs` pins every child to `headless` and reports the initiating
|
|
97
|
+
shell's value (`[gate] shell ORCH_SPAWN_MODE=…; children pinned to headless`), the unit-spawning
|
|
98
|
+
tests (`caller-driven`, `orch-force`, `print-profile`, `queue-backoff`), the `adapt --smoke` block
|
|
99
|
+
acceptance and `tools/orch-level-check.py` pin `headless` explicitly, the spawn-mode default
|
|
100
|
+
assertions strip the variable, the `--mode window` case passes the flag, and `release-package`
|
|
101
|
+
pins `ORCH_AGENT=codex`. Acceptance 2026-09-16: `gate.mjs --all` green with the variable set in the
|
|
102
|
+
shell and again without it, zero popups, logs filed
|
|
103
|
+
(`D:/temp/orch-L5-gate-fix-window-20260916.log`, `D:/temp/orch-L5-gate-fix-novar-20260916.log`).
|
|
104
|
+
New red-capable criterion: `test/gate.test.mjs` (the gate must report the shell value and stay
|
|
105
|
+
green; before the change the line does not exist ⇒ red). Regression 295/295.
|
|
106
|
+
|
|
107
|
+
## 0.9.7 — core 1.14.7
|
|
108
|
+
|
|
109
|
+
### Fixed
|
|
110
|
+
|
|
111
|
+
- **Process-identity failures are now diagnosable.** Measured 2026-09-15: a single
|
|
112
|
+
`Get-CimInstance Win32_Process` call took **79.8 s** on this machine (WMI/CIM degraded under
|
|
113
|
+
load) while the engine's snapshot budget is 15 s — every action entry then failed closed with a
|
|
114
|
+
bare `Cannot verify process identities`, which names neither the elapsed time nor the cause and
|
|
115
|
+
offers no way out. The line now carries the budget, the elapsed time, the cause and the knob:
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
before: [orchestrator] FATAL: Cannot verify process identities
|
|
119
|
+
after: [orchestrator] FATAL: Cannot verify process identities: the process snapshot did not
|
|
120
|
+
return within 15000 ms (elapsed 15210 ms; the machine may be under heavy load);
|
|
121
|
+
retry, or raise ORCH_PS_TIMEOUT_MS (budget 15000 ms, cap 120000)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`ORCH_PS_TIMEOUT_MS` raises the budget (default 15000 ms, capped at 120000 ms). New red-capable
|
|
125
|
+
tests: `test/snapshot-diagnostics.test.mjs` (a 1 ms budget must fail with the elapsed time and
|
|
126
|
+
the knob named; the env override and the cap are asserted).
|
|
127
|
+
|
|
6
128
|
## 0.9.6 — core 1.14.6
|
|
7
129
|
|
|
8
130
|
### Fixed
|
package/README-zh.md
CHANGED
|
@@ -12,18 +12,19 @@ open-tui-orchestrator 面向复杂、较长、包含多个可独立推进部分
|
|
|
12
12
|
|
|
13
13
|
编排器服务于复杂长任务,不会把每个小指令都套上一层窗口。单任务、单块计划或只能按依赖串行推进的请求不触发开窗;CLI 返回完整的原始任务清单和规划原因,由发起 Agent 在当前对话中直接执行。只有同一依赖波次至少有两个可执行块时,才进入并行执行——默认无窗 headless(同一 launcher 换隐藏载具,不弹窗、不抢焦点),`--mode window` 可切换为可见 TUI 窗口;因此不开窗不会意味着任务被丢弃。
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
窗口容量按**当前实际剩余可用内存**计算:每满 1000 MB 可用内存给 2 个窗口。
|
|
16
16
|
|
|
17
17
|
~~~text
|
|
18
|
-
|
|
19
|
-
最大窗口数 = round(标称内存 GB × 3 ÷ 4)
|
|
18
|
+
最大窗口数 = floor(剩余可用内存 MB ÷ 1000) × 2
|
|
20
19
|
~~~
|
|
21
20
|
|
|
22
|
-
`
|
|
21
|
+
`floor` 表示向下取整,结果不低于 1。例如剩余可用 8000 MB 得到 16 个窗口,4000 MB 得到 8 个,900 MB 得到 1 个。用**剩余可用**而不是总内存是有意的:别人已经占住的内存不是这次运行能支配的容量。实际同时活动的窗口数还要经过跨运行、跨工作区的全局协调器确认;活动窗口和正在启动的预留窗口都会占用容量。任务数超过容量时,多出的任务会并入已有块,在块内按依赖串行执行,不会被推迟到下一批,也不会从原始账本中删除。
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
例如,剩余可用 8000 MB 得到 16 个窗口容量;9 个互不冲突的任务会形成 9 个单任务块,最多同时打开 9 个窗口,9 项任务仍全部进入账本并完成验收。
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
单元会自我防御:进程还活着但不再产出任何证据(检查点不动、结果文件不出现、启动器日志不增长)的单元,在 `ORCH_UNIT_STALL_MS`(默认 600000 ms)后被判定为**停滞**,随即关窗并在该块的尝试预算内自动重跑,而不是一直等到运行上限;`ORCH_UNIT_TIMEOUT_MS`(默认 2700000 ms)限制单个单元的最长寿命。被杀单元留下的残窗会在执行器启动前被收掉,掉电/被杀之后不会留下一排死窗格要人手动关。
|
|
26
|
+
|
|
27
|
+
Windows Terminal 是默认承载。窗口沿用发起对话的工作目录;`--mode window` 下新窗口会被**主动带到最前**(置顶再取消置顶,并请求前台),确保发起运行的人真的看得见它——要回到"开在后台、不抢焦点"的旧行为,设 `ORCH_WINDOW_NOACTIVATE=1`。启动脚本、结果、锁、拆解缓存和恢复账本统一放在发起工作区的 `temp/orchestrator`;宿主 Agent 自己的配置、会话和凭据仍由宿主管理。PowerShell 只承担 Windows 生命周期、编码和进程树管理辅助,窗口承载和 Agent 适配优先走 Node.js 与 Windows Terminal。
|
|
27
28
|
|
|
28
29
|
Codex、pi、opencode、Kimi Code、Claude Code 和固定版本 OpenClaw 适配器(把不同宿主 CLI 的参数、输出和关闭方式转换成统一协议)已经接通。宿主由 `--agent`/`ORCH_AGENT`、宿主环境标记或 Windows 祖先进程链自动识别——哪个 agent 发起,窗口就用哪个 agent 的 CLI,且窗口继承发起窗当前使用的 model 与 thinking effort(显式 `ORCH_MODEL`/`ORCH_EFFORT` 优先;否则按宿主检测——Codex rollout、Kimi Code 会话 wire 日志、pi settings、opencode 与 Claude 配置,画像 agent 按其 `modelDetection` 规格——最后回退到 CLI 自身配置)。未知 Agent、未验证的 CLI 或缺失的可执行文件会直接给出诊断,不会自动安装,也不会回退到另一个 Agent。已经授权的执行上下文在 TUI 窗启动后直接执行,不插入额外人工确认;编排器只继承调用方已有权限,不扩大权限范围。
|
|
29
30
|
|
|
@@ -74,13 +75,13 @@ node cli.mjs --plan-file plan.json --unit-cmd-file unit.json --run-windows '修
|
|
|
74
75
|
|
|
75
76
|
于是,我想在我用的其它 Agent 中也能多并发+TODO LIST,然而并不是所有 Agent 都可以自由启动大量 subagents,也不是所有 Agent 都会用TODO LIST干活。因此,一个通用的替代办法就是调用它已经安装好的 CLI,打开多个 TUI 窗口并行执行。多个窗口各自运行一个独立任务块,效果与同时启动多个 subagents 接近,但不要求宿主 Agent 本身具备子代理编排能力。TODO LIST 则由技能约束和执行协议固定下来:主控先保留完整原始任务,窗口内再按块列清单、写检查点、逐项更新状态。
|
|
76
77
|
|
|
77
|
-
诚实地说,这两个能力对简单小活属于杀鸡用牛刀,所以这个编排器从来不是为了让所有请求都弹窗。它的目标是让 Agent 在复杂长任务中稳定使用 TODO LIST;能安全并行的部分,以至少两个 TUI
|
|
78
|
+
诚实地说,这两个能力对简单小活属于杀鸡用牛刀,所以这个编排器从来不是为了让所有请求都弹窗。它的目标是让 Agent 在复杂长任务中稳定使用 TODO LIST;能安全并行的部分,以至少两个 TUI 窗口同时执行;可打开的窗口数由"剩余可用内存 MB ÷ 1000 取整后 × 2"计算最大并发限制数。若请求只有一个任务、只有一个块,或依赖关系决定它只能串行,编排器就不触发开窗机制,由发起 Agent 在当前对话中直接处理。若任务多于窗口上限,多出的任务会并入某个已有块串行完成,最终账本必须覆盖每一项原始任务。
|
|
78
79
|
|
|
79
80
|
## 具体实现思路
|
|
80
81
|
|
|
81
82
|
### 1. 先确认工作区和资源容量
|
|
82
83
|
|
|
83
|
-
编排器从调用进程的当前目录或 `--workspace` 确定工作区,先创建并使用该工作区下的 `temp/orchestrator`
|
|
84
|
+
编排器从调用进程的当前目录或 `--workspace` 确定工作区,先创建并使用该工作区下的 `temp/orchestrator` 作为状态之家。随后读取本机**剩余可用内存**,按 `floor(剩余可用内存 MB ÷ 1000) × 2` 计算窗口容量(不低于 1)。容量是整个用户会话的共享上限,活动窗口和正在启动的预留窗口都会计入。
|
|
84
85
|
|
|
85
86
|
### 2. 建立不可丢失的原始任务账本
|
|
86
87
|
|
|
@@ -176,6 +177,6 @@ node cli.mjs --agent claude --doctor
|
|
|
176
177
|
node cli.mjs --agent openclaw --doctor
|
|
177
178
|
~~~
|
|
178
179
|
|
|
179
|
-
当前通用核心为 1.
|
|
180
|
+
当前通用核心为 1.16.3,离线回归为 322/322 通过;覆盖无窗 headless 派发默认(`--mode window` 保留可见弹窗;launcher/身份/租约/恢复契约同构,隐藏载具不抢焦点)、单元载体分级(TUI 类 agent 走 ConPTY 伪终端载体——真实控制台、无窗口、不抢焦点,身份/租约/恢复契约与窗口模式同构;print 类维持无控制台载体)、执行看板(块与任务状态实时 `[board]` 行、run `events.jsonl`、只读 `--watch <runId>` 流式视图含 `--jsonl`、`--status` 活动 run 摘要)、任务不丢失、多块并行容量调度、依赖波次、同项目任务按资源键精细化拆分、全局容量租约(排队块等待指数退避+抖动;`ORCH_WAIT_BACKOFF_MS`/`ORCH_WAIT_BACKOFF_MAX_MS` 可调;协调器互斥等待亦可调可重试:`ORCH_MUTEX_WAIT_MS`/`ORCH_MUTEX_RETRIES`,锁票按龄回收 `ORCH_MUTEX_TICKET_TTL_MS`)、Windows Terminal 启动(同步拉起,弹窗可靠且不抢发起窗焦点)、失焦启动回归、进程身份、断点恢复、恢复账本加固(launcherFiles 工作区包容、验收契约哈希绑定、注册表锁老化回收)、守护进程驱动的控制器丢失自动恢复(test/guardian-live-result.json)与手动 --resume-run 收编存活窗口恢复(test/recovery-live-result.json)、发起方自动识别(环境标记优先、进程链兜底:kimi/codex/claude/pi/opencode/openclaw/hermes)、`orch` 强制编排入口、print-class 数据驱动画像适配管线(表面探测→画像→解析自检→活验守门→重封→自动提交,失败一律回滚;画像 agent 按 `modelDetection` 跟随发起窗 model/effort;hermes 画像已落地——hermes 发起即自动挡:识别其身份并以自身 CLI 执行(`--no-restore-cwd` 防旧会话 CWD 写偏))、调用方自助通道(调用方自带计划文件在覆盖/依赖/回退保证不变的前提下跑通且全程零模型调用、调用方自带单元命令让任意 CLI 走同一启动器/租约/恢复/验收契约、只读 `--tasks` 账本助手、单元命令的 resume 身份一致性、未知 agent 的两条出路诊断)、档位去向留痕(summary 记录 model/effort 与三态来源:explicit/宿主会话/host-default;机器可读入口 stdout 保持纯数据)、机器可调用失败契约(公开入口失败一律只回一行 `[orchestrator] FATAL: <原因>`、无 Node 栈帧(`ORCH_DEBUG=1` 才打);未知 run 不甩 `ENOENT`、不挂起——`--verify-run`/`--resume-run` 立刻回 `Run not found: <id>`,`--watch` 先给有界宽限 `ORCH_WATCH_GRACE_MS`(默认 60s,覆盖 `--run-windows` 仍在规划、run 目录尚未建立的竞态)再拒,看板中途丢 `summary.json` 也立刻报错而不静默停摆)、维护锁、当前核心安全审计及修复(docs/security-audit-2026-09-09.md)、弱网配置、SDK 诊断、宿主 CLI 探测超时重试与逐候选拒绝诊断、OpenClaw 版本门诊断(tui 接口探针先行:未验证版本以探针实测结果 + 最后验证版本 fail-closed——版本常量只记「最后验证于」,不是准入门槛)、恢复账本校验覆盖盘符根工作区和干净发行副本。测试环境要求:离线套件假定发起 agent 的 CLI(默认 `codex`)可在 PATH 解析(Windows 下前置 codex vendor bin,即回归环境约定);缺失时依赖 agent 解析的测试文件按设计快速失败(fail-closed),其中两个运行期依赖 CLI 的用例已标注 `{skip}`。真实模型额度、不同 Windows 用户安装、长时间断网恢复和每个未来 Agent 的现场兼容性也仍需单独验收。
|
|
180
181
|
|
|
181
182
|
未知宿主、无可执行 CLI 或显式路径无效时,命令会直接失败并给出原因;不会自动安装、猜测替代路径或换用其它 Agent。
|
package/README.md
CHANGED
|
@@ -12,18 +12,19 @@ When the plan contains genuinely parallel work, the orchestrator spawns one exec
|
|
|
12
12
|
|
|
13
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
14
|
|
|
15
|
-
Window capacity is computed from
|
|
15
|
+
Window capacity is computed from the memory that is actually available right now: every full 1000 MB of free memory buys two windows.
|
|
16
16
|
|
|
17
17
|
```text
|
|
18
|
-
|
|
19
|
-
maximum windows = round(nominal memory GB × 3 ÷ 4)
|
|
18
|
+
maximum windows = floor(available memory MB ÷ 1000) × 2
|
|
20
19
|
```
|
|
21
20
|
|
|
22
|
-
`
|
|
21
|
+
`floor` drops the fraction, and the result never goes below 1. For example, 8000 MB of free memory yields 16 windows, 4000 MB yields 8, and 900 MB yields 1. *Available* rather than total memory is used on purpose: memory other processes already hold is not capacity this run can hand out. 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
22
|
|
|
24
|
-
For example,
|
|
23
|
+
For example, 8000 MB of free memory gives a capacity of 16. 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
24
|
|
|
26
|
-
|
|
25
|
+
Units defend themselves: a unit that stays alive but stops producing evidence (no checkpoint movement, no result file, no launcher-log growth) is declared stalled after `ORCH_UNIT_STALL_MS` (default 600000 ms), then closed and retried inside the block's attempt budget instead of being waited on until the run deadline; `ORCH_UNIT_TIMEOUT_MS` (default 2700000 ms) caps one unit's lifetime. Windows left over from a killed unit are swept before the runner starts, so a power cut never leaves dead panes for a user to close by hand.
|
|
26
|
+
|
|
27
|
+
Windows Terminal is the default host. Windows inherit the initiating conversation's working directory, and in `--mode window` the fresh window is deliberately brought to the front (raised, then asked for the foreground) so the person who started the run can actually see it — set `ORCH_WINDOW_NOACTIVATE=1` to keep windows in the background instead. 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
|
|
|
28
29
|
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
|
|
|
@@ -74,13 +75,13 @@ The second was watching Claude Fable 5.1 begin work by writing a TODO LIST. For
|
|
|
74
75
|
|
|
75
76
|
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
|
|
|
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
|
|
78
|
+
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 two windows per full 1000 MB of available memory (floor(available memory MB ÷ 1000) × 2). 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
|
|
|
79
80
|
## Implementation
|
|
80
81
|
|
|
81
82
|
### 1. Resolve the workspace and capacity
|
|
82
83
|
|
|
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
|
|
84
|
+
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 the local available memory and computes the capacity as `floor(available memory MB ÷ 1000) × 2` (never below 1). This capacity is shared across the user's runs and workspaces, and both active windows and startup reservations count toward it.
|
|
84
85
|
|
|
85
86
|
### 2. Create a lossless task ledger
|
|
86
87
|
|
|
@@ -107,7 +108,7 @@ The plan is converted into dependency waves and passed through an explicit windo
|
|
|
107
108
|
|
|
108
109
|
- One block returns `mode: inline` and creates no run window.
|
|
109
110
|
- 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 (
|
|
111
|
+
- 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 (floor(available memory MB ÷ 1000) × 2).
|
|
111
112
|
|
|
112
113
|
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
|
|
|
@@ -176,6 +177,6 @@ node cli.mjs --agent claude --doctor
|
|
|
176
177
|
node cli.mjs --agent openclaw --doctor
|
|
177
178
|
```
|
|
178
179
|
|
|
179
|
-
The current generic core is 1.
|
|
180
|
+
The current generic core is 1.16.3, with 322/322 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
|
|
|
181
182
|
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/docs/adapt.md
CHANGED
|
@@ -24,6 +24,7 @@ node scripts/adapt.mjs [--agent <id>] [--cli <path>] [--profiles-dir <dir>]
|
|
|
24
24
|
(`ADAPTOK-xxxxxxxx`);未回、超时或非零退出即失败回滚。`--no-live` 关闭(不推荐)。
|
|
25
25
|
6. **块级真实验收(可选 `--smoke`)**:用画像 agent 走一遍真实的 `orch` 单块执行
|
|
26
26
|
(checkpoint 协议 + 验收契约 + 结果文件),把"能接上"验证到"跑得通"。
|
|
27
|
+
内部显式钉 `headless`(非交互自动化不弹窗,不受本机 `ORCH_SPAWN_MODE` 影响)。
|
|
27
28
|
⚠️ 会消耗该 agent 的真实额度,且要求它已登录/有模型配置。
|
|
28
29
|
7. **重封 + 自动提交(默认开)**:`release-integrity --seal` 后,仅提交画像文件与 `release.json`
|
|
29
30
|
(显式路径,绝不用 `git add -A`)。`--no-commit` 关闭。
|
package/docs/caller-driven.md
CHANGED
|
@@ -64,7 +64,7 @@ Hard rules (the existing core, unchanged):
|
|
|
64
64
|
- argv is built element-by-element (`shell:false`); the PowerShell launcher keeps quotes, Chinese and spaces intact.
|
|
65
65
|
- No probe, no version assertion: the caller vouches for the command.
|
|
66
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`
|
|
67
|
+
- `unitProfile` is stored in the run ledger. The **guardian** resume passes it from the ledger automatically; a **manual** `--resume-run` must re-supply the same unit command (`--unit-cmd`/`--unit-cmd-file`) — omitting it is refused with `re-supply the identical --unit-cmd/--unit-cmd-file to resume`, and supplying a different one is refused as well (measured 2026-09-15 in the L5 recovery drill).
|
|
68
68
|
|
|
69
69
|
## 3. Run
|
|
70
70
|
|
|
@@ -4,7 +4,7 @@ Generic development uses scripts/core. The dedicated Codex plugin now owns a sep
|
|
|
4
4
|
|
|
5
5
|
## Planning and capacity
|
|
6
6
|
|
|
7
|
-
Detect memory first:
|
|
7
|
+
Detect memory first: maximum windows = floor(available memory MB / 1000) * 2 (never below 1). 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
8
|
|
|
9
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
10
|
|
|
@@ -29,6 +29,16 @@ Acceptance JSON maps T001, T002... to arrays of parent-defined checks. Supported
|
|
|
29
29
|
|
|
30
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
31
|
|
|
32
|
+
## Unit liveness
|
|
33
|
+
|
|
34
|
+
A running unit is watched for progress, not just for an exit: checkpoints, the result file and the launcher logs all count as progress. A unit that stays alive without any progress for `ORCH_UNIT_STALL_MS` (default 600000 ms) is declared `stalled: no unit progress for Ns`, closed through the normal teardown path (descendants are killed so the launcher still reaches its own `exit 0` — never a forced kill of the launcher itself), and retried inside the block's attempt budget. `ORCH_UNIT_TIMEOUT_MS` (default 2700000 ms) caps a single unit's lifetime even when progress keeps trickling. Leftover Windows Terminal windows recorded by this run (`win-*.window.json` handles, still owned by Windows Terminal, host without live children) are swept before the runner starts, so killed runs and power cuts do not leave dead panes to close by hand.
|
|
35
|
+
|
|
36
|
+
## Progress deadline for unproductive units
|
|
37
|
+
|
|
38
|
+
The contract asks each task to run its `started` checkpoint *before* doing the work, so "no checkpoint at all" is a positive signal: the unit never began. `waitWithStallWatchdog` therefore checks the attempt's `checkpoints/` directory in addition to file-mtime progress. If it has not moved past the spawn moment within `ORCH_UNIT_FIRST_CHECKPOINT_MS` (default 300000 ms; `0` disables), the attempt is closed early with an `unproductive:` reason and retried inside the attempt budget. A unit that checkpoints normally is never touched by this deadline, and a completely frozen unit still ends on the independent `ORCH_UNIT_STALL_MS` channel.
|
|
39
|
+
|
|
40
|
+
At the budget gate, a block whose remaining tasks *all* still have no checkpoint is granted one extra attempt (`ORCH_UNPRODUCTIVE_RETRIES`, default 1 — set `0` to disable): that signature means the model turned in talk rather than work, which is worth exactly one retry and never more. The ledger records `unproductiveRetries`; when the allowance is spent the block still reports an honest `failed`.
|
|
41
|
+
|
|
32
42
|
## Interruption and recovery
|
|
33
43
|
|
|
34
44
|
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.
|
|
@@ -68,3 +78,59 @@ The public entries answer failures the same way for every caller: one `[orchestr
|
|
|
68
78
|
## Regression commands
|
|
69
79
|
|
|
70
80
|
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.
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
## Failure policy (three tiers)
|
|
84
|
+
|
|
85
|
+
Every failure walks three tiers; a bare error is never the end state.
|
|
86
|
+
|
|
87
|
+
1. **auto** — the engine repairs it on its own and records the accounting (unit stall -> close window -> retry;
|
|
88
|
+
killed/uncertain units with a `retrySafe` acceptance -> retry; stale leases/tickets -> reclaimed).
|
|
89
|
+
2. **repair** — a known remedy exists and is attempted (path/entry re-probe, unlock-and-retry, single retry on
|
|
90
|
+
spawn ENOENT, retrySafe classification for idempotent work).
|
|
91
|
+
3. **guidance** — nothing left to try: the output must state the problem, the cause, what was already tried,
|
|
92
|
+
and exactly what the user should do next (`cause:` / `tried:` / `fix:` / `docs:` lines after the FATAL line).
|
|
93
|
+
|
|
94
|
+
The first line stays the machine contract — `[orchestrator] FATAL: <reason>` — and no bare stack frames are ever
|
|
95
|
+
emitted (stack only with `ORCH_DEBUG=1`). Guidance lines follow it for humans; machines can keep matching line one.
|
|
96
|
+
|
|
97
|
+
Acceptance files declare idempotency so tier 1 can retry safely:
|
|
98
|
+
`{"T001": {"checks": [...], "retrySafe": true}}`. Use `retrySafe` only when re-running the task is
|
|
99
|
+
side-effect free (writing files, recomputing) — never for non-idempotent work (sending messages, placing orders).
|
|
100
|
+
|
|
101
|
+
## Transient failures are absorbed, not reported
|
|
102
|
+
|
|
103
|
+
A unit that dies quickly with no terminal checkpoint (a `started` checkpoint and nothing after) is a *transient*
|
|
104
|
+
signature: the engine auto-retries it once per block instead of surfacing it as an error. Bounds and the escape
|
|
105
|
+
hatch: `ORCH_TRANSIENT_RETRIES` (default 1), `ORCH_TRANSIENT_WINDOW_MS` (default 180000 ms — a failure that ran
|
|
106
|
+
well past the window is never treated as transient, so real failures are not masked), and `"retrySafe": false`
|
|
107
|
+
vetoes the automatic retry for tasks whose effects are not safe to repeat. `retrySafe: true` keeps its stronger
|
|
108
|
+
meaning (always safe to re-run). Red-capable criterion: `test/transient-retry.test.mjs`.
|
|
109
|
+
|
|
110
|
+
## Power-cut posture (what survives a real power loss)
|
|
111
|
+
|
|
112
|
+
A real power cut kills every process at once; nothing can be done at that instant. Stability comes from three
|
|
113
|
+
invariants, and the response is a boot-time convergence — not a heroic repair:
|
|
114
|
+
|
|
115
|
+
1. **Durable, atomic state.** Ledger and lease writes go `tmp -> fsync -> rename` (`atomicJson` in `leases.mjs`);
|
|
116
|
+
readers treat a missing or unreadable ledger as *unknown*, never as "finished". Directory-entry fsync is not
|
|
117
|
+
available on Windows and is intentionally skipped — the rename is the atomic replace.
|
|
118
|
+
2. **Idempotent units.** Acceptance is a content check, so a half-written artifact fails verification and a re-run
|
|
119
|
+
is free. Rule: only re-checkable work belongs in a unit; non-idempotent effects must declare
|
|
120
|
+
`{"retrySafe": false}` so the engine never re-runs them by itself.
|
|
121
|
+
3. **A deterministic "who is dead" criterion.** Every run and lease record carries `bootId` (boot time derived from
|
|
122
|
+
`os.uptime()`, second precision). `bootId !== current` means *certainly from a previous boot*; PIDs are reused
|
|
123
|
+
across reboots and are never used to prove liveness.
|
|
124
|
+
|
|
125
|
+
Boot-time convergence (report-only by default):
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
node scripts/recover-stale.mjs --workspace <dir> # list interrupted runs + their resume commands
|
|
129
|
+
node scripts/recover-stale.mjs --workspace <dir> --resume # resume them through the one --resume-run path
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Recommended deployment: run the report at logon (Task Scheduler), add `--resume` when unattended. Recovery is
|
|
133
|
+
itself interruptible — if the machine dies again mid-resume the next scan picks it up (bounded, converging).
|
|
134
|
+
|
|
135
|
+
Resume admission is clamped to *current* free memory (`floor(freeMB/1000)*2`): a busier machine queues blocks in
|
|
136
|
+
waves and never fails a legitimately-created ledger just because memory shrank.
|
package/docs/pi-adapter.md
CHANGED
|
@@ -29,7 +29,7 @@ open-tui-orchestrator --agent pi --resume-run my-run
|
|
|
29
29
|
|
|
30
30
|
模型规划必须返回原始 T001 等任务 ID。主持按 ID 恢复原文后再分块,所以模型改写摘要不会让完整覆盖检查失效。遗漏、重复或未知 ID 会拒绝该规划,不丢任务。
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
内存上限为"剩余可用内存 MB ÷ 1000 取整后 × 2"(不低于 1):剩余可用 8000 MB 对应上限 16,9 项独立任务分为 9 个单任务块,而不是因逗号自动变成一个大串行块。
|
|
33
33
|
|
|
34
34
|
## 验证入口
|
|
35
35
|
|
package/docs/public-readiness.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
| 项 | 证据 |
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| MIT 许可 + 作者署名 | `LICENSE` |
|
|
10
|
-
| 离线回归全绿 |
|
|
10
|
+
| 离线回归全绿 | 296/296(本轮补 2 条:`scripts/gate.mjs` 自身可红判据;Windows-only 入口拒绝判据——判据本身已用「去守卫 + 重封副本」验证能红;2026-09-15 再补 3 条:封印树「链接拒绝」判据——红例用「摘掉链接检查 + 类型白名单」实测能红;身份枚举失败话术必须带预算/耗时/出路——红例=把话术改回裸串即红;2026-09-16 再补 1 条:gate 必须报告本壳 `ORCH_SPAWN_MODE` 且子进程钉 headless——红例=改动前无报告行即红,且本壳设 `window` 时旧版四条断言真红;2026-09-17 再补 1 条:门禁 L1 红名单——reporter `✖ failing tests` 区点名+`[gate] suite reds` 机读行+全量输出落 `temp/gate-suite-*.log`,判卷记录带 `suite_reds`;红例=解析器退化回空/对无标记文本编造即红) |
|
|
11
11
|
| 发行封印 + 副本流程 | `release.json`(43 核心 + 65 运行时)、`repair.mjs`、`docs/release-policy.md` |
|
|
12
12
|
| 机器可调用失败契约 | 一行 `[orchestrator] FATAL: …`、退出码 0/1/2、未知 run 不挂起(1.13.0 起) |
|
|
13
13
|
| 机判门禁(在仓里,任何人可跑) | `node scripts/gate.mjs --all`:L1 套件 / L2a doctor / L2b 反例能红 / L3 未知 run / L4 版本·封印·文档·套件总数对账 + 锁票巡检 |
|
|
@@ -24,9 +24,8 @@
|
|
|
24
24
|
**结论(2026-09-14,core 1.14.4 / 0.9.4):清单只剩两件事,且都不可逆、都必须用户亲手点——一、发 npm 包(需要 `npm login`/token);二、仓转公开。其余全部就绪。**
|
|
25
25
|
|
|
26
26
|
1. ~~公开前的个人路径清理~~ **完成 2026-09-14**:18 个 tracked 文件、77 处用户名归一化为 `<user>`;根因一并修掉(证据采集器、画像适配器、hermes 画像 provenance)。验证:`git grep -c yecha` 只剩公开的 GitHub 句柄 `yechang1450`。
|
|
27
|
-
2.
|
|
28
|
-
|
|
29
|
-
3. ~~CI~~ **完成 2026-09-14 / 实测 2026-09-15**:`.github/workflows/ci.yml`(`windows-latest` + Node 24 + 装 codex + `--install-deps` 自检 + `node scripts/gate.mjs --all`)。**实测结论**:在私有仓手动派发该工作流 → 作业零步骤、3 秒失败,GitHub 注解为「recent account payments have failed or your spending limit needs to be increased」——**私有仓的 Actions 作业起不来(与代码无关,是账户计费问题)**;而 GitHub 计费文档明确:**公开仓的标准 hosted runner 免费**。所以正确姿势是**转公开后再武装**(改文件头 2 行 `on:`),提前武装只会收获红叉。
|
|
27
|
+
2. ~~**npm 发布**~~ **已发布(最新=0.9.9 · `latest` · 2026-09-17 00:27 +08)**:`open-tui-orchestrator@0.9.9` 已在 registry 上(93 文件 / 315.8 kB、shasum `4dc01c89…`、integrity `sha512-ceuO3vJb…`)。发布前对当轮 HEAD `f1ce647` 复绿:`node scripts/gate.mjs --all` = `ORCH_GATE: PASS — 5 criteria`(295/295)+ `npm publish --dry-run` 面检同值。账号=个人账号 unscoped(`npm whoami` = `yechang1450`;正名未被占)。**端到端烟测(0.9.9 · 冷前缀 `D:/temp/orch-coldinstall-099`)**:① `npm install open-tui-orchestrator` → `--version` = `0.9.9 (core 1.14.9)` → `--install-deps` = `{"ok":true,"action":"installed"}`(幂等复跑 `already-present`)→ `--agent codex --doctor` = `hostError=null / carrierAvailable=true`;② `npx --yes open-tui-orchestrator@latest --version` = `0.9.9 (core 1.14.9)`(任何 agent 从 PATH 即装的路径成立)。**历史**:0.9.6 = 2026-09-15 首发(93 文件 / 310.5 kB、shasum `4b16de76…`)。
|
|
28
|
+
3. ~~CI~~ **弃用(用户令 2026-09-15)**:GitHub Actions 一律不用——私有仓被账户计费拦死(2026-09-15 实测:作业零步骤、3 秒失败),转公开也不再启用。**判定全部走本机等价门禁**:`node scripts/gate.mjs --all`(一条命令五判据 + 反例能红)+ 定时巡逻(83 的 schtasks 法,需要时并入)。`.github/workflows/ci.yml` 仅作历史记录保留、不带任何触发。
|
|
30
29
|
4. ~~公开文档面~~ **完成 2026-09-14**:`CHANGELOG.md`、`.github/ISSUE_TEMPLATE/bug_report.yml`、`SECURITY.md`(私密漏洞报告渠道 + 披露政策)、`CONTRIBUTING.md`(屋规:判据必须能红、封印纪律、数字对账)。
|
|
31
30
|
5. ~~信任与安全声明~~ **完成 2026-09-14**:`docs/trust-and-safety.md`(隐藏 agent 以你的凭据运行、Codex 的 `--dangerously-bypass-approvals-and-sandbox`、`--unit-cmd` 属调用方自授权、写哪些文件、无遥测无后台服务、如何卸载),README/README-zh 首屏与 INSTALL 均给出一行摘要 + 链接。
|
|
32
31
|
6. ~~平台声明~~ **完成 2026-09-14**:README/README-zh 首屏与 INSTALL「Requirements」均明写 **Windows-only**,其它平台直接给诊断。
|
|
@@ -34,30 +33,30 @@
|
|
|
34
33
|
**注意(2026-09-15 环境事实)**:本机 codex 账号额度耗尽(Sep 19 恢复)——模型驱动的真跑今天做不了;编排器在该条件下退化正确(inline 明说、无假成功),调用方自助通道(零模型调用)的真机双块验收已于 2026-09-15 通过。
|
|
35
34
|
8. ~~验收证据去本地化~~ **完成 2026-09-14**。
|
|
36
35
|
|
|
37
|
-
###
|
|
36
|
+
### 公开前一毫米(done 一件、剩一件可选)
|
|
38
37
|
```powershell
|
|
39
|
-
#
|
|
40
|
-
|
|
38
|
+
# 一、发布 ✅ 已完成(最新=0.9.9 / latest,2026-09-17 上架;冷装三步 + npx 实跑通过)
|
|
39
|
+
# 产物基线:93 文件 / 315.8 kB、shasum 4dc01c89…(0.9.6 = 2026-09-15 首发、310.5 kB)
|
|
41
40
|
|
|
42
|
-
#
|
|
41
|
+
# 二、转公开(可选:CI 理由已作废,只剩「让外人能看到/克隆源码」一条)
|
|
43
42
|
gh repo edit yechang1450/open-tui-orchestrator --visibility public --accept-visibility-change-consequences
|
|
44
|
-
#
|
|
45
|
-
# 私有仓的 Actions 作业被账户计费拦截(2026-09-15 实测),公开仓的标准 runner 免费。
|
|
43
|
+
# 注意:转公开后**不**武装 CI(CI 已弃用,用户令 2026-09-15);判定走本机等价门禁 `node scripts/gate.mjs --all`。
|
|
46
44
|
```
|
|
47
45
|
|
|
48
46
|
## 三、需要你决策
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
**当前只剩一个决定(npm 发布账号那条已随发布定案):**
|
|
51
49
|
|
|
52
|
-
- **仓库是否转公开**(private → public):不可逆;历史里只有作者信息(已清理机器用户名,只剩公开的 GitHub 句柄)。
|
|
53
|
-
-
|
|
50
|
+
- **仓库是否转公开**(private → public):不可逆;历史里只有作者信息(已清理机器用户名,只剩公开的 GitHub 句柄)。CI 理由已作废(CI 弃用),现在只剩「让外人看到/克隆源码」一个动机——愿意再点。
|
|
51
|
+
- ~~**npm 发布账号**~~ **已定 2026-09-15:个人账号 unscoped**(`open-tui-orchestrator@0.9.9` = `latest` 已发布;正名未被占,无需 scope 退路)。
|
|
54
52
|
|
|
55
|
-
已由我按「最省事且不烧钱」定完、不需要你操心的:平台路线(Windows-only 明说,不投跨平台)、CI
|
|
53
|
+
已由我按「最省事且不烧钱」定完、不需要你操心的:平台路线(Windows-only 明说,不投跨平台)、CI(**弃用**——判定走本机等价门禁 `node scripts/gate.mjs --all`,用户令 2026-09-15)、信任声明与安全渠道(`docs/trust-and-safety.md` + `SECURITY.md`)。
|
|
56
54
|
|
|
57
55
|
## 四、推进记录
|
|
58
56
|
|
|
59
57
|
1. ~~个人路径清理 + `CHANGELOG.md` / issue 模板~~ ✅ 2026-09-14
|
|
60
58
|
2. ~~`npm publish --dry-run` 预演 + 打包瘦身(`files`)~~ ✅ 2026-09-14(89 文件 / 292 kB)
|
|
61
|
-
3. ~~CI 骨架 + 门禁入仓~~ ✅ 2026-09-14(`scripts/gate.mjs` + `.github/workflows/ci.yml
|
|
59
|
+
3. ~~CI 骨架 + 门禁入仓~~ ✅ 2026-09-14(`scripts/gate.mjs` + `.github/workflows/ci.yml`,有意不武装)→ **2026-09-15 用户令:GitHub CI 弃用,判定走本机等价门禁**
|
|
62
60
|
4. ~~信任/安全声明定稿 + `SECURITY.md` + `CONTRIBUTING.md`~~ ✅ 2026-09-14
|
|
63
61
|
5. 干净环境首次验收(第 7 项)——**你的部分**,过了即可对外说「可以用」
|
|
62
|
+
6. ~~0.9.9 上架(`latest` 刷新)~~ ✅ 2026-09-17 00:27 +08:发布前 `gate.mjs --all` PASS(5 判据 · 295/295)→ `npm publish` → registry 回读(`version=0.9.9`、shasum `4dc01c89…`、integrity `sha512-ceuO3vJb…`)+ 冷装三步(含 `--install-deps` 幂等复跑)+ `npx` 烟测全绿;证据 `D:/temp/orch-coldinstall-099/npm-publish-099-smoke.log`。
|