@sorenllm/opencode-forge 0.3.4 → 0.5.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 +185 -166
- package/dist/index.js +706 -1717
- package/package.json +64 -61
package/README.md
CHANGED
|
@@ -33,8 +33,10 @@ OpenSpec spec workflows remain a third, separate lane.
|
|
|
33
33
|
- Plan files: `.opencode/plan/<date>-<slug>.md` in your project, frontmatter
|
|
34
34
|
state machine `draft → approved → done` (exit: `abandoned`).
|
|
35
35
|
- While a plan is in draft, `write` / `edit` / `bash` / `task` are **denied
|
|
36
|
-
at the permission layer** — including your own `allow` config
|
|
37
|
-
|
|
36
|
+
at the permission layer** — including your own `allow` config, and for
|
|
37
|
+
every agent in that session (the draft protects shared session state; see
|
|
38
|
+
Agent partition below). The only exits are approval and discard. This is
|
|
39
|
+
deliberate; see Design stance.
|
|
38
40
|
- `plan_approve` / `plan_close` are pinned to a confirmation dialog: the
|
|
39
41
|
model can never flip the state itself.
|
|
40
42
|
- Goal files: `.opencode/goal/<date>-<slug>.md`, state machine
|
|
@@ -42,9 +44,40 @@ OpenSpec spec workflows remain a third, separate lane.
|
|
|
42
44
|
session plus a workspace queue. Completion is **verified by the plugin**,
|
|
43
45
|
not attested by the model: `goal_complete` re-executes every check itself
|
|
44
46
|
and refuses (fail-closed) on any failure.
|
|
45
|
-
- The native `build` / `plan` agents
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
- The native `build` / `plan` agents **stay installed and independent**:
|
|
48
|
+
forge coexists with them in the Tab cycle. The plugin hard-partitions the
|
|
49
|
+
two worlds (see Agent partition below) instead of hiding anything; plan
|
|
50
|
+
and goal files are never deleted on uninstall.
|
|
51
|
+
|
|
52
|
+
## Agent partition (who sees what)
|
|
53
|
+
|
|
54
|
+
The plugin draws a hard line between two agent families, enforced at the
|
|
55
|
+
tool layer (verified against the host's `tools` filter):
|
|
56
|
+
|
|
57
|
+
| | **forge family** — `forge` + your forge.json `forge-*` workers | **everyone else** — native `build` / `plan` / `general` / `explore` and your own agents |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| exec surface | `forge_shell` / `forge_jobs` only; builtin `shell`/`bash` hidden (unconditional, probe-independent; covers user-defined `agent.forge` entries too) | builtin `shell`/`bash`, fully native |
|
|
60
|
+
| forge tools (`plan_*`, `goal_*`, `crew_*`, `forge_shell`, `forge_jobs`) | `forge` sees all; `forge-*` workers only `forge_shell` + `forge_jobs` | none visible |
|
|
61
|
+
| dispatching `forge-*` via `task` | allowed | refused at the tool layer |
|
|
62
|
+
| writing `.opencode/plan/` / `.opencode/goal/` | allowed (state machine governs) | refused at the tool layer (reads stay open) |
|
|
63
|
+
| forge system prompts (job guidance, plan/goal notices) | injected | never injected — zero forge text |
|
|
64
|
+
| watchdog (stuck builtin shell) | governed | untouched — no markers, no kills |
|
|
65
|
+
|
|
66
|
+
Escape hatches: `jobs.keepBuiltinShell: true` gives the forge family the
|
|
67
|
+
builtin shell back; `jobs.mode: "native"` retires `forge_shell`/`forge_jobs`
|
|
68
|
+
entirely (the hide is withdrawn in the same stroke). Mixed sessions (Tab
|
|
69
|
+
between agents in one session): harness state (plans/goals) stays
|
|
70
|
+
session-bound, tool surfaces follow the current speaker, and the goal loop
|
|
71
|
+
parks instead of driving a non-forge turn. One deliberate exception: a
|
|
72
|
+
draft-phase write ban binds the **session** (protecting shared state), so a
|
|
73
|
+
non-forge agent is write-blocked until the draft is approved or discarded in
|
|
74
|
+
forge. Running `/plan` `/goal` `/crew` under a non-forge agent redirects to
|
|
75
|
+
forge instead of executing.
|
|
76
|
+
|
|
77
|
+
**Default subject.** The host otherwise defaults new sessions to `build`
|
|
78
|
+
once a build agent exists — the plugin pins `default_agent: "forge"`
|
|
79
|
+
automatically (a `default_agent` you set yourself always wins), so bare
|
|
80
|
+
runs and fresh sessions still land on forge under coexistence.
|
|
48
81
|
|
|
49
82
|
## Install
|
|
50
83
|
|
|
@@ -78,18 +111,19 @@ the plugin never writes them):
|
|
|
78
111
|
"forge": {
|
|
79
112
|
"model": "provider/model", // pick any model for forge
|
|
80
113
|
"disable": true // one-knob return to native: no forge,
|
|
81
|
-
//
|
|
114
|
+
// no injections at all, no tools/commands
|
|
82
115
|
}
|
|
83
116
|
},
|
|
84
117
|
// per-plugin options ride the plugin entry as a [spec, options] tuple
|
|
85
118
|
"plugin": [
|
|
86
119
|
["@sorenllm/opencode-forge", {
|
|
87
120
|
"jobs": {
|
|
88
|
-
"mode": "auto", // "auto" (default) | "forge"
|
|
89
|
-
|
|
121
|
+
"mode": "auto", // "auto" (default) | "forge" — equivalent, both keep the partition
|
|
122
|
+
// | "native" — retire the supervisor, builtin shell returns
|
|
123
|
+
"keepBuiltinShell": false // true: give the forge family the builtin shell back (the one exec escape hatch)
|
|
90
124
|
},
|
|
91
125
|
"watchdog": {
|
|
92
|
-
"mode": "kill", // "kill" (default) | "dry-run" | "off"
|
|
126
|
+
"mode": "kill", // "kill" (default) | "dry-run" | "off" — forge-family sessions only
|
|
93
127
|
"stallMs": 600000 // stall threshold, min 60000
|
|
94
128
|
}
|
|
95
129
|
}]
|
|
@@ -110,12 +144,14 @@ and the plugin's command of that name is not registered.
|
|
|
110
144
|
`~/.cache/opencode/packages/github_ChengZiiii/opencode-forge/`).
|
|
111
145
|
3. Delete the `agent["forge"]` block from your config if you added one
|
|
112
146
|
(otherwise the name lingers in the agent list).
|
|
113
|
-
4. Done —
|
|
147
|
+
4. Done — all runtime injections (agents, tool hiding, commands) are gone
|
|
148
|
+
with the plugin; nothing was ever written to your config
|
|
114
149
|
(the hide was runtime-only). Your `.opencode/plan/` and `.opencode/goal/`
|
|
115
150
|
files are yours; delete them yourself if you want.
|
|
116
|
-
5. Optional runtime debris: delete `<tmp>/opencode-forge/` (job logs, the
|
|
117
|
-
|
|
118
|
-
|
|
151
|
+
5. Optional runtime debris: delete `<tmp>/opencode-forge/` (job logs, the
|
|
152
|
+
job registry ledger, the watchdog ledger, and any leftover dispatch
|
|
153
|
+
ledger from older versions — the file ledger table above lists
|
|
154
|
+
everything).
|
|
119
155
|
|
|
120
156
|
## File ledger
|
|
121
157
|
|
|
@@ -125,13 +161,13 @@ What this plugin touches, exhaustively:
|
|
|
125
161
|
| --- | --- | --- |
|
|
126
162
|
| `<project>/.opencode/plan/*.md` | plan files | user data — kept forever, uninstall never deletes |
|
|
127
163
|
| `<project>/.opencode/goal/*.md` | goal files (contract, Check Log, Turn Ledger) | user data — kept forever, uninstall never deletes |
|
|
128
|
-
| merged config object (RAM only) | forge agent, native build/plan
|
|
164
|
+
| merged config object (RAM only) | forge agent, static `forge-<id>` subagents, tool-partition injections (forge family shell hide, non-forge forge-tool hide, native build/plan/general/explore minimal entries), `command.plan`, `command.goal`, `command.crew`, goal permission keys, `permission.forge_shell`, `permission.crew_close` | vanishes when the plugin is removed; nothing is written to disk |
|
|
129
165
|
| `<tmp>/opencode-forge/jobs/<jobId>.log` | job output tee (full output; oldest rotated out above 50 files) | runtime debris — delete freely, also after uninstall |
|
|
130
166
|
| `<tmp>/opencode-forge/jobs/ledger.jsonl` | job registry ledger (bounded: 1 MB reset, 200 entries) | runtime debris — delete freely, also after uninstall |
|
|
131
167
|
| `<tmp>/opencode-forge/jobs/registry.json` | persistent survivor registry (bounded: 100 entries) | runtime debris — after uninstall, kill any still-running `survive` jobs yourself first |
|
|
132
168
|
| `<tmp>/opencode-forge/watchdog/log.jsonl` | watchdog interventions ledger (bounded: 200 entries, oldest rotated) | runtime debris — delete freely, also after uninstall |
|
|
133
|
-
| `<tmp>/opencode-forge/dispatch/ledger.jsonl` | dispatch ledger
|
|
134
|
-
| `<project>/.opencode/forge.json`, `~/.config/opencode/forge.json` | your
|
|
169
|
+
| `<tmp>/opencode-forge/dispatch/ledger.jsonl` | dispatch ledger — **no longer written** (the dispatch engine was removed); only present as inert debris from older versions | runtime debris — delete freely |
|
|
170
|
+
| `<project>/.opencode/forge.json`, `~/.config/opencode/forge.json` | your static subagent definitions (JSONC). **User data — the plugin only reads it, never writes or migrates it** | yours — version the project one, keep the global one out of sync tools if it holds machine-specific models |
|
|
135
171
|
| `~/.cache/opencode/packages/...` | installed package copy | written by the `opencode plugin` installer, not the plugin |
|
|
136
172
|
| `~/.config/opencode/opencode.json` | `plugin` array entry | written by the installer |
|
|
137
173
|
|
|
@@ -292,19 +328,20 @@ as usual. Config `jobs.survive: "always"` flips the per-call default; config
|
|
|
292
328
|
whose pid died are detected at next start, ledgered as orphans, and dropped
|
|
293
329
|
from the registry. Stop survivors explicitly — nothing else will.
|
|
294
330
|
|
|
295
|
-
###
|
|
331
|
+
### Mode matrix (supervisor lifecycle)
|
|
296
332
|
|
|
297
|
-
|
|
298
|
-
|
|
333
|
+
The old three-stage capability probe is retired: the forge-side partition
|
|
334
|
+
holds regardless of what the host's builtin shell can do, so `auto` and
|
|
335
|
+
`forge` are equivalent. Only the manual `native` mode retires the
|
|
336
|
+
supervisor:
|
|
299
337
|
|
|
300
|
-
|
|
|
338
|
+
| mode | how you get there | builtin shell (forge family) | forge_shell / forge_jobs |
|
|
301
339
|
| --- | --- | --- | --- |
|
|
302
|
-
|
|
|
303
|
-
|
|
|
304
|
-
| 2 — native confirmed complete | manual only: `jobs.mode: "native"` (never auto-detected — completeness is a semantic judgement) | visible | retired; calls throw with a pointer to the native parameter |
|
|
340
|
+
| auto (default) / forge | nothing to do | hidden — the exec surface is forge_shell, always | registered |
|
|
341
|
+
| native | manual only: `jobs.mode: "native"` | restored | retired; calls throw with a pointer to the native parameter |
|
|
305
342
|
|
|
306
|
-
|
|
307
|
-
|
|
343
|
+
`jobs.keepBuiltinShell: true` gives the forge family the builtin shell back
|
|
344
|
+
in any mode (the watchdog keeps governing those calls).
|
|
308
345
|
|
|
309
346
|
### Artifacts and uninstall additions
|
|
310
347
|
|
|
@@ -319,163 +356,144 @@ These are runtime debris, not data — with ONE caveat: **if you uninstall with
|
|
|
319
356
|
/F /T`, or just reboot); deleting the directory afterwards is safe and
|
|
320
357
|
complete.
|
|
321
358
|
|
|
322
|
-
##
|
|
359
|
+
## Forge subagents (static worker agents in forge.json)
|
|
360
|
+
|
|
361
|
+
Forge subagents are **static, user-authored worker agents**: each forge.json
|
|
362
|
+
entry pins a model and an optional reasoning depth, materializes as a native
|
|
363
|
+
`forge-<id>` subagent, and is dispatched through the host's **native `task`
|
|
364
|
+
tool** — which means every run is visible in the TUI, expandable and
|
|
365
|
+
monitorable, exactly like any native subagent. There is no dispatch tool, no
|
|
366
|
+
child-session engine, and no configuration invitation anywhere: the session
|
|
367
|
+
AI never writes your config.
|
|
323
368
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
result is an honest report: the actual model/depth used, real token counts, a
|
|
327
|
-
self-computed cost (`null` when the model is unpriced), the
|
|
328
|
-
`depthTranslation` disclosure (`canonical high → native XHigh`, or
|
|
329
|
-
`verbatim`), and the worker's concluding report.
|
|
369
|
+
No forge.json? Nothing happens — the plugin registers nothing and the native
|
|
370
|
+
subagent behavior is untouched. Onboarding is this README, for you the human.
|
|
330
371
|
|
|
331
|
-
### forge.json — the
|
|
372
|
+
### forge.json — the configuration
|
|
332
373
|
|
|
333
|
-
|
|
334
|
-
|
|
374
|
+
A dedicated `forge.json` (JSONC — comments allowed), resolved by a two-level
|
|
375
|
+
cascade with a single winning source (never merged):
|
|
335
376
|
|
|
336
377
|
1. `<project>/.opencode/forge.json` — project-level, versionable, team-shared
|
|
337
378
|
2. `~/.config/opencode/forge.json` — global
|
|
338
|
-
3. the built-in seed — `research {depths: [low, medium]}` and
|
|
339
|
-
`review {depths: [medium, high, max]}`, both pinned to the placeholder
|
|
340
|
-
`Local/GPT Luna` (an identity that never resolves: an unconfigured install
|
|
341
|
-
fails every dispatch with the configuration recipe below)
|
|
342
379
|
|
|
343
380
|
```jsonc
|
|
344
381
|
{
|
|
345
|
-
// forge
|
|
382
|
+
// forge subagents — definitions; see the restart table below for apply timing
|
|
346
383
|
"agents": {
|
|
347
384
|
"research": {
|
|
348
|
-
"model": "zai-coding-plan/glm-5.3", // exact "provider/model" string
|
|
349
|
-
"
|
|
350
|
-
// "prompt": "optional role prompt
|
|
351
|
-
// "shape": "write",
|
|
352
|
-
// "permission": { "bash": "deny" } // optional override; "task" is
|
|
385
|
+
"model": "zai-coding-plan/glm-5.3", // exact "provider/model" string — REQUIRED
|
|
386
|
+
"thoughtLevel": "low", // optional: none/low/medium/high/max or a native level name
|
|
387
|
+
// "prompt": "optional role prompt; built-in research/review defaults exist",
|
|
388
|
+
// "shape": "write", // default readonly denies write/edit/bash
|
|
389
|
+
// "permission": { "bash": "deny" } // optional override; "task" is ALWAYS denied
|
|
353
390
|
}
|
|
354
391
|
}
|
|
355
392
|
}
|
|
356
393
|
```
|
|
357
394
|
|
|
358
395
|
- **The plugin only ever reads this file.** It never creates, writes, or
|
|
359
|
-
migrates it.
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
`
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
the
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
- **Keyless endpoints × readonly agents**: free/keyless providers can return
|
|
452
|
-
a deterministic EMPTY response for any toolset-reduced (readonly) agent —
|
|
453
|
-
0 tokens, no parts. The plugin detects this and fails the dispatch honestly
|
|
454
|
-
(`empty-response`, never counted as success); use a `shape: "write"` agent
|
|
455
|
-
or a keyed provider for readonly work on such endpoints.
|
|
456
|
-
- **`opencode run` and background**: a finished run-mode session cannot be
|
|
457
|
-
re-prompted, so completion briefs have nowhere to land — completions are
|
|
458
|
-
ledger-only (read them via `forge_dispatch_list`). Background mode targets
|
|
459
|
-
live TUI sessions; prefer sync under `opencode run`.
|
|
460
|
-
- **Host tool ceiling (~262s observed on 1.18.32)**: the host may kill a
|
|
461
|
-
sync tool call that exceeds roughly 262 seconds even though the plugin's
|
|
462
|
-
own deadline is higher. A same-family transport abort (~281s observed,
|
|
463
|
-
2026-09-27 incident) can also kill the background dispatch's turn POST.
|
|
464
|
-
Transport-interruption recovery (above) absorbs both: the dispatch falls
|
|
465
|
-
back to polling and still delivers the child's result; keep `timeoutMs`
|
|
466
|
-
modest anyway for sync-heavy workflows.
|
|
467
|
-
- **Child permission asks**: a worker that hits a permission ask surfaces the
|
|
468
|
-
ask on the CHILD's session in the TUI. An unanswered ask stalls the child
|
|
469
|
-
until the dispatch deadline — scope worker prompts to avoid permission
|
|
470
|
-
boundaries (readonly tiers and workspace-relative paths help).
|
|
396
|
+
migrates it. The old AI-authored onboarding path (seed placeholder +
|
|
397
|
+
error recipe) is gone: an unconfigured host is inert and silent, and the
|
|
398
|
+
session AI is never invited to touch config. The ONE exception is the
|
|
399
|
+
`/crew` initialization gate (below), where AI-assisted configuration is
|
|
400
|
+
offered to YOU and happens only on your explicit go-ahead.
|
|
401
|
+
- **Parsing is field-level fail-soft** (ZCode-style): a semantically bad
|
|
402
|
+
agent entry is skipped with an error finding while its siblings apply; a
|
|
403
|
+
mistyped optional field is ignored with a warning; a syntactically broken
|
|
404
|
+
file empties the agent set with one parse-location error. There is no seed
|
|
405
|
+
in any failure path. Findings surface in the plugin diagnostics log.
|
|
406
|
+
- **`depths` is deprecated**: the array is ignored with a warning finding.
|
|
407
|
+
Pin ONE `thoughtLevel` per agent instead.
|
|
408
|
+
- Each agent materializes as `forge-<id>` — a subagent-mode agent visible in
|
|
409
|
+
the task tool's vocabulary (never hidden), carrying the pinned `model`,
|
|
410
|
+
the worker discipline (workspace-relative paths only, verbatim reporting
|
|
411
|
+
of tool refusals, evidence-bearing conclusions) around its role prompt,
|
|
412
|
+
and a deny-style permission: `shape: "readonly"` (the default) denies
|
|
413
|
+
`write`/`edit`/`bash`, `shape: "write"` allows them, an explicit
|
|
414
|
+
`permission` map overrides the shape default — and `task` is ALWAYS
|
|
415
|
+
denied (recursive spawning stays physically impossible).
|
|
416
|
+
|
|
417
|
+
**What applies when** — edits take hold per field:
|
|
418
|
+
|
|
419
|
+
| change | needs a host restart? |
|
|
420
|
+
| --- | --- |
|
|
421
|
+
| `thoughtLevel` on an existing agent | no (applies to NEW sessions of that agent; running sessions keep their frozen depth) |
|
|
422
|
+
| agent added / removed, or `model` / `prompt` / `permission` changed | yes (materialization runs on the config hook) |
|
|
423
|
+
|
|
424
|
+
### Dispatching
|
|
425
|
+
|
|
426
|
+
Say "dispatch a subagent..." in a forge session and the model picks from the
|
|
427
|
+
task tool vocabulary: a configured `forge-*` agent whose description matches
|
|
428
|
+
runs on its pinned brain; if none matches, forge is instructed to say so
|
|
429
|
+
plainly and fall back to the native task channel. During a plan draft the
|
|
430
|
+
native `task` tool is already denied by the plan harness — no plugin-side
|
|
431
|
+
rule needed.
|
|
432
|
+
|
|
433
|
+
**Known pitfall — keyless endpoints × readonly agents**: free/keyless
|
|
434
|
+
providers can return a deterministic EMPTY response for any toolset-reduced
|
|
435
|
+
(readonly) agent. On the native task channel this now surfaces as a visible,
|
|
436
|
+
attributable task failure; the mitigation is unchanged — use
|
|
437
|
+
`shape: "write"` for such endpoints, or a keyed provider.
|
|
438
|
+
|
|
439
|
+
### The thoughtLevel word
|
|
440
|
+
|
|
441
|
+
One word per agent: the canonical `none | low | medium | high | max`, or the
|
|
442
|
+
model's native level name verbatim (e.g. Qwen's `XHigh`). Injection rides the
|
|
443
|
+
`chat.params` hook keyed by agent name, translated per provider family:
|
|
444
|
+
effort-style (OpenAI-compatible) receives `reasoningEffort = <word>`;
|
|
445
|
+
budget-style (Anthropic-style) receives a published thinking tier
|
|
446
|
+
(`low → 8192`, `medium → 16384`, `high → 24576`, `max → 32768`,
|
|
447
|
+
`none → off`); toggle-style (zai/GLM) receives thinking on/off. Rules:
|
|
448
|
+
verbatim-first, no interpolation, unknown family injects nothing. A word the
|
|
449
|
+
model does not natively offer injects NOTHING and records one finding naming
|
|
450
|
+
both vocabularies — the session is never broken by a config word. A
|
|
451
|
+
session's depth is frozen at its first request (editing forge.json
|
|
452
|
+
mid-session never rewrites a running session).
|
|
453
|
+
|
|
454
|
+
## Crew workflow (`/crew`)
|
|
455
|
+
|
|
456
|
+
`/crew <objective>` is the third state-activation command next to `/plan` and
|
|
457
|
+
`/goal`: it enters crew orchestration discipline for the session. The flow:
|
|
458
|
+
|
|
459
|
+
1. **Register** the declared plan: `crew_begin {objective, subtasks}` — one
|
|
460
|
+
titled subtask each, optionally naming the intended `forge-*` agent.
|
|
461
|
+
2. **Execute in waves** of parallel native `task` calls (results return
|
|
462
|
+
in-turn; the next wave launches only after the previous wave's results).
|
|
463
|
+
3. **Missing role?** Run the subtask through a native task call anyway and
|
|
464
|
+
tell the user — suggest configuring the missing role.
|
|
465
|
+
4. **Verify** each subtask against its acceptance-evidence statement;
|
|
466
|
+
retry a failure at most once.
|
|
467
|
+
5. **Close** with `crew_close` — a hard, ask-gated gate cross-checking the
|
|
468
|
+
report against the DECLARED plan: every declared subtask needs a
|
|
469
|
+
PASS/FAIL verdict with evidence; an undeclared subtask in the report
|
|
470
|
+
refuses the close (fold discoveries into an existing verdict, or discard
|
|
471
|
+
and re-crew); a FAIL needs both failure reports. The close output is the
|
|
472
|
+
crew's record — crew state is in-memory and a host restart ends it
|
|
473
|
+
honestly.
|
|
474
|
+
|
|
475
|
+
**Not initialized?** `/crew` on a host with zero usable forge agents refuses
|
|
476
|
+
with one-time setup guidance: the two file paths, a copy-paste template, and
|
|
477
|
+
the choice of configuring it yourself OR having the session AI do it — the
|
|
478
|
+
latter only on your explicit go-ahead in that conversation, through the
|
|
479
|
+
normal visible write path. This is the plugin's only configuration
|
|
480
|
+
invitation. After saving the file, restart opencode to materialize the
|
|
481
|
+
agents.
|
|
482
|
+
|
|
483
|
+
### Debris note
|
|
484
|
+
|
|
485
|
+
The old dispatch suite's ledger at `<tmp>/opencode-forge/dispatch/` is no
|
|
486
|
+
longer written. If it exists on your machine it is inert runtime debris —
|
|
487
|
+
delete the directory freely, also after uninstalling.
|
|
471
488
|
|
|
472
489
|
## Hang watchdog (a stuck builtin shell unblocks itself)
|
|
473
490
|
|
|
474
491
|
`forge_shell` is structurally immune to the stdio-EOF hang, but the builtin
|
|
475
|
-
`shell`/`bash` tool can still hang outside it —
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
492
|
+
`shell`/`bash` tool can still hang outside it — under `jobs.mode: "native"`
|
|
493
|
+
or `jobs.keepBuiltinShell: true`, forge-family sessions carry it again. The
|
|
494
|
+
watchdog is the independent backstop for those paths, in **forge-family
|
|
495
|
+
sessions only** (primary and delegated forge-* workers alike) — non-forge
|
|
496
|
+
agents' builtin shells are entirely native: no markers, no timing, no kills:
|
|
479
497
|
|
|
480
498
|
1. The host's shell environment hook stamps each builtin shell call's
|
|
481
499
|
process with a plugin-namespaced marker (`FORGE_WATCHDOG_MARK`), and the
|
|
@@ -538,11 +556,12 @@ read/grep/glob; if you genuinely need a shell command to decide the plan,
|
|
|
538
556
|
approve the plan first (revising after approval is allowed via a new `/plan`).
|
|
539
557
|
The escape hatches are `plan_approve` and `/plan discard`, by design.
|
|
540
558
|
|
|
541
|
-
Separately,
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
`
|
|
545
|
-
|
|
559
|
+
Separately, the builtin shell is hidden on the forge agent on purpose —
|
|
560
|
+
`forge_shell` is the exec surface there (see Agent partition and the job
|
|
561
|
+
supervisor chapter above). The hide is unconditional: it covers a
|
|
562
|
+
**user-defined** `agent.forge` entry too. The escape hatches are
|
|
563
|
+
`jobs.keepBuiltinShell: true` (builtin shell back on the forge family) and
|
|
564
|
+
`jobs.mode: "native"` (supervisor retired entirely).
|
|
546
565
|
|
|
547
566
|
A process restart forgets the session binding: the write-ban soft-disables
|
|
548
567
|
(safety over strictness) and the next session's system notice + `/plan
|