opencode-orchestrator-uni 0.1.0 → 0.2.0
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/README.md +36 -6
- package/README.ru.md +352 -0
- package/agents/en/arbiter.md +38 -0
- package/agents/en/empiric.md +84 -0
- package/agents/en/factcheck.md +43 -0
- package/agents/en/fix-runner.md +59 -0
- package/agents/en/invariant.md +79 -0
- package/agents/en/meta.md +48 -0
- package/agents/en/orchestrator.md +55 -0
- package/agents/en/redteam.md +57 -0
- package/docs/en/call-mechanics.md +235 -0
- package/docs/en/instruction-other-session.md +81 -0
- package/docs/en/process-ready-loop.md +66 -0
- package/docs/en/root-runbook.md +57 -0
- package/docs/{call-mechanics.md → ru/call-mechanics.md} +1 -1
- package/docs/{root-runbook.md → ru/root-runbook.md} +1 -1
- package/index.ts +126 -17
- package/package.json +3 -1
- /package/agents/{arbiter.md → ru/arbiter.md} +0 -0
- /package/agents/{empiric.md → ru/empiric.md} +0 -0
- /package/agents/{factcheck.md → ru/factcheck.md} +0 -0
- /package/agents/{fix-runner.md → ru/fix-runner.md} +0 -0
- /package/agents/{invariant.md → ru/invariant.md} +0 -0
- /package/agents/{meta.md → ru/meta.md} +0 -0
- /package/agents/{orchestrator.md → ru/orchestrator.md} +0 -0
- /package/agents/{redteam.md → ru/redteam.md} +0 -0
- /package/docs/{instruction-other-session.md → ru/instruction-other-session.md} +0 -0
- /package/docs/{process-ready-loop.md → ru/process-ready-loop.md} +0 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Instruction: a test run of the audit loop in another session (on another project)
|
|
2
|
+
|
|
3
|
+
The loop: read-only subagents `factcheck`, `empiric`, `invariant`, `arbiter`, `meta`, `redteam` + the writing `fix-runner` (fixes per the "to close" list).
|
|
4
|
+
The agents are universal (without project specifics). Definitions: your harness's global agents directory (the path depends on the OS) and the harness's project agents directory. A backup copy — in the kit directory (`agents/`).
|
|
5
|
+
|
|
6
|
+
## How to use
|
|
7
|
+
|
|
8
|
+
1. Open a session in the target project's directory — or choose the primary agent `orchestrator` (its audit loop is a built-in mode; it is enough to specify the project/change/data/budget).
|
|
9
|
+
2. Copy the prompt below into it and fill in the placeholders:
|
|
10
|
+
- `<PROJECT_PATH>` — the path to the project;
|
|
11
|
+
- `<CHANGE>` — a branch / a commit range / an uncommitted diff;
|
|
12
|
+
- `<BASE>` — the comparison base;
|
|
13
|
+
- `<DATA>` — the available corpora/test stands/reference materials (paths) and what is allowed to run;
|
|
14
|
+
- `<BUDGET>` — the time/build limits and prohibitions;
|
|
15
|
+
- `<MODE>` — `commit` (the light loop) or `release` (the gates with the red team).
|
|
16
|
+
|
|
17
|
+
## The prompt for the root agent (copy in full)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
You are the root orchestrator of the audit. Drive the audit loop to full ready.
|
|
21
|
+
|
|
22
|
+
Project: <PROJECT_PATH>
|
|
23
|
+
Change: <CHANGE>
|
|
24
|
+
Comparison base: <BASE>
|
|
25
|
+
Available data and test stands: <DATA>
|
|
26
|
+
Budget: <BUDGET>
|
|
27
|
+
Mode: <MODE>
|
|
28
|
+
|
|
29
|
+
The loop's rules:
|
|
30
|
+
- The auditors are read-only subagents; you make the fixes (micro-fixes) or delegate them to `fix-runner` (medium/large ones). You are the orchestrator: the brief, the launch, the consolidation of the reports, decisions, fixes. You do not audit yourself.
|
|
31
|
+
- Communication — via the brief file and SGR reports. Do NOT poll background subagents: the notifications arrive on their own.
|
|
32
|
+
- The loop lives until FULL ready: all roles ready, no open MAJORs, MINOR/NIT closed or explicitly accepted.
|
|
33
|
+
- Disputed matters are resolved by `arbiter` (disputed items only); the unprovable → the owner's decision: the root frames the question, records the risk and escalates.
|
|
34
|
+
- "Owner" = the user. Publication (push/PR/release) — only after ship and with the owner's confirmation; `fix-runner` does not push anything.
|
|
35
|
+
|
|
36
|
+
Steps:
|
|
37
|
+
0) Prepare the working directory `audit-<id>` in your environment's temporary area: the brief `case.md` (the template below), the registry `risks.md`.
|
|
38
|
+
For the initial audit — a "blind" copy of the state (without the history of fixes); for a repeat one — with the fixes delta.
|
|
39
|
+
1) Fact-check (foreground):
|
|
40
|
+
subagent(agent="factcheck", prompt="Read the brief <path>. Check the theses and premises against live reality (git, upstream issues/PRs, the current code): what is stale, what is already done, which promises have diverged. Return the SGR + the "FACT-CHECK" block.")
|
|
41
|
+
If verdict=stale/already_done — stop the loop and revise the task.
|
|
42
|
+
2) The pair in parallel (background, one block):
|
|
43
|
+
subagent(agent="empiric", background=true, prompt="Brief <path>. FACT-CHECK:\n<block>. Do your role; return the SGR.")
|
|
44
|
+
subagent(agent="invariant", background=true, prompt="Brief <path>. FACT-CHECK:\n<block>. Do your role; return the SGR.")
|
|
45
|
+
Save both sessionIDs from the responses.
|
|
46
|
+
3) Summary: parse the SGRs (verdicts, open MAJORs, conflicts by places/classes); materialize the summary reports/summary-<id>.md (for every MAJOR — id, verifier_role, verdict, evidence; the refuted list; accepted risks; the state revision).
|
|
47
|
+
4) Verification of MAJORs (cross-verification, fail-closed): every MAJOR finding is confirmed by the session of the opposite role (not by the author of the finding) —
|
|
48
|
+
subagent(agent="<sibling>", sessionID="<session of the opposite role>", prompt="A counterpart's finding <id>: <text>. The author's evidence: <command→output>. State revision: <directory hash/commit>. Confirm or refute it by your own reproduction; the default is not confirmed. Return: {id, verifier_role, verdict: confirmed|refuted|partial|unavailable, evidence}.")
|
|
49
|
+
partial/unavailable = "not confirmed"; a verifier failure/timeout — the same + noise. The refuted ones — into the refuted list; write the statuses into the summary reports/summary-<id>.md; an unconfirmed MAJOR does not go into fixes.
|
|
50
|
+
Conflicts → subagent(agent="arbiter", prompt="Disputed items: <item → evidence A → evidence B>. For each: confirmed/refuted/undecidable + the prism (reproducibility|contract|falsification|not_applicable; for several — an array) + the way of checking. Only these items.")
|
|
51
|
+
The summary's verdict (and the verification/arbitration) is recorded BEFORE the fixes and together with the state revision (hash/commit); a fix after the verdict is recorded → a repeat run (re-audit) or an explicit downgrade of the verdict: do not fix before the verdict.
|
|
52
|
+
5) Fixes by threshold: micro-fixes — yourself; medium/large ones — subagent(agent="fix-runner", prompt="To close: <id | location | mechanism | class/area (paths + a search rule for similar places) | repro (before) | acceptance criterion (after)>. Boundaries: <…>; budget/limit: <time, number of fixes>; commit fix-<round>. Return fix-runner/v1."). Before the re-audit check the delta (git show --stat + a key run); update risks.md (closed / accepted-risk with a reason).
|
|
53
|
+
6) Re-audit — resume of the SAME sessions (background, one block):
|
|
54
|
+
subagent(agent="empiric", sessionID="<emp>", background=true, prompt="Fixes: <range + the actual list of changed files>; closed: <…>. Check the closure of your findings and the absence of regressions on the new state; return the updated SGR.")
|
|
55
|
+
subagent(agent="invariant", sessionID="<inv>", background=true, prompt="… same …")
|
|
56
|
+
7) Repeat steps 3–6 until full ready is reached (the number of rounds is unlimited). From round 3 on, unresolved items — the arbiter one by one; recurring false ones — dismiss and record as the audit noise.
|
|
57
|
+
8) Release mode (the gates; order: red team → meta): freeze the candidate →
|
|
58
|
+
subagent(agent="redteam", background=true, prompt="Brief <path>. The author's submission: <…>. The summary of previous rounds: <…>. Falsification mandate: reproduce every claim with your own instruments; available data: <…>; generate crafted inputs. Return the SGR.")
|
|
59
|
+
→ the same loop (fixes; the red team's MAJOR findings — with verification by the session of the opposite role as directed by the root; a dispute → `arbiter`) with the red team's resume until its ready → subagent(agent="meta", prompt="Reports: <paths>; summary: <path to summary-<id>.md>. Check the MAJOR verification statuses in the summary (verdict + verifier_role); the evidence of every finding; the completeness of the self-checks, the absence of "green tests = checked". Return pass/fail.") → a check of the SHIP condition (below); ship only when it holds.
|
|
60
|
+
SHIP ⟺ meta.gate=pass ∧ closing_list is empty/closed ∧ coverage_gaps accepted by the owner (a record in risks.md). In all other cases ship is forbidden.
|
|
61
|
+
9) Final: a summary by rounds (the verdicts, what was closed, the accepted risks); save the reports in `reports/` of the audit's working directory.
|
|
62
|
+
|
|
63
|
+
Brief template `case.md`:
|
|
64
|
+
# Audit <id>: <name>
|
|
65
|
+
State: <path, commit/range; the base>
|
|
66
|
+
Context: <the goal of the change, what the author declares>
|
|
67
|
+
Materials: <diff/paths; what is ABSENT: e.g., git history unavailable>
|
|
68
|
+
Available data and budget: <corpora/reference materials/test stands (paths); whether a build is allowed (an isolated target in a temporary directory); the time limit; what is forbidden>
|
|
69
|
+
Constraints: read-only; experiments in a temporary directory; do not publish
|
|
70
|
+
Report contract: SGR (a summary + ```json per the role's schema)
|
|
71
|
+
|
|
72
|
+
If a subagent is unavailable ("Agent not found"): check the agent files in your harness's global agents directory (the path depends on the OS) and in the harness's project agents directory; restore from the backup copy in the kit directory (`agents/`).
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## What counts as the test's success
|
|
76
|
+
|
|
77
|
+
- The fact-check ran and either dismissed the stale task or gave a block for the pair.
|
|
78
|
+
- The pair produced SGR reports; the conflicts (if any) were resolved by the arbiter.
|
|
79
|
+
- The loop reached full ready (all verdicts ready; no open MAJORs).
|
|
80
|
+
- `reports\` holds all the reports and the summary `summary-<id>.md`, in `risks.md` — the accepted risks.
|
|
81
|
+
- No agent changed the project (check the target project's `git status`).
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Process: the run loop to full ready
|
|
2
|
+
|
|
3
|
+
Rule: the audit is considered complete only when **all roles** have issued `verdict=ready` and all findings are processed. No "stops after N rounds": the loop runs to full ready; when it does not converge — escalation, not endless repeats on the same state.
|
|
4
|
+
|
|
5
|
+
Numbering: "[N]" — the steps of this document (`process-ready-loop.md`); "Phase N" — the phases of `call-mechanics.md`. Cross-document references state their namespace explicitly.
|
|
6
|
+
|
|
7
|
+
## Tree
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
[0] FACT-CHECKER (the actuality of premises/issues/PRs)
|
|
11
|
+
│
|
|
12
|
+
├─ the task is stale/done → STOP (revise the task)
|
|
13
|
+
▼
|
|
14
|
+
[1] EMPIRIC ∥ INVARIANT (on state X0)
|
|
15
|
+
│ └─ conflicts? → ARBITER (disputed items only); MAJOR → independent verification (cross-verification, fail-closed)
|
|
16
|
+
▼
|
|
17
|
+
VERDICT: all ready and no open MAJORs?
|
|
18
|
+
├─ YES ──► for the release gates: [4] RED TEAM → [5] META
|
|
19
|
+
│ (input of [5]: the pair's reports + the red team's report + the summary reports/summary-<id>.md)
|
|
20
|
+
│ ├─ SHIP ⟺ `meta.gate`=pass ∧ closing_list is empty/closed ∧ coverage_gaps accepted by the owner (a record in risks-<id>.md)
|
|
21
|
+
│ ├─ otherwise ship is forbidden; a targeted list of non-conformities → fixes/rebuild of the reports → another meta pass; a repeated fail → escalation to the owner
|
|
22
|
+
│ for a commit/PR: SHIP if there were no fixes after the verdict (the red team and meta are not mandatory)
|
|
23
|
+
└─ NO ─► [2] FIXES (close the MAJORs; MINOR/NIT — close or explicitly accept with a rationale, a record in risks-<id>.md)
|
|
24
|
+
▼
|
|
25
|
+
[3] NEW STATE X1 (the fixes are committed/recorded)
|
|
26
|
+
▼
|
|
27
|
+
repeat [1] on X1 (both auditors, the same brief)
|
|
28
|
+
▼
|
|
29
|
+
… repeat [1] on Xn until all roles issue ready …
|
|
30
|
+
|
|
31
|
+
[A VERDICT EXISTS, THE STATE CHANGED] → repeat [1] (re-audit on the new state) or an explicit downgrade of the verdict
|
|
32
|
+
[COMMIT/PR] → the line "no fixes after the verdict"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
At the release gates the loop is the same, plus after the first full ready — the red team; its findings (including MAJORs — with independent verification by the session of the opposite role) go through the same loop of fixes/re-checks until it, too, issues ready.
|
|
36
|
+
|
|
37
|
+
## The "full ready" criterion
|
|
38
|
+
|
|
39
|
+
1. All roles (the empiric, the invariant; at the gates — the red team as well) — `verdict=ready`.
|
|
40
|
+
2. No open MAJORs. MINOR/NIT: closed or **explicitly accepted** with a reason (a record in `risks-<id>.md`; in the summary/PR: "accepted as debt: …").
|
|
41
|
+
3. All discrepancies are resolved by the arbiter; the unprovable → the owner's decision: the root frames the question, records the risk and escalates.
|
|
42
|
+
4. The repeat run goes **by the state after the fixes**, not by the old diff. No "re-votings" on the same snapshot.
|
|
43
|
+
5. At the release gates — `meta` = pass (`gate`=pass), `closing_list` is empty/closed, `coverage_gaps` accepted by the owner (a record in `risks-<id>.md`). **SHIP ⟺ `meta.gate`=pass ∧ `closing_list` is empty/closed ∧ `coverage_gaps` accepted by the owner (a record in `risks-<id>.md`); otherwise ship is forbidden.**
|
|
44
|
+
6. MAJOR findings go into fixes only after independent verification; the verifier's default is "not confirmed" (fail-closed).
|
|
45
|
+
7. The verdict and the verification are recorded together with the state revision (directory hash/commit); any fix after the verdict is recorded → a repeat run (re-audit) or an explicit downgrade of the verdict.
|
|
46
|
+
|
|
47
|
+
## When the loop "does not converge" — escalation (instead of a stop)
|
|
48
|
+
|
|
49
|
+
- Round 3+ and still not_ready → the arbiter examines **the remaining items one by one**: confirmed / refuted (a false positive) / undecidable.
|
|
50
|
+
- "refuted" — the finding is dismissed, recorded as a false positive of the auditor (noise data for calibration);
|
|
51
|
+
- "undecidable" — the owner's decision: the root frames the question, records the risk and escalates;
|
|
52
|
+
- "confirmed" — a mandatory fix, the loop continues.
|
|
53
|
+
- A sign of a meaningless continuation: the same false/undecidable findings repeat without new facts. This is not "not ready", this is an audit defect — record it and close it.
|
|
54
|
+
|
|
55
|
+
## Loop data (per round)
|
|
56
|
+
|
|
57
|
+
- Input: the diff/state Xn + the fact-checker's report + the list of accepted risks.
|
|
58
|
+
- Output: two SGR reports (the empiric, the invariant) + the arbiter's resolution (if any).
|
|
59
|
+
- The orchestrator's summary: `ready` / `not_ready(the list of open ones)`, what was closed, what was accepted.
|
|
60
|
+
- At the gates, additionally: the summary `reports/summary-<id>.md` — the input of meta `[5]` (Phase 6); the gates' verdicts — the red team (`closing_list`/`coverage_gaps`) and meta (`meta.gate`/`closing_list`/`coverage_gaps`).
|
|
61
|
+
|
|
62
|
+
## Notes
|
|
63
|
+
|
|
64
|
+
- "Ready" is not "zero findings" but "all findings are processed": closed or consciously accepted.
|
|
65
|
+
- The variance of single runs is accounted for by the loop itself: the next round gives a new sample; hence the loop to convergence, not "one run in a cage".
|
|
66
|
+
- History confirms the model: PR #37 (bsl-context) — the pair said ready with caveats, acceptance found 8 defects, after the fixes and a repeat acceptance — v0.22.0 and full ready.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Root-runbook: initiating an audit from the primary agent
|
|
2
|
+
|
|
3
|
+
## Roles
|
|
4
|
+
|
|
5
|
+
- **Root (the primary agent)** — does not audit: forms the brief, launches subagents, consolidates the reports; for the "unprovable" — the owner's decision: the root frames the question, records the risk and escalates. Maintains the registry of accepted risks, makes fixes as separate commits, decides on "ship".
|
|
6
|
+
- **Subagents** (read-only): `factcheck`, `empiric`, `invariant`, `arbiter`, (at the gates) `redteam`, `meta`.
|
|
7
|
+
|
|
8
|
+
## Brief template `case-<id>.md` (common to all steps)
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
# Audit: <task/PR/release>
|
|
12
|
+
State: <path to the sandbox/repo, commit or range; the diff base>
|
|
13
|
+
Context: <what the change is, its goal, what the author declares>
|
|
14
|
+
Materials: <the diff file, base/head paths; what is unavailable (e.g., git history)>
|
|
15
|
+
Constraints: read-only; experiments in a temporary directory; a targeted build; do not publish
|
|
16
|
+
Report format: SGR (a summary + ```json per its own schema)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
A "Fact-check (pre-pass): …" block is added to the pair's prompts — 5–15 lines from the fact-checker's report.
|
|
20
|
+
|
|
21
|
+
## Call checklist (order)
|
|
22
|
+
|
|
23
|
+
1. **Fact-check**: `subagent(agent="factcheck", prompt="<brief> + check the theses against live reality: git/upstream issues/PRs/code; what is stale, what is already done, which promises have diverged")`.
|
|
24
|
+
- If "stale/already done" → **STOP** and revise the task (the cheapest saving).
|
|
25
|
+
2. **The pair in parallel**: `subagent(agent="empiric", background=true, prompt="<brief> + the fact-check block")` and the same for `invariant`. Do not poll — wait for notifications.
|
|
26
|
+
3. **Summary by the root**: parse the JSON blocks; compute the verdicts (ready/not_ready), open MAJORs, intersections and discrepancies (by location+mechanism). Materialize the summary artifact `reports/summary-<id>.md` (definition — `call-mechanics.md` §4): for every MAJOR — {id, verifier_role, verdict, evidence}; the refuted list (explicit, even if empty); accepted risks; the state revision (directory hash/commit).
|
|
27
|
+
4. **Verification of MAJORs (cross-verification, fail-closed)**: every MAJOR finding is confirmed independently by **the session of the opposite role** (not by the author of the finding) — `subagent(agent="<sibling>", sessionID=<session of the opposite role>, prompt="A counterpart's finding <id>: <text>. The author's evidence: <command→output>. State revision: <directory hash/commit>. Confirm or refute it by your own reproduction; the default is not confirmed. Return: {id, verifier_role, verdict: confirmed|refuted|partial|unavailable, evidence}.")`. `verifier_role` ≠ the author's role; `partial`/`unavailable` = "not confirmed"; `refuted` — into the refuted list for the summary; a verifier failure/timeout — "not confirmed" + noise. Not confirmed → it does not go into fixes (a dispute → step 5, the arbiter); the verification statuses are written to the summary `reports/summary-<id>.md` (step 3). If a MAJOR finding is independently reproduced by both roles in their own runs — the mutual confirmation counts as verification (both sides in the summary), a separate run is not required. The sessionID in the call is mandatory: without it this is a new session, not a continuation — a step defect.
|
|
28
|
+
5. **The arbiter — only on discrepancies**: `subagent(agent="arbiter", prompt="Disputed items: <item → evidence A → evidence B>. For each: confirmed/refuted/undecidable + a command/quote.")`. The summary's verdict (and the verification/arbitration) is recorded BEFORE the fixes and **together with the state revision** (directory hash/commit); any fix after the verdict is recorded → a repeat run (re-audit) or an explicit downgrade of the verdict: do not fix before the verdict.
|
|
29
|
+
6. **Fixes** by threshold: micro — the root itself; medium/large ones — `fix-runner` (input: the "to close" list — id, location, mechanism, **class/area** (paths + a search rule for similar places), repro (before), acceptance criterion (after), severity — plus the boundaries and the **budget/limit (time, number of fixes)**; output: fix-runner/v1 with before/after/`proof`/tests). Separate commits `fix-<round>`; update the registry: `closed` / `accepted-risk` (with a reason). The root checks the delta before the re-audit. The round's test stand (if the kit ships a verification stand — the tools `tools/kit-verify` and `tools/kit-sync` of the kit directory; in rounds on other projects — only if the stand is explicitly included in the call boundaries): the stand's baseline into the fixer's prompt (if the output is passed — the fixer does not duplicate the run); closure — sync of the copies and a repeat run of the stand; both outputs — in the report's `stand` field. `fix-runner` statuses: `needs_decision` → the root's decision (for a dispute — the arbiter; if the decision is the owner's — escalation) with a record in the risk registry; `not_closed` → another iteration with the same id; the `out_of_scope` field (noticed outside the list) → include in the next "to close" list or escalate.
|
|
30
|
+
7. **Re-audit = resume of the same sessions** (the sessionID from step 2): "Fixes: <range + the actual list of changed files>. Check the closure of your findings and the absence of regressions; return the updated SGR." — historically, that is how it was done (a continuation of the GLM/DS sessions). The sessionID is mandatory: a call without it (a new session instead of a continuation) — a step defect.
|
|
31
|
+
8. **Repeat steps 3–7 until full ready is reached** (the number of rounds is unlimited; all roles ready, MAJOR=0). From round 3 on, unresolved items — the arbiter one by one; recurring false ones — dismiss and record as the audit noise.
|
|
32
|
+
9. **Gates (release; order: red team → meta)**: a frozen commit → `redteam` (the falsification mandate + corpora/test stands) → a loop over its findings (MAJORs — with verification by the session of the opposite role) → `meta` (input: the reports + the summary `reports/summary-<id>.md`; evidence, completeness, the absence of "green tests = checked").
|
|
33
|
+
- **SHIP ⟺ `meta.gate`=pass ∧ `closing_list` is empty/closed ∧ `coverage_gaps` accepted by the owner (a record in `risks-<id>.md`); otherwise ship is forbidden.**
|
|
34
|
+
- `meta` → fail: ship is forbidden; a targeted list of non-conformities → fixes/rebuild of the reports → another meta pass; a repeated fail → escalation to the owner. Ship — only after the meta's pass and the fulfillment of the ship condition above.
|
|
35
|
+
|
|
36
|
+
## Data transfer between nodes
|
|
37
|
+
|
|
38
|
+
- fact-check → both auditors: a short block in the prompt (5–15 lines);
|
|
39
|
+
- the pair → the root: SGR JSON (machine-readable; keep in `reports/`);
|
|
40
|
+
- the root → the arbiter: **only disputed items** + evidence from both sides;
|
|
41
|
+
- the root → re-audit: the fixes range + a brief "what was closed";
|
|
42
|
+
- the root → the red team: the author's submission + the summary of all previous rounds (the object of falsification);
|
|
43
|
+
- the root → meta: the reports (paths) + the summary `reports/summary-<id>.md`;
|
|
44
|
+
- the root → fix-runner: the "to close" list (with the class/area) + boundaries + budget/limit (time, number of fixes).
|
|
45
|
+
|
|
46
|
+
## Subtleties
|
|
47
|
+
|
|
48
|
+
- The `orch_uni_*` tools (if registered by the plugin): the state revision — `orch_uni_snapshot` (aggregate), an SGR report check — `orch_uni_validate_report` (invalid → resume of the session demanding that the SGR be reissued), the roles' sessionID/model/cost — `orch_uni_sessions`; otherwise — manually, as before.
|
|
49
|
+
- The brief and the risk registry — as files; the subagents return the reports into the chat (the `edit deny` rule in `permissions`; `shell` is allowed), the root saves what is needed itself.
|
|
50
|
+
- "Blind" runs — a sandbox without git history (without fix commits); re-audit — on the contrary, the fixes delta is shown.
|
|
51
|
+
- The root's verdict does not substitute for the auditors' verdicts: while both roles are not ready — the loop lives (plus the escalation rules from `process-ready-loop.md`).
|
|
52
|
+
- Timings are approximate: fact-check ~1–2 min, the pair ~5–10 min, the arbiter ~1–3 min per item, the red team — hours (the gates only).
|
|
53
|
+
- Universality: both auditors and the types being added carry no project specifics; all project specifics — in the briefs and reference materials.
|
|
54
|
+
- **Environment preflight (Code Mode)**: if `subagent` is unavailable — the loop does not start; the outcome is "done; the regular audit is deferred" (the revision + compensating checks + the run plan), details — `call-mechanics.md` §8.
|
|
55
|
+
- **A mandatory configuration snapshot after installing/updating the agents**: the configuration snapshot — `tools/config-guard` (if shipped), otherwise save a copy of the configuration manually (otherwise there will be nothing to restore from). Do not mix the sources: agent definitions — the kit directory (`agents/`), configuration — `tools/config-guard` (if shipped).
|
|
56
|
+
- **Snapshot coverage**: the configuration snapshot covers the **configuration** (your harness's global configuration directory: agent definitions, the main config, general instructions, etc.); the kit's docs (`docs/`, `agents/`, `README.md`) are NOT covered by the snapshot — the kit remains the source, and the run reports are kept in `reports/`. The snapshot check returns 0 when there are no divergences and 2 when problems are found.
|
|
57
|
+
- The configuration guard (if shipped): the default restore only adds the missing elements and does NOT overwrite existing ones; a full overwrite from the snapshot — only with explicit confirmation (`-Force` or an equivalent).
|
|
@@ -228,7 +228,7 @@ subagent({agent:"redteam", background:true,
|
|
|
228
228
|
|
|
229
229
|
Среда сессии может смениться в ходе работы: прямые инструменты заменяются вызовом через `execute`+каталог, инструмент `subagent` пропадает — аудиторский цикл в такой сессии невозможен.
|
|
230
230
|
|
|
231
|
-
-
|
|
231
|
+
- Preflight: перед аудиторской фазой проверь наличие `subagent`; нет — цикл не запускай.
|
|
232
232
|
- Статус итога: «готово; штатный аудит отложен». В итог: ревизия состояния (коммит/хэш каталога), компенсирующие проверки списком (явно как проверки), план прогона в обычной сессии.
|
|
233
233
|
- Запрещено: выдавать самопроверку/`ai`-ревью за независимый аудит; помечать задачу прошедшей аудит; писать «ready».
|
|
234
234
|
- Хендофф: аудит на зафиксированной ревизии в обычной сессии; либо роли запускаются отдельными процессами `opencode run --agent <роль> "<бриф>"` в каталоге проекта (если среда даёт shell — например, AetherShell); sessionID ролей сохранить (Фаза 3.5/re-audit).
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
- Вердикт root'а не подменяет вердикты аудиторов: пока обе роли не ready — цикл живёт (плюс правила эскалации из `process-ready-loop.md`).
|
|
52
52
|
- Тайминги ориентировочно: факт-чек ~1–2 мин, пара ~5–10 мин, арбитр ~1–3 мин на пункт, ред-тим — часы (только ворота).
|
|
53
53
|
- Универсальность: оба аудитора и добавляемые типы не содержат проектной конкретики; вся проектная специфика — в брифах и эталонах.
|
|
54
|
-
-
|
|
54
|
+
- **Preflight среды (Code Mode)**: если `subagent` недоступен — цикл не запускается; итог — «готово; штатный аудит отложен» (ревизия + компенсирующие проверки + план прогона), детали — `call-mechanics.md` §8.
|
|
55
55
|
- **Обязательный снимок конфигурации после установки/обновления агентов**: снимок конфигурации — `tools/config-guard` (если поставляется), иначе вручную сохранить копию конфигурации (иначе восстанавливать будет нечего). Источники не смешивать: определения агентов — каталог комплекта (`agents/`), конфигурация — `tools/config-guard` (если поставляется).
|
|
56
56
|
- **Покрытие снимка**: снимок конфигурации покрывает **конфигурацию** (глобальный каталог конфигурации вашего харнесса: определения агентов, основной конфиг, общие инструкции и пр.); доки комплекта (`docs/`, `agents/`, `README.md`) снимком НЕ покрыты — комплект остаётся источником, а отчёты прогонов сохраняются в `reports/`. Проверка снимка возвращает 0 при отсутствии расхождений и 2 при найденных проблемах.
|
|
57
57
|
- Хранитель конфигурации (если поставляется): восстановление по умолчанию докопирует только отсутствующие элементы и НЕ перезаписывает существующие; полная перезапись из снимка — только с явным подтверждением (`-Force` или аналог).
|
package/index.ts
CHANGED
|
@@ -34,6 +34,13 @@
|
|
|
34
34
|
* a Markdown agent file that sets its own model still wins, because config is
|
|
35
35
|
* applied after package plugins.
|
|
36
36
|
*
|
|
37
|
+
* Localization: agent definitions and process documents ship per locale
|
|
38
|
+
* (`agents/<locale>/`, `docs/<locale>/` — currently `ru` and `en`).
|
|
39
|
+
* `options.locale` selects one; otherwise the locale is auto-detected from
|
|
40
|
+
* `LC_ALL`/`LC_MESSAGES`/`LANG` and the runtime's Intl locale, falling back to
|
|
41
|
+
* `DEFAULT_LOCALE` (`en`). A locale that ships no content falls back with a
|
|
42
|
+
* warning — first to `DEFAULT_LOCALE`, then to the flat pre-locale layout.
|
|
43
|
+
*
|
|
37
44
|
* The only runtime dependency is `gray-matter` (frontmatter parsing). OpenCode
|
|
38
45
|
* installs package plugin dependencies automatically.
|
|
39
46
|
*/
|
|
@@ -50,16 +57,68 @@ export const PLUGIN_ID = "orchestrator.uni"
|
|
|
50
57
|
/** Name of the reference that exposes the kit's process documents. */
|
|
51
58
|
export const REFERENCE_NAME = "audit-kit"
|
|
52
59
|
|
|
53
|
-
/** Absolute path to the shipped agent definitions (`agents
|
|
60
|
+
/** Absolute path to the shipped agent definitions (`agents/<locale>/*.md`). */
|
|
54
61
|
export const AGENTS_DIRECTORY = fileURLToPath(new URL("./agents/", import.meta.url))
|
|
55
62
|
|
|
56
|
-
/** Absolute path to the shipped process documents (`docs
|
|
63
|
+
/** Absolute path to the shipped process documents (`docs/<locale>/*.md`). */
|
|
57
64
|
export const DOCS_DIRECTORY = fileURLToPath(new URL("./docs/", import.meta.url))
|
|
58
65
|
|
|
66
|
+
/** Locales the kit ships; `DEFAULT_LOCALE` content is the fallback for others. */
|
|
67
|
+
export const SUPPORTED_LOCALES = ["ru", "en"] as const
|
|
68
|
+
|
|
69
|
+
export type Locale = (typeof SUPPORTED_LOCALES)[number]
|
|
70
|
+
|
|
71
|
+
/** Fallback when no locale is configured or detected. */
|
|
72
|
+
export const DEFAULT_LOCALE: Locale = "en"
|
|
73
|
+
|
|
74
|
+
/** `<root>/<locale>/` — where one locale's content lives. */
|
|
75
|
+
export function localeDirectory(root: string, locale: Locale): string {
|
|
76
|
+
return path.join(root, locale)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Language subtag of a locale-ish tag: `ru-RU`, `en_US.UTF-8` → `ru`, `en`. */
|
|
80
|
+
export function parseLocaleTag(value: unknown): Locale | undefined {
|
|
81
|
+
if (typeof value !== "string") return undefined
|
|
82
|
+
const language = value
|
|
83
|
+
.trim()
|
|
84
|
+
.toLowerCase()
|
|
85
|
+
.split(/[-_.@]/, 1)[0]
|
|
86
|
+
return (SUPPORTED_LOCALES as readonly string[]).includes(language) ? (language as Locale) : undefined
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** First supported candidate wins; `DEFAULT_LOCALE` when none is. */
|
|
90
|
+
export function pickLocale(candidates: readonly string[]): Locale {
|
|
91
|
+
for (const candidate of candidates) {
|
|
92
|
+
const locale = parseLocaleTag(candidate)
|
|
93
|
+
if (locale) return locale
|
|
94
|
+
}
|
|
95
|
+
return DEFAULT_LOCALE
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Detection candidates: deliberate env overrides first, then the OS locale. */
|
|
99
|
+
function localeCandidates(): string[] {
|
|
100
|
+
const candidates: string[] = []
|
|
101
|
+
for (const key of ["LC_ALL", "LC_MESSAGES", "LANG"] as const) {
|
|
102
|
+
const value = process.env[key]
|
|
103
|
+
if (value) candidates.push(value)
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
candidates.push(Intl.DateTimeFormat().resolvedOptions().locale)
|
|
107
|
+
} catch {
|
|
108
|
+
// No Intl data in this runtime — the env values or the default decide.
|
|
109
|
+
}
|
|
110
|
+
return candidates
|
|
111
|
+
}
|
|
112
|
+
|
|
59
113
|
/** Description shown next to the `audit-kit` reference in session context. */
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
114
|
+
const REFERENCE_DESCRIPTIONS: Record<Locale, string> = {
|
|
115
|
+
ru:
|
|
116
|
+
"Процессные документы комплекта оркестратора (аудиторский цикл): call-mechanics.md, " +
|
|
117
|
+
"process-ready-loop.md, root-runbook.md, instruction-other-session.md.",
|
|
118
|
+
en:
|
|
119
|
+
"Process documents of the orchestrator agent kit (audit loop): call-mechanics.md, " +
|
|
120
|
+
"process-ready-loop.md, root-runbook.md, instruction-other-session.md.",
|
|
121
|
+
}
|
|
63
122
|
|
|
64
123
|
/** Frontmatter keys this loader understands; anything else is reported and ignored. */
|
|
65
124
|
const SUPPORTED_KEYS: ReadonlySet<string> = new Set(["description", "mode", "model", "permissions"])
|
|
@@ -231,32 +290,81 @@ function parseAgentFile(id: string, source: string): KitAgent | undefined {
|
|
|
231
290
|
}
|
|
232
291
|
}
|
|
233
292
|
|
|
234
|
-
/**
|
|
235
|
-
async function
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
293
|
+
/** Markdown file names in a directory, sorted; `[]` when the directory is absent. */
|
|
294
|
+
async function listMarkdown(directory: string): Promise<string[]> {
|
|
295
|
+
try {
|
|
296
|
+
const entries = await readdir(directory, { withFileTypes: true })
|
|
297
|
+
return entries
|
|
298
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".md"))
|
|
299
|
+
.map((entry) => entry.name)
|
|
300
|
+
.sort()
|
|
301
|
+
} catch {
|
|
302
|
+
return []
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Resolve one content root for a locale: `<root>/<locale>/` first, then
|
|
308
|
+
* `<root>/<DEFAULT_LOCALE>/`, then the flat pre-locale layout. Every fallback
|
|
309
|
+
* is reported — a silently wrong language would be worse than a warning.
|
|
310
|
+
*/
|
|
311
|
+
async function resolveLocalized(
|
|
312
|
+
root: string,
|
|
313
|
+
what: string,
|
|
314
|
+
locale: Locale,
|
|
315
|
+
): Promise<{ directory: string; files: string[] }> {
|
|
316
|
+
const steps: readonly { readonly directory: string; readonly label: string }[] = [
|
|
317
|
+
{ directory: localeDirectory(root, locale), label: `"${locale}/"` },
|
|
318
|
+
{ directory: localeDirectory(root, DEFAULT_LOCALE), label: `"${DEFAULT_LOCALE}/"` },
|
|
319
|
+
{ directory: root, label: "the flat pre-locale layout" },
|
|
320
|
+
]
|
|
321
|
+
for (let index = 0; index < steps.length; index += 1) {
|
|
322
|
+
const step = steps[index]!
|
|
323
|
+
const files = await listMarkdown(step.directory)
|
|
324
|
+
if (files.length === 0) continue
|
|
325
|
+
if (index > 0) {
|
|
326
|
+
warn(`${what}: no content shipped for locale "${locale}" — falling back to ${step.label}`)
|
|
327
|
+
}
|
|
328
|
+
return { directory: step.directory, files }
|
|
329
|
+
}
|
|
330
|
+
return { directory: localeDirectory(root, locale), files: [] }
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Read and parse every shipped `agents/<locale>/*.md` definition (stable file order). */
|
|
334
|
+
async function loadKitAgents(locale: Locale): Promise<KitAgent[]> {
|
|
335
|
+
const { directory, files } = await resolveLocalized(AGENTS_DIRECTORY, "agents", locale)
|
|
241
336
|
const agents: KitAgent[] = []
|
|
242
337
|
for (const file of files) {
|
|
243
|
-
const source = await readFile(path.join(
|
|
338
|
+
const source = await readFile(path.join(directory, file), "utf8")
|
|
244
339
|
const agent = parseAgentFile(file.replace(/\.md$/, ""), source)
|
|
245
340
|
if (agent) agents.push(agent)
|
|
246
341
|
}
|
|
247
342
|
return agents
|
|
248
343
|
}
|
|
249
344
|
|
|
345
|
+
/** `options.locale`, when present and supported; invalid values fall through to detection. */
|
|
346
|
+
function readLocaleOptions(options: unknown): Locale | undefined {
|
|
347
|
+
if (typeof options !== "object" || options === null) return undefined
|
|
348
|
+
const raw = (options as Record<string, unknown>).locale
|
|
349
|
+
if (raw === undefined) return undefined
|
|
350
|
+
const locale = parseLocaleTag(raw)
|
|
351
|
+
if (!locale) {
|
|
352
|
+
warn(`options.locale: expected one of ${SUPPORTED_LOCALES.join("|")}, got ${JSON.stringify(raw)}, ignored`)
|
|
353
|
+
}
|
|
354
|
+
return locale
|
|
355
|
+
}
|
|
356
|
+
|
|
250
357
|
async function setup(ctx: Plugin.Context): Promise<Plugin.Cleanup | undefined> {
|
|
251
358
|
const models = readModelOptions(ctx.options)
|
|
252
|
-
const
|
|
359
|
+
const locale = readLocaleOptions(ctx.options) ?? pickLocale(localeCandidates())
|
|
360
|
+
const agents = await loadKitAgents(locale)
|
|
253
361
|
const defined = new Set(agents.map((agent) => agent.id))
|
|
254
362
|
for (const id of models.perAgent.keys()) {
|
|
255
363
|
if (!defined.has(id)) warn(`options.agents["${id}"]: unknown kit agent, override ignored`)
|
|
256
364
|
}
|
|
257
365
|
const overrides = new Map([...models.perAgent].filter(([id]) => defined.has(id)))
|
|
258
366
|
if (agents.length === 0) {
|
|
259
|
-
warn(`no agent definitions found
|
|
367
|
+
warn(`no agent definitions found for locale "${locale}" under ${AGENTS_DIRECTORY}`)
|
|
260
368
|
} else {
|
|
261
369
|
await ctx.agent.transform((editor) => {
|
|
262
370
|
for (const agent of agents) {
|
|
@@ -283,11 +391,12 @@ async function setup(ctx: Plugin.Context): Promise<Plugin.Cleanup | undefined> {
|
|
|
283
391
|
// rendered for models in session context. The promise editor's parameter type uses
|
|
284
392
|
// the public client shape, which does not declare it, so the source is built as a
|
|
285
393
|
// variable to keep the excess-property check out of the way.
|
|
394
|
+
const docs = await resolveLocalized(DOCS_DIRECTORY, "docs", locale)
|
|
286
395
|
const docsReference = {
|
|
287
396
|
type: "local" as const,
|
|
288
397
|
// AbsolutePath is a branded schema type; the runtime value is a plain path.
|
|
289
|
-
path:
|
|
290
|
-
description:
|
|
398
|
+
path: docs.directory as Reference.LocalSource["path"],
|
|
399
|
+
description: REFERENCE_DESCRIPTIONS[locale],
|
|
291
400
|
}
|
|
292
401
|
await ctx.reference.transform((editor) => {
|
|
293
402
|
if (editor.get(REFERENCE_NAME)) return
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "opencode-orchestrator-uni",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"description": "Native OpenCode V2 plugin that registers the orchestrator agent kit (root orchestrator, audit subagents, fix-runner) and exposes the kit's process documents as the audit-kit reference.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"agents",
|
|
37
37
|
"docs",
|
|
38
38
|
"README.md",
|
|
39
|
+
"README.ru.md",
|
|
39
40
|
"LICENSE"
|
|
40
41
|
],
|
|
41
42
|
"scripts": {
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
"test": "bun test ./test/",
|
|
44
45
|
"lint": "biome check .",
|
|
45
46
|
"lint:fix": "biome check --write .",
|
|
47
|
+
"check:locales": "bun tools/locale-check.ts",
|
|
46
48
|
"eval": "bun eval/run.ts",
|
|
47
49
|
"eval:selfcheck": "bun eval/selfcheck.ts",
|
|
48
50
|
"eval:recompute": "bun eval/recompute.ts"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|