@bojackduy/opencode-loopd 1.11.2 → 1.12.2

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.11.2",
4
+ "version": "1.12.2",
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",
@@ -112,11 +112,26 @@ The goal starts immediately. The user can monitor it via `/loop` (<leader>o). Pl
112
112
 
113
113
  ## Owner Agent: Discover Models and Recover the Same Goal
114
114
 
115
+ ### Agent listing and same-goal switching
116
+
117
+ `loopd_list_models({})` also returns `agents` (public name, description, mode: `primary`, `subagent`, or OpenCode's `all`) and independent `agentCatalog` capability/source/switching metadata. `assignedGoals` includes this owner's assigned agents and `agentSwitch` pending/last/failure state. Missing v1 `client.agent.list` reports an empty inventory with capability `unsupported`; failed discovery reports `unavailable`. Neither permits an unvalidated switch.
118
+
119
+ ```text
120
+ loopd_list_models({})
121
+ switch_goal_identity({goal_id:"existing-goal", agent:"researcher"})
122
+ # Only when explicitly resuming a blocked goal:
123
+ switch_goal_identity({goal_id:"existing-goal", agent:"researcher", resume:true})
124
+ ```
125
+
126
+ Agent switching is owner-only and validates the exact available name. It retains goal ID, worker session, transcript, progress, inbox, checks, budgets, counters, interactive mode and topology. Busy/unknown workers queue a persisted request until the next permitted confirmed-idle turn. v2 switches the idle session via `session.switchAgent`; v1 uses that SDK API if present, otherwise reports `deferred` and sends the assignment on the next prompt. Switching alone never wakes an interactive goal, unpauses or resumes. `resume:true` is valid only for blocked goals; if still busy, it remains deferred and blocked until an explicit retry/resume when safe. Completed goals cannot switch. Pending failures block once without creating a replacement worker.
127
+
128
+ On the dashboard **Goals tab**, `:agents` lists names/modes and `:agent <name>` changes the selected goal's agent. Both are refused on the Commands tab (never forwarded as process stdin). Goal rows display `🤖 agent-name` alongside the model.
129
+
115
130
  Use agent tools, not a new goal or a human dashboard action, to change a worker's provider/model:
116
131
 
117
132
  1. `loopd_list_models({})` returns public provider/model IDs, names, usable/connected status, context/output limits, host switching capability, and identities of **this owner session's** goals.
118
133
  2. `inspect_background_goal({goal_id, includeTranscript:false})` shows assigned model, `modelSwitch` (pending/last/failure), `modelFallback` (attempted/status), and `lastProviderLimit` (local observation source/time/model/status/retryAt).
119
- 3. Select a positively usable catalog model and call `switch_goal_model({goal_id, model:"provider/model"})`. Keep the existing goal ID and worker session. **Never spawn a duplicate goal to switch providers.**
134
+ 3. Select a positively usable catalog model and call `switch_goal_identity({goal_id, model:"provider/model"})`. Keep the existing goal ID and worker session. **Never spawn a duplicate goal to switch providers.**
120
135
 
121
136
  **Quota caveat:** the inspected v1/v2 host catalogs do not expose remaining provider quota or reset times. Results explicitly say `quota.status:"unknown"` and `quota.capability:"unsupported"`, with source and observation time. Connected/enabled credentials do **not** establish balance. `lastProviderLimit` is an observed 429/quota failure, not live balance data; `retryAt` is derived only from a supplied Retry-After value, not a fabricated quota reset. Request headers, auth tokens, keys, and raw provider records are not returned. Older/missing host APIs report unsupported; failed catalog requests report unavailable and cannot validate switches.
122
137
 
@@ -125,13 +140,23 @@ Use agent tools, not a new goal or a human dashboard action, to change a worker'
125
140
  ```text
126
141
  loopd_list_models({})
127
142
  inspect_background_goal({goal_id:"existing-goal", includeTranscript:false})
128
- switch_goal_model({goal_id:"existing-goal", model:"connected-provider/usable-model"})
143
+ switch_goal_identity({goal_id:"existing-goal", model:"connected-provider/usable-model"})
129
144
  # If and only if the goal is blocked by a provider quota/rate limit:
130
- switch_goal_model({goal_id:"existing-goal", model:"connected-provider/usable-model", resume:true})
145
+ switch_goal_identity({goal_id:"existing-goal", model:"connected-provider/usable-model", resume:true})
131
146
  ```
132
147
 
133
148
  Switch results distinguish `applied`, `deferred`, and `unsupported`. Busy/retrying/unconfirmable workers defer until a confirmed-idle continuation; there is no implicit abort. v1 supports explicit model identity on the **next prompt** (reported deferred); v2 supports an idle-session model switch. Future prompts and compaction use the switched assignment. Default switching does not unpause, resume, or wake interactive goals. `resume:true` explicitly starts a permitted turn only for a provider-limit-blocked goal, never paused, budget-limited, or unrelated-blocked goals. If the worker is still busy, the switch remains deferred and resume must be requested again when safe. Budgets, failure/rejection counters, checks, progress, inbox, topology, transcript, and session ID are preserved.
134
149
 
150
+ ### Combined identity switching
151
+
152
+ `switch_goal_identity` replaces the separate model/agent tools. Provide at least one of `model` or `agent`; omitted fields stay unchanged. To change both together:
153
+
154
+ ```text
155
+ switch_goal_identity({goal_id:"existing-goal", model:"connected-provider/usable-model", agent:"researcher"})
156
+ ```
157
+
158
+ Combined switches validate both catalogs before touching the host, serialize with all goal operations, and persist both assignments in one state mutation. If the second host change fails, the first is rolled back, including next-prompt assignments. If the host itself rejects rollback, the operation reports failure: inspect the existing live session before retrying; do not create a replacement. Hosts cannot expose a single atomic two-field API, so live host changes are sequential under the goal lock, with no intervening worker prompt. Busy workers persist the pair as `pendingIdentity` and apply it together at the next permitted idle turn. Discovery exposes `assignedGoals[].pendingIdentity`. A later single-field request updates that field of the pending pair. Combined `resume:true` is explicit and valid only for blocked goals; model-only resume retains the provider-limit requirement. No switch resets budgets or failure counters, replaces a worker, or implicitly wakes an interactive goal. Dashboard `:model` and `:agent` both dispatch the unified bus operation.
159
+
135
160
  ### Opt-in ordered fallback
136
161
 
137
162
  ```text
@@ -421,8 +446,10 @@ Open the shared dashboard with `/loop` (or `<leader>o`) — focuses the **Goals*
421
446
  | `:abort` | Abort worker session now (status unchanged; A) | `abort_goal_worker` |
422
447
  | `:pause` / `:resume` / `:retry` / `:clear` | Quick controls (also p/r/R/x) | `pause_goal` / `resume_goal` / `clear_goal` |
423
448
  | `:interactive` | Toggle manual mode (engine never starts turns) | `interactive: true` at creation |
424
- | `:model <provider/model>` | Switch this goal's model (same worker/session) | `switch_goal_model` |
449
+ | `:model <provider/model>` | Switch this goal's model (same worker/session) | `switch_goal_identity` |
425
450
  | `:models` | Show provider/model inventory + quota status | `loopd_list_models` |
451
+ | `:agent <name>` | Switch this goal's agent (same worker/session; Goals tab only) | `switch_goal_identity` |
452
+ | `:agents` | List available agent names/modes and host capabilities (Goals tab only) | `loopd_list_models` |
426
453
  | `:logs` / `:help` / `:q` | Toggle logs / help / close | — |
427
454
 
428
455
  > Goal creation (`:goal start`) was removed from the dashboard — create goals via `/goal` in the parent chat so the agent can clarify the objective first.