@bojackduy/opencode-loopd 1.10.4 → 1.10.5

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@bojackduy/opencode-loopd",
4
- "version": "1.10.4",
4
+ "version": "1.10.5",
5
5
  "description": "Codex-inspired background goal engine for OpenCode — autonomous subagents, engine-driven loop, child worker sessions and modal TUI dashboard. Like Claude Code loop for OpenCode.",
6
6
  "type": "module",
7
7
  "license": "AGPL-3.0-or-later",
@@ -60,8 +60,8 @@ Every goal has an **immutable contract** at creation — the source of truth for
60
60
 
61
61
  * **Objective** — semantic requirements (free text, self-contained). The worker derives concrete requirements from it.
62
62
  * **Checks** — deterministic shell commands that **must pass** for `complete_goal` to be accepted. For `workspaceWrite:true` goals they are **mandatory** (explicit `checks` or plugin `defaultChecks`), and they run from `checkCwd` (writers default to project root; artifact-only jobs run from their `artifactDir`).
63
- * **Agent** — any OpenCode agent name: built-in (`build`, `plan`, `explore`, `general`), file `~/.config/opencode/agents/*.md`, or `opencode.jsonc` `agent.*`. Discover with `opencode agent list`. Prefer `subagent`-mode agents for workers; `primary`-mode agents work but may expect user interaction. Resolution: explicit arg → **calling session's live agent** (read at creation) → `defaultAgent` → OpenCode global default. So a parent in Plan spawns a Plan worker unless told otherwise.
64
- * **Model** — any model as `"providerID/modelID"` (e.g. `openai/gpt-5.6-sol`, `ollama/qwen3.8:27b`, `openrouter/google/gemma-4-31b-it:free`). Discover with `opencode models [provider]`. Sent on every worker prompt. Resolution: explicit arg → **calling session's live model** → `defaultModel` → agent config → session history → global default. Caveat: OpenCode exposes no live-model field for history-derived models, so those fall through to agent config.
63
+ * **Agent** — any OpenCode agent name: built-in (`build`, `plan`, `explore`, `general`), file `~/.config/opencode/agents/*.md`, or `opencode.jsonc` `agent.*`. Discover with `opencode agent list`. Prefer `subagent`-mode agents for workers; `primary`-mode agents work but may expect user interaction. Resolution: explicit arg → **calling session's live agent** (read at creation) → `defaultAgent` → OpenCode global default. So a parent in Plan spawns a Plan worker unless told otherwise. **Omit by default — inherit.** Do NOT pass `agent` unless the caller explicitly requests a different identity or the task needs it; explicit values freeze identity and break session upgrades.
64
+ * **Model** — any model as `"providerID/modelID"` (e.g. `openai/gpt-5.6-sol`, `ollama/qwen3.8:27b`, `openrouter/google/gemma-4-31b-it:free`). Discover with `opencode models [provider]`. Sent on every worker prompt. Resolution: explicit arg → **calling session's live model** → `defaultModel` → agent config → session history → global default. Caveat: OpenCode exposes no live-model field for history-derived models, so those fall through to agent config. **Omit by default — inherit.** Do NOT pass `model` unless explicitly requested.
65
65
  * **WorkspaceWrite** — `true` (default) = may touch the shared repo; **only one active writer at a time** is allowed (enforced on `start`/`resume`/`retry` with rollback). Set `false` explicitly for artifact-only/read-only work to allow concurrency.
66
66
  * **Budgets** — `tokenBudget` (tokens) and `costBudget` (dollars, e.g. `0.5`). When either is exceeded the engine aborts the worker mid-turn and sets `budget_limited`. The worker is stopped immediately, not just at the next turn boundary.
67
67
  * **Limits** — `maxTurns` (default 50), `maxNoProgress`, `maxFailures`, `maxEvaluatorRejections` (default 3), `timeoutMs`, `compactEvery`, `progressFile`.
@@ -87,8 +87,9 @@ Use `loopd_create_goal` **after** clarifying the objective with the user (what /
87
87
  loopd_create_goal({
88
88
  name: "short-name",
89
89
  objective: "Detailed description of what the goal should accomplish.",
90
- agent: "researcher", // any agent name; optional, inherits calling session then defaultAgent
91
- model: "openai/gpt-5.6-sol", // any provider/model; optional, inherits calling session then defaultModel
90
+ // agent/model: omit by default to inherit calling session — only pass when explicitly requested
91
+ // agent: "researcher",
92
+ // model: "openai/gpt-5.6-sol",
92
93
  costBudget: 0.5, // optional dollars; aborts worker when exceeded
93
94
  checks: ["npm test"], // mandatory if workspaceWrite:true (or configure defaultChecks)
94
95
  checkCwd: "/project/root", // optional; writers default to project root, readers to artifactDir
@@ -204,10 +205,24 @@ Owner-side equivalents of TUI `:force` / `:block`. Bypass checks — use only wh
204
205
 
205
206
  ## Command Sessions (Standalone Interactive Processes)
206
207
 
207
- A **command session** is a raw OS process (spawned via `bun-pty`, falling back to a plain pipe) — no agent, no turns, no checks. It is completely independent from goals: starting/stopping one never pauses, blocks, or completes any goal, and vice versa. Use it whenever the request is "run X and let me watch/type into it" rather than "figure out/build X".
208
+ A **command session** is a raw OS process (spawned via `bun-pty`, falling back to a plain pipe) — no agent, no turns, no checks. It is completely independent from goals: starting/stopping one never pauses, blocks, or completes any goal, and vice versa. Use it whenever the request is "run X and let me watch/type into it" rather than "figure out/build X". By default, natural exits (including code 0), timeouts, and missing processes notify the owner; manual termination stays silent. Notification tails are sanitized for chat, while the retained terminal log remains raw. A finished command can still be opened to read its saved output; Ctrl+C/input cannot affect it.
208
209
 
209
210
  Typical uses: dev servers (`npm run dev`), watch mode (`npm test -- --watch`), REPLs (`python3`, `node`), log tails (`tail -f`), one-off scripts, interactive shells.
210
211
 
212
+ ### Prefer commands over the built-in shell
213
+
214
+ **Rule: if it might take a while or never return, start it as a command — never run it in the built-in shell/bash tool.** The built-in shell blocks your entire turn until the process exits: a 5-minute `npm install` freezes you mid-turn (and can hit tool timeouts, losing the output). `loopd_command_start` returns immediately with a `command_id` and pushes you on exit or pattern match, so you stay responsive.
215
+
216
+ Use a command (not the built-in shell) for: installs (`npm/pip/brew/cargo install`, downloads), builds, full test suites, migrations, dev servers, watchers, log tails, REPLs, anything interactive, or anything whose duration you can't guarantee is seconds. The built-in shell is only for quick one-shots (`ls`, `git status`, a single fast test).
217
+
218
+ ```
219
+ User: install the dependencies and set up the project.
220
+ # WRONG: built-in shell `npm install` — blocks your turn for minutes.
221
+ # RIGHT:
222
+ Agent: loopd_command_start({ title: "npm install", command: "npm", args: ["install"], notify_on_exit: true })
223
+ # → returns immediately; you keep working, and get pushed when it finishes.
224
+ ```
225
+
211
226
  ### Agent Tools
212
227
 
213
228
  | Tool | Purpose |
@@ -248,7 +263,7 @@ On the Commands tab, `o` **never** opens a chat session or a dialog — it close
248
263
  - Paste forwards raw bytes immediately.
249
264
  - The viewport is measured and resizes both the emulator and the real PTY.
250
265
 
251
- Colon commands available directly on the Commands tab (before opening fullscreen): `:new <command> [args...]` (quoted args preserved, e.g. `:new bash -c "echo hi"`), `:interrupt`, `:terminate`, `:remove`, and bare text + Enter writes stdin to the selected command. Goal controls (`p`/`r`/`R`/`x`/`A`/`N`) never fire on the Commands tab.
266
+ Single-key helpers on the Commands tab: `X` force-kill (SIGKILL now, no grace — for hung processes), `R` restart with the same spec (new ID, old record kept; env values are never persisted so restarted env commands lose their env), `x` remove finished (running commands get a hint, never a kill). Colon commands (before opening fullscreen): `:new <command> [args...]` (quoted args preserved, e.g. `:new bash -c "echo hi"`), `:interrupt`, `:terminate`, `:kill`, `:restart`, `:remove`, `:await <goalID>`, and bare text + Enter writes stdin to the selected command. Goal-only keys (`p`/`r`/`A`/`N`) never fire on the Commands tab; `R`/`x` are tab-scoped (retry/clear on Goals, restart/remove-done on Commands).
252
267
 
253
268
  ### Example: Watching a Dev Server
254
269
 
@@ -330,7 +345,17 @@ Open the shared dashboard with `/loop` (or `<leader>o`) — focuses the **Goals*
330
345
  | `h` | Select Goals directionally |
331
346
  | `l` | Select Commands directionally |
332
347
 
333
- `j`/`k`/`g`/`G` select within whichever tab is active. Goal controls (`p`/`r`/`R`/`x`/`A`/`N`) only fire on the Goals tab and are refused with a hint on Commands.
348
+ `j`/`k`/`g`/`G` select within whichever tab is active. Goal controls (`p`/`r`/`A`/`N`) only fire on the Goals tab and are refused with a hint on Commands; `R`/`x` are tab-scoped (retry/clear on Goals, restart/remove-done on Commands).
349
+
350
+ ### Commands Tab — Keyboard Shortcuts
351
+
352
+ | Key | Action |
353
+ |-----|--------|
354
+ | `X` | Force-kill selected command (SIGKILL now, no grace) |
355
+ | `R` | Restart selected command with the same spec (new ID) |
356
+ | `x` | Remove selected command — finished only (running gets a hint) |
357
+ | `o` | Open fullscreen terminal page |
358
+ | `Ctrl+C` | Interrupt selected command (SIGINT delivery, never a kill) |
334
359
 
335
360
  ### Goals Tab — Keyboard Shortcuts (Normal Mode)
336
361
 
@@ -373,11 +398,13 @@ Open the shared dashboard with `/loop` (or `<leader>o`) — focuses the **Goals*
373
398
  | `:new <command> [args...]` | Start a new command (quoted args preserved) | `loopd_command_start` |
374
399
  | `:interrupt` | SIGINT the selected command | `loopd_command_interrupt` |
375
400
  | `:terminate` | SIGTERM→SIGKILL the selected command | `loopd_command_terminate` |
401
+ | `:kill` | SIGKILL now, no grace (same as `X`) | — (no agent equivalent; agents use `loopd_command_terminate`) |
402
+ | `:restart` | Stop + respawn same spec, new ID (same as `R`) | — (no agent equivalent) |
376
403
  | `:remove` | Delete a finished command's record + log | `loopd_command_remove` |
377
404
  | bare text + Enter | Write that line as stdin to the selected command | `loopd_command_write` |
378
405
  | `q` | Detach (view closes; command keeps running) | — |
379
406
 
380
- Inside the **fullscreen terminal page** (reached via `o`): every keystroke forwards immediately as raw input (no line-submit box), `Ctrl+C` is interrupt input to the process, `Ctrl+]` detaches back to where you came from without stopping the process.
407
+ Inside the **fullscreen terminal page** (reached via `o`): every keystroke forwards immediately as raw input (no line-submit box), `Ctrl+C` is interrupt input to the process, `Ctrl+]` detaches back to where you came from without stopping the process. Press `?` on the Commands tab to see force-kill, restart, and removal shortcuts before opening the terminal.
381
408
 
382
409
  **Scrolling:** both lists are a `<scrollbox>` capped at 10 rows — short lists sit compact with no gap, long lists scroll and follow `j`/`k`/`g`/`G` via `scrollChildIntoView`. Detail + input stay pinned below in both cases. Empty list shows the fallback tip.
383
410
 
@@ -411,8 +438,7 @@ Agent: Got it — creating with a contract.
411
438
  loopd_create_goal({
412
439
  name: "docs-write",
413
440
  objective: "Write a README.md covering: what it is, install, usage, architecture, and examples. Then create CHANGELOG.md with a v1.0.0 entry.",
414
- agent: "researcher",
415
- model: "openai/gpt-5.6-sol",
441
+ // omit agent/model to inherit calling session — only pass when explicitly requested
416
442
  costBudget: 0.5,
417
443
  checks: ["test -f README.md", "test -f CHANGELOG.md"],
418
444
  checkCwd: "/Users/you/project", // explicit for writers; defaults to project root