@sorenllm/opencode-forge 0.3.2 → 0.3.3
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 +140 -2
- package/dist/index.js +2196 -202
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -114,8 +114,8 @@ and the plugin's command of that name is not registered.
|
|
|
114
114
|
(the hide was runtime-only). Your `.opencode/plan/` and `.opencode/goal/`
|
|
115
115
|
files are yours; delete them yourself if you want.
|
|
116
116
|
5. Optional runtime debris: delete `<tmp>/opencode-forge/` (job logs, the
|
|
117
|
-
job registry ledger,
|
|
118
|
-
above lists everything).
|
|
117
|
+
job registry ledger, the watchdog ledger, and the dispatch ledger — the
|
|
118
|
+
file ledger table above lists everything).
|
|
119
119
|
|
|
120
120
|
## File ledger
|
|
121
121
|
|
|
@@ -130,6 +130,8 @@ What this plugin touches, exhaustively:
|
|
|
130
130
|
| `<tmp>/opencode-forge/jobs/ledger.jsonl` | job registry ledger (bounded: 1 MB reset, 200 entries) | runtime debris — delete freely, also after uninstall |
|
|
131
131
|
| `<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
132
|
| `<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: resolved identity/depth, outcomes, tokens/cost, timeouts, lost-on-exit, crew summaries (bounded, capped rotation) | runtime debris — delete freely, also after uninstall |
|
|
134
|
+
| `<project>/.opencode/forge.json`, `~/.config/opencode/forge.json` | your dispatch agent 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 |
|
|
133
135
|
| `~/.cache/opencode/packages/...` | installed package copy | written by the `opencode plugin` installer, not the plugin |
|
|
134
136
|
| `~/.config/opencode/opencode.json` | `plugin` array entry | written by the installer |
|
|
135
137
|
|
|
@@ -317,6 +319,142 @@ These are runtime debris, not data — with ONE caveat: **if you uninstall with
|
|
|
317
319
|
/F /T`, or just reboot); deleting the directory afterwards is safe and
|
|
318
320
|
complete.
|
|
319
321
|
|
|
322
|
+
## Worker dispatch (scoped subagents on a chosen model + depth)
|
|
323
|
+
|
|
324
|
+
`forge_dispatch {prompt, agent, depth}` spawns a child session that runs one
|
|
325
|
+
scoped task on the agent's pinned model at the requested reasoning depth. The
|
|
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.
|
|
330
|
+
|
|
331
|
+
### forge.json — the dispatch configuration
|
|
332
|
+
|
|
333
|
+
Agents live in a dedicated `forge.json` (JSONC — comments allowed), resolved
|
|
334
|
+
by a three-level cascade with a single winning source (never merged):
|
|
335
|
+
|
|
336
|
+
1. `<project>/.opencode/forge.json` — project-level, versionable, team-shared
|
|
337
|
+
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
|
+
|
|
343
|
+
```jsonc
|
|
344
|
+
{
|
|
345
|
+
// forge dispatch agents — save the file; changes apply on the next dispatch, no restart
|
|
346
|
+
"agents": {
|
|
347
|
+
"research": {
|
|
348
|
+
"model": "zai-coding-plan/glm-5.3", // exact "provider/model" string
|
|
349
|
+
"depths": ["low", "medium"], // first entry is the default
|
|
350
|
+
// "prompt": "optional role prompt riding inside the discipline wrapper",
|
|
351
|
+
// "shape": "write", // default readonly denies mutating tools
|
|
352
|
+
// "permission": { "bash": "deny" } // optional override; "task" is always denied
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
- **The plugin only ever reads this file.** It never creates, writes, or
|
|
359
|
+
migrates it. Unconfigured? The first `forge_dispatch` error carries a
|
|
360
|
+
machine-actionable recipe (detected identities, both file paths, the
|
|
361
|
+
template, a verify dispatch) — or just ask your session AI to configure
|
|
362
|
+
dispatch; `forge_dispatch_config` shows the exact live state.
|
|
363
|
+
- **Hot-apply**: the file is re-read (mtime-cached) before every dispatch and
|
|
364
|
+
on every `forge_dispatch_config` call — edits take effect on the next
|
|
365
|
+
dispatch, no host restart. One boundary: newly added agents materialize
|
|
366
|
+
their hidden `forge-<agent>` entry only on the next config reload; the
|
|
367
|
+
dispatch itself still works (the body names the agent), but a brand-new
|
|
368
|
+
agent's first dispatch may need a host reload if the host validates agent
|
|
369
|
+
names on session create.
|
|
370
|
+
- Each agent materializes as a hidden `mode: subagent` agent `forge-<agent>`
|
|
371
|
+
carrying the role prompt (explicit `prompt` > built-in research/review
|
|
372
|
+
defaults > generic worker) and a deny-style permission: `shape: "readonly"`
|
|
373
|
+
(the default) denies `write`/`edit`/`bash`, `shape: "write"` allows them,
|
|
374
|
+
an explicit `permission` map overrides the shape default — and `task` is
|
|
375
|
+
ALWAYS denied (recursive dispatch stays physically impossible). Agents
|
|
376
|
+
never carry a `model` field; the brain is bound per dispatch.
|
|
377
|
+
|
|
378
|
+
### The depth metalanguage
|
|
379
|
+
|
|
380
|
+
`depth` comes from the canonical five-word set `none | low | medium | high |
|
|
381
|
+
max`, or the model's **native level names verbatim** (escape hatch — e.g.
|
|
382
|
+
Qwen's `XHigh`). Translation to the provider's native parameter follows three
|
|
383
|
+
hard rules:
|
|
384
|
+
|
|
385
|
+
- **verbatim-first**: a meta word the model natively offers passes as-is
|
|
386
|
+
- **no interpolation**: a meta word with no counterpart errors listing both
|
|
387
|
+
vocabularies — never a nearest guess
|
|
388
|
+
- **full disclosure**: the report always shows `canonical → native`
|
|
389
|
+
|
|
390
|
+
Per family: effort-style providers (OpenAI-compatible) receive
|
|
391
|
+
`reasoningEffort = <word>`; budget-style (Anthropic-style) receive a
|
|
392
|
+
published thinking tier — `low → 8192`, `medium → 16384`, `high → 24576`,
|
|
393
|
+
`max → 32768` budget tokens, `none → thinking off`; toggle-style (zai/GLM)
|
|
394
|
+
receive thinking on/off (that shape has no level slot — disclosed honestly).
|
|
395
|
+
A provider whose option shape the plugin does not know gets nothing injected,
|
|
396
|
+
disclosed as `not injected (unknown provider shape)`. The provider is the
|
|
397
|
+
final judge of every depth word: its raw errors flow back unedited.
|
|
398
|
+
|
|
399
|
+
### Legacy inline roster (advanced)
|
|
400
|
+
|
|
401
|
+
The pre-forge.json inline form still works when configured (the plugin
|
|
402
|
+
options `dispatch.roster` / `dispatch.tiers`): tier ids it defines keep their
|
|
403
|
+
exact-match resolution, one-retry-excluding-a-failed-identity semantics, and
|
|
404
|
+
startup dead-key warnings. Legacy tier ids win over forge.json agents of the
|
|
405
|
+
same name while inline options exist. Zero-config auto-generation of roster
|
|
406
|
+
entries was removed (configured ≠ usable) — dispatch without configuration is
|
|
407
|
+
the seed + recipe path above.
|
|
408
|
+
|
|
409
|
+
### Background dispatch and wake briefs
|
|
410
|
+
|
|
411
|
+
`forge_dispatch` takes `background: true`: it returns
|
|
412
|
+
`{dispatchId, agent, requested, resolved, depth, queuedAt}` immediately and
|
|
413
|
+
the full result arrives later as a coalesced `[forge:dispatch-complete]`
|
|
414
|
+
brief — debounced, never interrupting an active turn (delivery waits for an
|
|
415
|
+
idle), each terminal delivered exactly once, and merged into a single
|
|
416
|
+
combined re-prompt when a goal continuation fires on the same idle.
|
|
417
|
+
`forge_dispatch_list` recovers in-flight and recent terminal state (with
|
|
418
|
+
dispatchIds) after context compaction; `forge_dispatch_kill {dispatchId}`
|
|
419
|
+
stops polling, suppresses the brief, and ledgers `killed`. On host exit,
|
|
420
|
+
in-flight dispatches are ledgered `lost-on-exit` — sessions are host memory
|
|
421
|
+
objects, there are no orphan processes and no survive semantics.
|
|
422
|
+
|
|
423
|
+
### Crew workflow (`/crew`)
|
|
424
|
+
|
|
425
|
+
`/crew <objective>` registers a crew: decompose the objective into
|
|
426
|
+
evidence-checked subtasks, dispatch them as background waves paced on
|
|
427
|
+
completion briefs (**never past the concurrency cap — launch the next batch
|
|
428
|
+
only as briefs free capacity**), at most ONE retry per failed subtask, then
|
|
429
|
+
`crew_close` with the full report. The close is a hard gate: every subtask
|
|
430
|
+
needs a PASS/FAIL verdict with evidence, a FAIL needs both failure reports,
|
|
431
|
+
and the report is cross-checked against the dispatch ledger — a dispatched
|
|
432
|
+
subtask missing from the report refuses the close (refusals never bother you
|
|
433
|
+
with a dialog). A valid close asks once, appends a crew summary to the
|
|
434
|
+
ledger, and ends the crew. Crew state is in-memory by design: a host restart
|
|
435
|
+
ends it honestly and the ledger keeps the history.
|
|
436
|
+
|
|
437
|
+
### Known pitfalls
|
|
438
|
+
|
|
439
|
+
- **Keyless endpoints × readonly agents**: free/keyless providers can return
|
|
440
|
+
a deterministic EMPTY response for any toolset-reduced (readonly) agent —
|
|
441
|
+
0 tokens, no parts. The plugin detects this and fails the dispatch honestly
|
|
442
|
+
(`empty-response`, never counted as success); use a `shape: "write"` agent
|
|
443
|
+
or a keyed provider for readonly work on such endpoints.
|
|
444
|
+
- **`opencode run` and background**: a finished run-mode session cannot be
|
|
445
|
+
re-prompted, so completion briefs have nowhere to land — completions are
|
|
446
|
+
ledger-only (read them via `forge_dispatch_list`). Background mode targets
|
|
447
|
+
live TUI sessions; prefer sync under `opencode run`.
|
|
448
|
+
- **Host tool ceiling (~262s observed on 1.18.32)**: the host may kill a
|
|
449
|
+
sync tool call that exceeds roughly 262 seconds even though the plugin's
|
|
450
|
+
own deadline is higher. Keep `timeoutMs` at or below ~240000 for
|
|
451
|
+
sync-heavy workflows; a killed call still leaves the child session
|
|
452
|
+
reclaimable via `forge_dispatch_list` / the host API.
|
|
453
|
+
- **Child permission asks**: a worker that hits a permission ask surfaces the
|
|
454
|
+
ask on the CHILD's session in the TUI. An unanswered ask stalls the child
|
|
455
|
+
until the dispatch deadline — scope worker prompts to avoid permission
|
|
456
|
+
boundaries (readonly tiers and workspace-relative paths help).
|
|
457
|
+
|
|
320
458
|
## Hang watchdog (a stuck builtin shell unblocks itself)
|
|
321
459
|
|
|
322
460
|
`forge_shell` is structurally immune to the stdio-EOF hang, but the builtin
|