opencode-herdr-orchestration 0.1.6 → 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 +246 -166
- package/bin/orchestration.js +23 -0
- package/hooks/pre-push +8 -8
- package/package.json +2 -2
- package/src/agents.js +193 -53
- package/src/index.js +99 -7
- package/src/installer.js +172 -5
- package/src/prompts.js +103 -42
- package/src/response.js +36 -12
- package/src/state.js +336 -0
package/README.md
CHANGED
|
@@ -1,11 +1,227 @@
|
|
|
1
1
|
# opencode-herdr-orchestration
|
|
2
2
|
|
|
3
|
-
Capability-separated OpenCode agents for planning, implementation, independent review, and delivery through Herdr
|
|
3
|
+
Capability-separated OpenCode agents for planning, implementation, independent review, and delivery through Herdr, organized as **Developer → Shepherd → Flock**.
|
|
4
4
|
|
|
5
5
|
This package registers the agents, provides complete structured worker-response retrieval, injects session-specific orchestration mode into shell environments, and ships a reproducible Git `pre-push` policy for new and existing repositories.
|
|
6
6
|
|
|
7
7
|
Requires Node.js 20 or newer when running the package CLI or tests.
|
|
8
8
|
|
|
9
|
+
## Philosophy
|
|
10
|
+
|
|
11
|
+
There is exactly **one Developer: the human operator**, and the Developer sits above the Shepherd. The Developer owns intent, product judgment, risk tolerance, and every final approval. No agent replaces the Developer; the Shepherd and every agent below it only prepare decisions for the Developer.
|
|
12
|
+
|
|
13
|
+
The Developer works through the Shepherd's two **technical phases**, each a registered agent:
|
|
14
|
+
|
|
15
|
+
- `shepherd` — the **planning** phase. Researches the repository through read-only workers and presents implementation-ready plans. It never implements.
|
|
16
|
+
- `shepherd-governor` — the **governance** phase. Approves plans by taking over the session, contracts bounded work, judges semantics — integrated results, review verdicts, and escalations — and owns everything remote: pushes, merges, PRs, and delivery.
|
|
17
|
+
|
|
18
|
+
The **Flock** is the bounded workforce herded under those phases:
|
|
19
|
+
|
|
20
|
+
- `sheepdog` — herds the working flock: prepares worker branches and worktrees, drives worker delegation, watches progress, retries and re-contracts leaves, runs deterministic validation, selects the shearer review tier, recovers conflicts by re-scoping ownership, and performs local integration of worker output under the governor's contracts.
|
|
21
|
+
- `grazer` — read-only research workers that graze the repository for evidence.
|
|
22
|
+
- `sheep` — bounded implementation workers that produce verified local commits.
|
|
23
|
+
- `shearer-low` / `shearer-medium` — independent read-only reviewers.
|
|
24
|
+
|
|
25
|
+
Three invariants govern the whole system:
|
|
26
|
+
|
|
27
|
+
1. **Authority flows down, never sideways or up.** Each role may mutate only what its authority permits. A worker cannot delegate beyond its assignment; a reviewer cannot mutate; the flock cannot deliver.
|
|
28
|
+
2. **Results flow up through structured channels.** Shepherd-phase agents act on settled, paginated responses and committed artifacts — never on terminal scrollback, partial state, or a worker's reasoning transcript.
|
|
29
|
+
3. **Approval never flows down implicitly.** Reaching a milestone never grants authority, and the Developer's acknowledgement of a plan is a separate, explicit control.
|
|
30
|
+
|
|
31
|
+
## Topology
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
Developer (the human operator, one per flock)
|
|
35
|
+
│
|
|
36
|
+
├── Planning phase: shepherd
|
|
37
|
+
│ └── grazer read-only research
|
|
38
|
+
│
|
|
39
|
+
└── Governance phase: shepherd-governor
|
|
40
|
+
├── grazer read-only research
|
|
41
|
+
└── sheepdog herds the working flock
|
|
42
|
+
├── sheep bounded implementation
|
|
43
|
+
├── shearer-low independent review, low reasoning
|
|
44
|
+
└── shearer-medium independent review, medium reasoning
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Roles and authorities
|
|
48
|
+
|
|
49
|
+
| Agent | Tier | Direct mutation | Spawns | Git authority |
|
|
50
|
+
| --- | --- | --- | --- | --- |
|
|
51
|
+
| `shepherd` | Strong (inherits active model) | Markdown plans, research notes, task briefs, handoffs | `grazer` only | Local Markdown commits only; no push |
|
|
52
|
+
| `shepherd-governor` | Strong (inherits active model) | Markdown briefs, handoffs, review notes | `grazer`, `sheepdog` | Integration, push, PR, merge, delivery |
|
|
53
|
+
| `sheepdog` | Fast worker (`litellm/glm-5.3-flash`) | None | `grazer`, `sheep`, `shearer-low`, `shearer-medium` | Local integration of worker output; no push |
|
|
54
|
+
| `grazer` | Fast worker (`litellm/glm-5.3-flash`) | None | None | Read-only inspection |
|
|
55
|
+
| `sheep` | Fast worker (`litellm/glm-5.3-flash`) | Assigned implementation | None | Local task commit only |
|
|
56
|
+
| `shearer-low` | Reviewer (`litellm-responses/gpt-5.6-terra`, low) | None | None | Read-only inspection |
|
|
57
|
+
| `shearer-medium` | Reviewer (`litellm-responses/gpt-5.6-terra`, medium) | None | None | Read-only inspection |
|
|
58
|
+
|
|
59
|
+
Model tiers are deliberate. The two shepherd-phase agents run on the active strong model and inherit its reasoning behavior, because planning and governance are the judgment-heavy work. Flock workers run on a fast, cheap model because their tasks are bounded and their outputs are validated. Shearers run a dedicated independent model at fixed `low` and `medium` reasoning so review is never the same model grading its own family's homework.
|
|
60
|
+
|
|
61
|
+
Sheepdog chooses `shearer-low` for localized mechanical changes with strong deterministic coverage. It chooses `shearer-medium` for security, architecture, migrations, public APIs, deployment, concurrency, cross-component work, weak coverage, or material uncertainty.
|
|
62
|
+
|
|
63
|
+
## Governing controls
|
|
64
|
+
|
|
65
|
+
### Separate acknowledgement and milestone controls
|
|
66
|
+
|
|
67
|
+
Acknowledgement and progress are intentionally different mechanisms:
|
|
68
|
+
|
|
69
|
+
- **Acknowledgement** is the act of selecting `shepherd-governor` in the session. Switching from the planning phase to the governance phase approves the latest presented plan. Nothing else — not a worker finishing, not a check passing, not time passing — approves a plan.
|
|
70
|
+
- **Milestones** track progress: the plan header (`Plan-ID`, `Base-Commit`, `Status`), each `task_id` contract, each verified local task commit, and each review verdict.
|
|
71
|
+
|
|
72
|
+
Milestones never imply acknowledgement, and acknowledgement never auto-advances a milestone. Governance-phase startup reports the plan ID, approved base, and current HEAD, then inspects divergence from the approved base before delegating.
|
|
73
|
+
|
|
74
|
+
Before acknowledging any task contract, the sheepdog reads the authoritative plan artifact itself with `herdr_plan_read` using the contract's plan ID — never a secondhand summary — and replans when the contract contradicts what the plan artifact says.
|
|
75
|
+
|
|
76
|
+
Final plans carry:
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
Plan-ID: <short-project-topic>-<YYYYMMDD>-<sequence>
|
|
80
|
+
Base-Commit: <full commit hash>
|
|
81
|
+
Status: PROPOSED
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Worker reply protocol
|
|
85
|
+
|
|
86
|
+
Worker replies are keyword-prefixed structured messages with two distinct channels:
|
|
87
|
+
|
|
88
|
+
- **Acknowledgement replies** open a coordinator's first response to a task contract: `ACK` (contract accepted, work starting), `CORRECT` (contract must be corrected first), `REPLAN` (contract conflicts with the approved plan or repository evidence), or `STOP` (blocked outright). Only the sheepdog — a squad lead, not a leaf — sends an acknowledgement turn, and only to the governor.
|
|
89
|
+
- **Leaf workers work directly.** Grazer, sheep, and shearers start their assignments without an acknowledgement turn. If a leaf cannot start, its first reply begins with `CORRECT`, `REPLAN`, or `STOP`.
|
|
90
|
+
- **Milestone replies** report completed milestones: `CONTINUE` (ready for the next milestone), `CORRECT` (defects need correction within the current task), `REPLAN` (evidence invalidates the plan; escalation for re-planning), `STOP` (blocked after a milestone), or `FINALIZE` (all milestones complete, final report follows).
|
|
91
|
+
|
|
92
|
+
`FINALIZE` closes a task and is never an acknowledgement. `CORRECT`, `REPLAN`, and `STOP` are legal on either channel.
|
|
93
|
+
|
|
94
|
+
### No chain of thought
|
|
95
|
+
|
|
96
|
+
Shepherd-phase agents govern by contracts and results, not by reading worker reasoning. The `herdr_agent_response` tool returns only the latest completed final assistant message: intermediate tool-call steps, errors, ignored text, reasoning, and terminal rendering are excluded at the source. Shearers likewise receive fresh bounded context — user goal, approved plan, task contract, base and implementation commits, diff, and verification results — never the worker conversation.
|
|
97
|
+
|
|
98
|
+
This keeps delegation bounded, keeps review independent, and keeps a shepherd-phase agent's context small enough to govern many workers.
|
|
99
|
+
|
|
100
|
+
### Deterministic validation
|
|
101
|
+
|
|
102
|
+
Before requesting semantic review, sheepdog runs or delegates deterministic repository-native checks (the repository's own check and test commands) and owns retrying deterministic failures back to the responsible sheep. Shearers judge semantics and defects; deterministic coverage is confirmed first so review cycles are never spent discovering what a test would have caught.
|
|
103
|
+
|
|
104
|
+
### Review
|
|
105
|
+
|
|
106
|
+
Shearer verdicts are:
|
|
107
|
+
|
|
108
|
+
- `PASS`: integrate after governor checks.
|
|
109
|
+
- `REWORK`: return actionable findings to the responsible sheep and review the correction.
|
|
110
|
+
- `ESCALATE`: research, re-plan, or request Shepherd judgment.
|
|
111
|
+
|
|
112
|
+
Two failed semantic review cycles for the same task escalate instead of looping indefinitely.
|
|
113
|
+
|
|
114
|
+
### Escalation
|
|
115
|
+
|
|
116
|
+
Task contracts carry explicit `escalate_if` conditions. Flock workers must escalate rather than guess when evidence contradicts the assignment, ownership must expand, a public API or migration changes unexpectedly, a product or architecture decision is needed, permissions block required work, or repeated attempts fail. Shepherd-phase agents resolve what repository evidence permits and surface unresolved product choices to the Developer — the human — for judgment. `unknown` is treated as inconclusive, never complete.
|
|
117
|
+
|
|
118
|
+
### Local integration and conflict delegation
|
|
119
|
+
|
|
120
|
+
Sheepdog owns the execution mechanics end to end: it prepares each parallel `sheep` worker a dedicated branch and worktree with non-overlapping ownership and an explicit integration order, performs local integration of worker output under the governor's contracts using a narrow merge and cherry-pick lifecycle (`git merge --ff-only`, `git merge --no-ff --no-edit`, `git cherry-pick`, and their continue/abort/quit forms), and owns conflict recovery: on any conflict it aborts immediately, re-inspects, recovers by re-scoping ownership or issuing bounded recovery tasks to the responsible sheep, and escalates when conflicts invalidate the plan, repeat, or exceed its authority. Sheepdog never hand-edits a conflicted file. The governor judges the result semantically — mechanical drift is reported as deviations, and it returns defects to sheepdog rather than editing implementation itself. No flock role pushes, opens PRs, or delivers; all integration stays local until the delivery authority pushes.
|
|
121
|
+
|
|
122
|
+
## State storage
|
|
123
|
+
|
|
124
|
+
All durable orchestration state lives outside agent memory and plugin process state, in two places:
|
|
125
|
+
|
|
126
|
+
- **The constrained orchestration state API.** Four plugin tools store and retrieve Markdown artifacts with JSON frontmatter under the repository's shared Git common directory: `herdr_plan_write` for plan artifacts (the planning shepherd only), `herdr_plan_read` for plan artifacts (shepherd, shepherd-governor, and sheepdog), and `herdr_execution_write` and `herdr_execution_read` for execution artifacts (sheepdog). Sheepdog and the governor may read the authoritative plan but never write one; plan authorship stays with the planning shepherd. Artifacts live at `<git-common-dir>/herdr/plans/<planId>.md` and `<git-common-dir>/herdr/executions/<planId>.md`. Writes are atomic; the service invokes only read-only `git rev-parse`; Markdown bodies are capped at 1 MiB; plan IDs are 1–64 characters of letters, digits, dot, underscore, or hyphen.
|
|
127
|
+
- **Git history.** Shepherd-phase agents may commit intended Markdown planning artifacts locally, and worker output is a verified local commit; progress is Git history, reviewable and revertable.
|
|
128
|
+
|
|
129
|
+
Every worker worktree sheepdog creates — including worktrees created from other worktrees — is a **peer that shares the same Git common repository**, never a nested checkout. Because the state API stores artifacts under the common directory, a plan written in one worktree governs workers in all of them, and orchestration state survives session ends, process restarts, and machine reboots. Separate clones never share this state.
|
|
130
|
+
|
|
131
|
+
Everything not stored this way is explicitly ephemeral: `SHEPHERD_MODE` is injected per OpenCode session and dies with the session; response cursors are HMAC-signed with a per-plugin-process secret, expire after six hours, and do not survive a restart of the shepherd's OpenCode plugin process.
|
|
132
|
+
|
|
133
|
+
## Git authority
|
|
134
|
+
|
|
135
|
+
| Mode | Hook behavior |
|
|
136
|
+
| --- | --- |
|
|
137
|
+
| Planning phase (`shepherd`) | Denies every push; delivery belongs to the governance phase |
|
|
138
|
+
| Governance phase (`governor`) | Allows only the current attached branch pushed to the same remote branch |
|
|
139
|
+
| Flock workers (sheepdog, grazer, sheep) | Denies every push |
|
|
140
|
+
| Shearers (shearer-low, shearer-medium) | Denies every push |
|
|
141
|
+
| Missing/unknown | Does not interfere with normal human Git use |
|
|
142
|
+
|
|
143
|
+
The hook rejects pushes from detached HEAD in governance mode, protected branches, ref renames, unrelated refs, and remote ref deletions. In governance mode `main` and `master` are always protected; add repository-specific branches:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
git config --add orchestration.protectedBranch production
|
|
147
|
+
git config --add orchestration.protectedBranch release
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Security boundary
|
|
151
|
+
|
|
152
|
+
The hook and OpenCode Bash matchers are defense in depth, not a security sandbox. A process with arbitrary local command execution may be able to alter environment variables, Git configuration, hooks, or invoke remote protocols outside ordinary `git push`. Keep server-side protected branches and repository permissions as the authoritative control. A future integration can strengthen mode provenance with signed, session-scoped policy data, but cannot replace remote authorization.
|
|
153
|
+
|
|
154
|
+
## Response topology
|
|
155
|
+
|
|
156
|
+
The `herdr_agent_response` OpenCode tool retrieves completed responses from Herdr-managed OpenCode workers without reading terminal scrollback or creating response files. Only `shepherd`, `shepherd-governor`, and `sheepdog` may call it, with a second role allowlist check inside the tool, and only for settled Herdr targets with a trusted `herdr:opencode` session mapping and an approved role. Each caller may retrieve only from its own workers: `shepherd` from `grazer`; `shepherd-governor` from `grazer` and `sheepdog`; `sheepdog` from `grazer`, `sheep`, `shearer-low`, and `shearer-medium`.
|
|
157
|
+
|
|
158
|
+
Initial call:
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"target": "frontend_worker",
|
|
163
|
+
"maxBytes": 8192
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Continuation call:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"cursor": "<opaque-signed-cursor>",
|
|
172
|
+
"maxBytes": 8192
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Successful page:
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"ok": true,
|
|
181
|
+
"target": "frontend_worker",
|
|
182
|
+
"sessionID": "ses_...",
|
|
183
|
+
"messageID": "msg_...",
|
|
184
|
+
"role": "sheep",
|
|
185
|
+
"finish": "stop",
|
|
186
|
+
"offset": 0,
|
|
187
|
+
"nextOffset": 8192,
|
|
188
|
+
"totalBytes": 24500,
|
|
189
|
+
"complete": false,
|
|
190
|
+
"cursor": "<opaque-signed-cursor>",
|
|
191
|
+
"text": "..."
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Shepherd-phase agents must continue until `complete` is true before acting on the response. Pages use UTF-8 byte offsets and never split a multibyte character.
|
|
196
|
+
|
|
197
|
+
The tool:
|
|
198
|
+
|
|
199
|
+
- resolves Herdr's tracked OpenCode session ID;
|
|
200
|
+
- runs `opencode export <sessionID>` with an argv API rather than shell interpolation;
|
|
201
|
+
- selects the latest completed final assistant message after the latest user prompt;
|
|
202
|
+
- excludes intermediate tool-call steps, errors, ignored text, reasoning, and terminal rendering;
|
|
203
|
+
- pins continuation reads to the original session, message, response digest, and offset;
|
|
204
|
+
- HMAC-signs opaque cursors with a random per-plugin-process secret;
|
|
205
|
+
- paginates on UTF-8-safe boundaries with a bounded tool-output budget;
|
|
206
|
+
- supports concurrent shepherds without response files or shared mutable retrieval state.
|
|
207
|
+
|
|
208
|
+
The export backend is intentional. Herdr starts each worker as a separate OpenCode process, so the shepherd plugin's SDK client may be tied to a different OpenCode server. `opencode export` reads the shared OpenCode session store across those process boundaries.
|
|
209
|
+
|
|
210
|
+
Normal lifecycle failures return structured errors such as:
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"ok": false,
|
|
215
|
+
"error": {
|
|
216
|
+
"code": "AGENT_NOT_SETTLED",
|
|
217
|
+
"message": "Herdr agent frontend_worker is working; wait for idle or done.",
|
|
218
|
+
"retryable": true
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Cursor continuations do not query Herdr again, so a worker may begin another turn or leave the live agent list after the initial page. The response remains pinned as long as the OpenCode session and message still exist and are unchanged. Cursors expire after six hours by default and do not survive a restart of the shepherd's OpenCode plugin process.
|
|
224
|
+
|
|
9
225
|
## Installation
|
|
10
226
|
|
|
11
227
|
Install or update the plugin with the cross-platform npm CLI:
|
|
@@ -19,11 +235,11 @@ The installer:
|
|
|
19
235
|
|
|
20
236
|
- locates the global OpenCode config directory;
|
|
21
237
|
- installs an exact package version there;
|
|
22
|
-
- interactively asks which models the shepherd,
|
|
238
|
+
- interactively asks which models the shepherd, sheepdog, flock worker, and shearer reviewer roles should use when run in a terminal;
|
|
23
239
|
- preserves existing JSONC comments, trailing commas, plugins, and tuple options;
|
|
24
240
|
- adds the stable file URL required by current OpenCode npm plugin loading;
|
|
25
241
|
- creates timestamped backups of changed config and npm manifest files;
|
|
26
|
-
- validates
|
|
242
|
+
- validates every agent role through a short-lived OpenCode debug process;
|
|
27
243
|
- never restarts or signals a running OpenCode process.
|
|
28
244
|
|
|
29
245
|
The shared Git push policy remains opt-in:
|
|
@@ -43,7 +259,7 @@ npx -y opencode-herdr-orchestration@latest uninstall --with-hooks
|
|
|
43
259
|
|
|
44
260
|
After installation or update, quit and restart OpenCode intentionally when ready. Existing processes keep their already-loaded configuration.
|
|
45
261
|
|
|
46
|
-
`configure-agents` updates the same model choices after installation. Press Enter to keep the displayed value, or enter `-` to restore that role's package default. Model names must include their provider prefix, such as `anthropic/claude-sonnet-4-6`. The
|
|
262
|
+
`configure-agents` updates the same model choices after installation. Press Enter to keep the displayed value, or enter `-` to restore that role's package default. Model names must include their provider prefix, such as `anthropic/claude-sonnet-4-6`. The sheepdog and flock worker reasoning efforts accept the provider's variant names, such as `low`, `medium`, or `high`; leaving either unset uses the model's default reasoning behavior. Shepherd-phase agents always inherit the active model and its reasoning behavior, and shearer reasoning stays fixed at `low` and `medium`.
|
|
47
263
|
|
|
48
264
|
## Manual Installation
|
|
49
265
|
|
|
@@ -89,39 +305,25 @@ Confirm the installed agents:
|
|
|
89
305
|
opencode agent list
|
|
90
306
|
```
|
|
91
307
|
|
|
92
|
-
The expected package agents are `shepherd
|
|
93
|
-
|
|
94
|
-
### Migrating from standalone agent files
|
|
95
|
-
|
|
96
|
-
Agent definitions are merged by agent name. A local file with the same name as a package agent overrides the corresponding package fields, including its prompt and permissions.
|
|
308
|
+
The expected package agents are `shepherd`, `shepherd-governor`, `sheepdog`, `grazer`, `sheep`, `shearer-low`, and `shearer-medium`.
|
|
97
309
|
|
|
98
|
-
|
|
310
|
+
## Breaking migration: remove stale agent files
|
|
99
311
|
|
|
100
|
-
|
|
312
|
+
**Before updating to the current package, remove stale standalone agent files from the previous architecture.** Agent definitions are merged by agent name, so a leftover local file with a colliding name silently overrides the corresponding package fields, including its prompt and permissions.
|
|
101
313
|
|
|
102
|
-
|
|
314
|
+
Archive or remove these standalone files after any existing OpenCode processes that depend on them have ended:
|
|
103
315
|
|
|
104
|
-
|
|
105
|
-
shepherd-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
├── shearer-review-low
|
|
112
|
-
└── shearer-review-medium
|
|
113
|
-
```
|
|
316
|
+
- `shepherd-plan.md`
|
|
317
|
+
- `shepherd-build.md`
|
|
318
|
+
- `sheep-plan.md`
|
|
319
|
+
- `sheep-build.md`
|
|
320
|
+
- `shearer-review-low.md`
|
|
321
|
+
- `shearer-review-medium.md`
|
|
322
|
+
- Older misspelled files such as `sheperd-plan.md` and `sheperd-build.md`, which load as additional legacy agents until removed.
|
|
114
323
|
|
|
115
|
-
|
|
116
|
-
| --- | --- | --- | --- | --- |
|
|
117
|
-
| `shepherd-plan` | Markdown planning artifacts | `sheep-plan` only | Markdown commits; current non-protected branch push | Active strong model |
|
|
118
|
-
| `shepherd-build` | Markdown handoffs/review notes | All four leaf roles | Integration, push, PR, merge, delivery | Active strong model |
|
|
119
|
-
| `sheep-plan` | None | None | Read-only inspection | `litellm/glm-5.3-flash` |
|
|
120
|
-
| `sheep-build` | Assigned implementation | None | Local task commit only | `litellm/glm-5.3-flash` |
|
|
121
|
-
| `shearer-review-low` | None | None | Read-only inspection | GPT-5.6 Terra, low |
|
|
122
|
-
| `shearer-review-medium` | None | None | Read-only inspection | GPT-5.6 Terra, medium |
|
|
324
|
+
Do not remove agent files merely to affect an already-running process. Complete or stop that process first, update the files, then start a new OpenCode process and verify the effective agent list with `opencode agent list`.
|
|
123
325
|
|
|
124
|
-
|
|
326
|
+
Local agent definitions with the same names are merged over plugin defaults. This permits deliberate user customization without losing unspecified plugin permissions or prompts — but only if the local file is intentionally maintained, not a stale leftover.
|
|
125
327
|
|
|
126
328
|
## Plugin configuration
|
|
127
329
|
|
|
@@ -140,6 +342,7 @@ For the published package, use:
|
|
|
140
342
|
|
|
141
343
|
```jsonc
|
|
142
344
|
{
|
|
345
|
+
"$schema": "https://opencode.ai/config.json",
|
|
143
346
|
"plugin": ["opencode-herdr-orchestration"]
|
|
144
347
|
}
|
|
145
348
|
```
|
|
@@ -153,6 +356,8 @@ Plugin tuple options can override model defaults:
|
|
|
153
356
|
"opencode-herdr-orchestration",
|
|
154
357
|
{
|
|
155
358
|
"shepherdModel": "anthropic/claude-sonnet-4-6",
|
|
359
|
+
"sheepdogModel": "litellm/glm-5.3-flash",
|
|
360
|
+
"sheepdogVariant": "medium",
|
|
156
361
|
"workerModel": "litellm/glm-5.3-flash",
|
|
157
362
|
"workerVariant": "medium",
|
|
158
363
|
"reviewerModel": "litellm-responses/gpt-5.6-terra"
|
|
@@ -162,7 +367,9 @@ Plugin tuple options can override model defaults:
|
|
|
162
367
|
}
|
|
163
368
|
```
|
|
164
369
|
|
|
165
|
-
|
|
370
|
+
The sheepdog has its own model and reasoning effort options, separate from the sheep workers it supervises.
|
|
371
|
+
|
|
372
|
+
Machine-specific governance permissions and instructions can also be added declaratively without a local JavaScript wrapper:
|
|
166
373
|
|
|
167
374
|
```jsonc
|
|
168
375
|
{
|
|
@@ -170,21 +377,19 @@ Machine-specific `shepherd-build` permissions and instructions can also be added
|
|
|
170
377
|
[
|
|
171
378
|
"opencode-herdr-orchestration",
|
|
172
379
|
{
|
|
173
|
-
"
|
|
380
|
+
"shepherdPermissions": {
|
|
174
381
|
"private_deployment_status": "allow"
|
|
175
382
|
},
|
|
176
|
-
"
|
|
383
|
+
"shepherdPromptAppend": "Use private deployment tools according to local policy."
|
|
177
384
|
}
|
|
178
385
|
]
|
|
179
386
|
]
|
|
180
387
|
}
|
|
181
388
|
```
|
|
182
389
|
|
|
183
|
-
`
|
|
184
|
-
|
|
185
|
-
Local agent definitions with the same names are merged over plugin defaults. This permits deliberate user customization without losing unspecified plugin permissions or prompts.
|
|
390
|
+
`shepherdPermissions` is merged over the planning `shepherd` agent's permissions. `shepherdPromptAppend` is appended as a separate final paragraph to the `shepherd` agent's prompt. Keep secrets out of plugin options because configuration may be displayed by diagnostics.
|
|
186
391
|
|
|
187
|
-
Machine- or organization-specific MCP tools, deployment rules, and private service instructions should stay in a local
|
|
392
|
+
Machine- or organization-specific MCP tools, deployment rules, and private service instructions should stay in a local governance-agent override rather than this public package. Add only the private permissions and prompt additions required by your environment; unspecified package defaults remain intact through deep merging.
|
|
188
393
|
|
|
189
394
|
OpenCode loads plugins and agent definitions at startup. Restart OpenCode when intentionally enabling or updating the plugin. The installer never stops or restarts an OpenCode process.
|
|
190
395
|
|
|
@@ -226,137 +431,12 @@ node .\bin\orchestration.js status
|
|
|
226
431
|
node .\bin\orchestration.js uninstall-hooks
|
|
227
432
|
```
|
|
228
433
|
|
|
229
|
-
Add repository-specific protected branches:
|
|
230
|
-
|
|
231
|
-
```bash
|
|
232
|
-
git config --add orchestration.protectedBranch production
|
|
233
|
-
git config --add orchestration.protectedBranch release
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
`main` and `master` are always protected in planning mode.
|
|
237
|
-
|
|
238
|
-
## Push policy
|
|
239
|
-
|
|
240
|
-
The plugin tracks the selected agent per OpenCode session and injects `SHEPHERD_MODE` into that session's shell environment.
|
|
241
|
-
|
|
242
|
-
| Mode | Hook behavior |
|
|
243
|
-
| --- | --- |
|
|
244
|
-
| `plan` | Allows only the attached current branch to the same non-protected remote branch |
|
|
245
|
-
| `build` | Does not add planning restrictions; shepherd-build owns delivery |
|
|
246
|
-
| `sheep-plan` | Denies every push |
|
|
247
|
-
| `sheep-build` | Denies every push |
|
|
248
|
-
| `review` | Denies every push |
|
|
249
|
-
| Missing/unknown | Does not interfere with normal human Git use |
|
|
250
|
-
|
|
251
|
-
The hook rejects detached-HEAD planning pushes, protected branches, ref renames, unrelated refs, and deletions.
|
|
252
|
-
|
|
253
|
-
### Security boundary
|
|
254
|
-
|
|
255
|
-
The hook and OpenCode Bash matchers are defense in depth, not a security sandbox. A process with arbitrary local command execution may be able to alter environment variables, Git configuration, hooks, or invoke remote protocols outside ordinary `git push`. Keep server-side protected branches and repository permissions as the authoritative control. A future integration can strengthen mode provenance with signed, session-scoped policy data, but cannot replace remote authorization.
|
|
256
|
-
|
|
257
|
-
## Planning handoff
|
|
258
|
-
|
|
259
|
-
Final plans include:
|
|
260
|
-
|
|
261
|
-
```text
|
|
262
|
-
Plan-ID: <topic>-<YYYYMMDD>-<sequence>
|
|
263
|
-
Base-Commit: <full commit hash>
|
|
264
|
-
Status: PROPOSED
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
Switching the session to `shepherd-build` approves that plan. Build startup reports the plan ID, approved base, and current HEAD, then inspects divergence before delegation.
|
|
268
|
-
|
|
269
|
-
Implementation contracts include the objective, ownership and forbidden paths, dependencies, acceptance criteria, verification, escalation conditions, and required deliverables. `sheep-build` hands a verified local commit upward. `shepherd-build` runs deterministic checks before requesting semantic review.
|
|
270
|
-
|
|
271
|
-
Reviewer verdicts are:
|
|
272
|
-
|
|
273
|
-
- `PASS`: integrate after shepherd checks.
|
|
274
|
-
- `REWORK`: return actionable findings to the responsible sheep and review the correction.
|
|
275
|
-
- `ESCALATE`: research, re-plan, or request user judgment.
|
|
276
|
-
|
|
277
|
-
Two failed semantic review cycles escalate instead of looping indefinitely.
|
|
278
|
-
|
|
279
434
|
## Worker interruption
|
|
280
435
|
|
|
281
|
-
Herdr currently exposes `send-keys`, not a narrower agent interrupt command.
|
|
436
|
+
Herdr currently exposes `send-keys`, not a narrower agent interrupt command. Shepherd-phase agents retain it to send Ctrl+C only after confirming that a worker is genuinely stuck. Prompts prohibit using worker terminals to type implementation commands or bypass shepherd permissions.
|
|
282
437
|
|
|
283
438
|
This restriction is not hard-enforced by Herdr. A native `herdr agent interrupt <target>` command would close that capability gap.
|
|
284
439
|
|
|
285
|
-
## Worker response retrieval
|
|
286
|
-
|
|
287
|
-
The `herdr_agent_response` OpenCode tool retrieves completed responses from Herdr-managed OpenCode workers without reading terminal scrollback or creating response files.
|
|
288
|
-
|
|
289
|
-
Initial call:
|
|
290
|
-
|
|
291
|
-
```json
|
|
292
|
-
{
|
|
293
|
-
"target": "frontend_worker",
|
|
294
|
-
"maxBytes": 8192
|
|
295
|
-
}
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
Continuation call:
|
|
299
|
-
|
|
300
|
-
```json
|
|
301
|
-
{
|
|
302
|
-
"cursor": "<opaque-signed-cursor>",
|
|
303
|
-
"maxBytes": 8192
|
|
304
|
-
}
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
Successful page:
|
|
308
|
-
|
|
309
|
-
```json
|
|
310
|
-
{
|
|
311
|
-
"ok": true,
|
|
312
|
-
"target": "frontend_worker",
|
|
313
|
-
"sessionID": "ses_...",
|
|
314
|
-
"messageID": "msg_...",
|
|
315
|
-
"role": "sheep-build",
|
|
316
|
-
"finish": "stop",
|
|
317
|
-
"offset": 0,
|
|
318
|
-
"nextOffset": 8192,
|
|
319
|
-
"totalBytes": 24500,
|
|
320
|
-
"complete": false,
|
|
321
|
-
"cursor": "<opaque-signed-cursor>",
|
|
322
|
-
"text": "..."
|
|
323
|
-
}
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
Shepherds must continue until `complete` is true before acting on the response. Pages use UTF-8 byte offsets and never split a multibyte character.
|
|
327
|
-
|
|
328
|
-
The tool:
|
|
329
|
-
|
|
330
|
-
- resolves Herdr's tracked OpenCode session ID;
|
|
331
|
-
- runs `opencode export <sessionID>` with an argv API rather than shell interpolation;
|
|
332
|
-
- selects the latest completed final assistant message after the latest user prompt;
|
|
333
|
-
- excludes intermediate tool-call steps, errors, ignored text, reasoning, and terminal rendering;
|
|
334
|
-
- pins continuation reads to the original session, message, response digest, and offset;
|
|
335
|
-
- HMAC-signs opaque cursors with a random per-plugin-process secret;
|
|
336
|
-
- paginates on UTF-8-safe boundaries with a bounded tool-output budget;
|
|
337
|
-
- supports concurrent shepherds without response files or shared mutable retrieval state;
|
|
338
|
-
- allows only `shepherd-plan` and `shepherd-build`, with a second authorization check inside the tool;
|
|
339
|
-
- accepts only settled Herdr targets with a trusted `herdr:opencode` session mapping and an approved leaf role.
|
|
340
|
-
|
|
341
|
-
The export backend is intentional. Herdr starts each worker as a separate OpenCode process, so the shepherd plugin's SDK client may be tied to a different OpenCode server. `opencode export` reads the shared OpenCode session store across those process boundaries.
|
|
342
|
-
|
|
343
|
-
Normal lifecycle failures return structured errors such as:
|
|
344
|
-
|
|
345
|
-
```json
|
|
346
|
-
{
|
|
347
|
-
"ok": false,
|
|
348
|
-
"error": {
|
|
349
|
-
"code": "AGENT_NOT_SETTLED",
|
|
350
|
-
"message": "Herdr agent frontend_worker is working; wait for idle or done.",
|
|
351
|
-
"retryable": true
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
Cursor continuations do not query Herdr again, so a worker may begin another turn or leave the live agent list after the initial page. The response remains pinned as long as the OpenCode session and message still exist and are unchanged. Cursors expire after six hours by default and do not survive a restart of the shepherd's OpenCode plugin process.
|
|
357
|
-
|
|
358
|
-
The previous file helper may remain for already-running OpenCode sessions. Once those processes end and this plugin is active, it is no longer needed by new sessions.
|
|
359
|
-
|
|
360
440
|
## Development
|
|
361
441
|
|
|
362
442
|
```bash
|
|
@@ -364,7 +444,7 @@ npm run check
|
|
|
364
444
|
npm test
|
|
365
445
|
```
|
|
366
446
|
|
|
367
|
-
Tests cover topology, model variants, permissions, override merging, session mode isolation, response selection, signed cursors, UTF-8 pagination, concurrent response reads, tool authorization, and Git hook behavior on protected, worker, review,
|
|
447
|
+
Tests cover topology, model variants, permissions, override merging, session mode isolation, response selection, signed cursors, UTF-8 pagination, concurrent response reads, tool authorization, orchestration state storage and access, and Git hook behavior on protected, worker, review, governance, and planning pushes.
|
|
368
448
|
|
|
369
449
|
## Releases
|
|
370
450
|
|
|
@@ -394,6 +474,6 @@ For stronger release control, configure required reviewers on the `npm` environm
|
|
|
394
474
|
- OpenCode command patterns cannot prove semantic Git intent; the hook adds checks but server-side protection is still required.
|
|
395
475
|
- `herdr agent send-keys` is broader than interrupt-only authority.
|
|
396
476
|
- Global `core.hooksPath` is singular. Existing hook frameworks must be composed rather than overwritten.
|
|
397
|
-
- PR commands are narrowly available to
|
|
477
|
+
- PR commands are narrowly available to the governance phase, but work only in GitHub repositories with an authenticated `gh` installation.
|
|
398
478
|
- Agent registration and environment hooks take effect only in newly started OpenCode processes.
|
|
399
479
|
- Worker session exports are capped at 64 MiB by default to bound host memory use. The response tool returns `SESSION_EXPORT_TOO_LARGE` rather than loading a larger session.
|
package/bin/orchestration.js
CHANGED
|
@@ -14,10 +14,13 @@ import {
|
|
|
14
14
|
PACKAGE_NAME,
|
|
15
15
|
packageVersion,
|
|
16
16
|
orchestrationOptions,
|
|
17
|
+
reconcileAgentFiles,
|
|
18
|
+
removeAgentFilesManifest,
|
|
17
19
|
restoreBackup,
|
|
18
20
|
status as installationStatus,
|
|
19
21
|
uninstallPackage,
|
|
20
22
|
validateOpenCode,
|
|
23
|
+
writeAgentFilesManifest,
|
|
21
24
|
writePluginConfig,
|
|
22
25
|
} from "../src/installer.js";
|
|
23
26
|
|
|
@@ -29,6 +32,8 @@ const [command, ...flags] = process.argv.slice(2);
|
|
|
29
32
|
|
|
30
33
|
const MODEL_ROLES = [
|
|
31
34
|
["shepherdModel", "Shepherd agents", "OpenCode active model", "model"],
|
|
35
|
+
["sheepdogModel", "Sheepdog agents", "litellm/glm-5.3-flash", "model"],
|
|
36
|
+
["sheepdogVariant", "Sheepdog reasoning effort", "model default", "variant"],
|
|
32
37
|
["workerModel", "Sheep worker agents", "litellm/glm-5.3-flash", "model"],
|
|
33
38
|
["workerVariant", "Sheep worker reasoning effort", "model default", "variant"],
|
|
34
39
|
["reviewerModel", "Shearer review agents", "litellm-responses/gpt-5.6-terra", "model"],
|
|
@@ -98,6 +103,15 @@ async function promptForModels(configDir) {
|
|
|
98
103
|
return answers;
|
|
99
104
|
}
|
|
100
105
|
|
|
106
|
+
function reportReconciliation(reconciliation) {
|
|
107
|
+
for (const relPath of reconciliation.deleted) {
|
|
108
|
+
process.stdout.write(`Removed obsolete package-owned agent file: ${relPath}\n`);
|
|
109
|
+
}
|
|
110
|
+
for (const item of reconciliation.remediation) {
|
|
111
|
+
process.stdout.write(`Action required (${item.reason}): ${item.remediation}\n`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
101
115
|
async function installOrUpdate(useLatest) {
|
|
102
116
|
const configDir = configDirectory();
|
|
103
117
|
const models = command === "install" && process.stdin.isTTY && process.stdout.isTTY
|
|
@@ -112,8 +126,11 @@ async function installOrUpdate(useLatest) {
|
|
|
112
126
|
restoreBackup(result.file, result.backup, result.existed);
|
|
113
127
|
throw new Error(`OpenCode validation failed; restored the previous config. ${error.message}`);
|
|
114
128
|
}
|
|
129
|
+
const reconciliation = reconcileAgentFiles(configDir, { remove: false });
|
|
130
|
+
writeAgentFilesManifest(configDir, reconciliation.manifestEntries, version);
|
|
115
131
|
if (flags.includes("--with-hooks")) installHooks();
|
|
116
132
|
process.stdout.write(`Configured ${PACKAGE_NAME}@${version} in ${result.file}\n`);
|
|
133
|
+
reportReconciliation(reconciliation);
|
|
117
134
|
if (result.backup) process.stdout.write(`Backup: ${result.backup}\n`);
|
|
118
135
|
for (const backup of packageBackups) process.stdout.write(`Backup: ${backup}\n`);
|
|
119
136
|
process.stdout.write("Restart OpenCode intentionally to load the new configuration.\n");
|
|
@@ -141,8 +158,14 @@ function uninstallOrchestration() {
|
|
|
141
158
|
const configDir = configDirectory();
|
|
142
159
|
const result = writePluginConfig(configDir, true);
|
|
143
160
|
const packageBackups = uninstallPackage(configDir);
|
|
161
|
+
const reconciliation = reconcileAgentFiles(configDir, { remove: true });
|
|
162
|
+
removeAgentFilesManifest(configDir);
|
|
144
163
|
if (flags.includes("--with-hooks")) uninstallHooks();
|
|
145
164
|
process.stdout.write(`Removed orchestration plugin configuration from ${result.file}\n`);
|
|
165
|
+
for (const relPath of reconciliation.deleted) {
|
|
166
|
+
process.stdout.write(`Removed package-owned agent file: ${relPath}\n`);
|
|
167
|
+
}
|
|
168
|
+
reportReconciliation(reconciliation);
|
|
146
169
|
if (result.backup) process.stdout.write(`Backup: ${result.backup}\n`);
|
|
147
170
|
for (const backup of packageBackups) process.stdout.write(`Backup: ${backup}\n`);
|
|
148
171
|
}
|
package/hooks/pre-push
CHANGED
|
@@ -4,11 +4,11 @@ set -eu
|
|
|
4
4
|
|
|
5
5
|
mode=${SHEPHERD_MODE:-none}
|
|
6
6
|
case "$mode" in
|
|
7
|
-
|
|
8
|
-
echo "opencode-herdr-orchestration: $mode may not push;
|
|
7
|
+
shepherd|sheepdog|grazer|sheep|shearer)
|
|
8
|
+
echo "opencode-herdr-orchestration: $mode may not push; final delivery belongs to the shepherd-governor." >&2
|
|
9
9
|
exit 1
|
|
10
10
|
;;
|
|
11
|
-
|
|
11
|
+
governor)
|
|
12
12
|
;;
|
|
13
13
|
*)
|
|
14
14
|
exit 0
|
|
@@ -17,7 +17,7 @@ esac
|
|
|
17
17
|
|
|
18
18
|
current_branch=$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true)
|
|
19
19
|
if [ -z "$current_branch" ]; then
|
|
20
|
-
echo "opencode-herdr-orchestration: shepherd-
|
|
20
|
+
echo "opencode-herdr-orchestration: shepherd-governor may not push from detached HEAD." >&2
|
|
21
21
|
exit 1
|
|
22
22
|
fi
|
|
23
23
|
|
|
@@ -32,7 +32,7 @@ is_protected() {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
if is_protected "$current_branch"; then
|
|
35
|
-
echo "opencode-herdr-orchestration: shepherd-
|
|
35
|
+
echo "opencode-herdr-orchestration: shepherd-governor may not push protected branch $current_branch." >&2
|
|
36
36
|
exit 1
|
|
37
37
|
fi
|
|
38
38
|
|
|
@@ -41,20 +41,20 @@ while read -r local_ref local_sha remote_ref remote_sha; do
|
|
|
41
41
|
[ -n "${local_ref:-}" ] || continue
|
|
42
42
|
|
|
43
43
|
if [ "$local_sha" = "$zero" ]; then
|
|
44
|
-
echo "opencode-herdr-orchestration: shepherd-
|
|
44
|
+
echo "opencode-herdr-orchestration: shepherd-governor may not delete remote refs." >&2
|
|
45
45
|
exit 1
|
|
46
46
|
fi
|
|
47
47
|
|
|
48
48
|
expected_local="refs/heads/$current_branch"
|
|
49
49
|
expected_remote="refs/heads/$current_branch"
|
|
50
50
|
if [ "$local_ref" != "$expected_local" ] || [ "$remote_ref" != "$expected_remote" ]; then
|
|
51
|
-
echo "opencode-herdr-orchestration: shepherd-
|
|
51
|
+
echo "opencode-herdr-orchestration: shepherd-governor may push only $expected_local to the same remote branch." >&2
|
|
52
52
|
exit 1
|
|
53
53
|
fi
|
|
54
54
|
|
|
55
55
|
remote_branch=${remote_ref#refs/heads/}
|
|
56
56
|
if is_protected "$remote_branch"; then
|
|
57
|
-
echo "opencode-herdr-orchestration: shepherd-
|
|
57
|
+
echo "opencode-herdr-orchestration: shepherd-governor may not update protected branch $remote_branch." >&2
|
|
58
58
|
exit 1
|
|
59
59
|
fi
|
|
60
60
|
done
|