@lawaty/lacode 0.9.0 → 0.11.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/CHANGELOG.md +242 -2
- package/README.md +25 -16
- package/dist/commands.js +14 -3
- package/dist/index.js +963 -379
- package/dist/lacode.js +288 -65
- package/dist/pool.js +41 -6
- package/dist/tunnel.js +248 -27
- package/docs/CONFIGURATION.md +21 -7
- package/docs/OMNIROUTE.md +1 -1
- package/docs/TUNNEL.md +86 -16
- package/docs/USER-STORIES.md +94 -9
- package/package.json +1 -1
- package/presets/README.md +4 -3
- package/presets/free-tier.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -6,7 +6,246 @@ All notable changes to LaCode are recorded here. The format follows
|
|
|
6
6
|
|
|
7
7
|
Versions come from git release commits; the npm version is bumped in the same
|
|
8
8
|
commit as the changelog entry it describes.
|
|
9
|
-
## [
|
|
9
|
+
## [0.11.0] — the old sessions and the old event rows are swept away on their own, and a pool edit takes effect without a restart
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **A pool edit is picked up live, and a model the provider has withdrawn no
|
|
14
|
+
longer takes its slot down with it.** The slot table used to be read once per
|
|
15
|
+
process, so editing `pool.txt`, `pool.json` or the bundled preset did nothing
|
|
16
|
+
until a restart — and a slot whose model id had been retired failed every
|
|
17
|
+
spawn routed to it. The plugin now watches the three file sources (`watchMs`,
|
|
18
|
+
default 1s; `watchDebounceMs`, default 250ms; both configurable, `watchMs: 0`
|
|
19
|
+
disables). When the resolved table actually changes it reassigns `s.slots`,
|
|
20
|
+
re-binds the variant agents and provider whitelists from the same config
|
|
21
|
+
object, and validates every slot with one real request — cooling or not. A
|
|
22
|
+
provider answering "Model not found" is now a first-class outcome:
|
|
23
|
+
`pool_recheck` benches that model for the quota cap (`model-missing`) so
|
|
24
|
+
routing stops choosing it, and names it in the report. Given `models`,
|
|
25
|
+
`pool_recheck` validates those even when they are not cooling; omitting
|
|
26
|
+
`models` keeps the old, cheap "probe what is cooling" contract. The watcher is
|
|
27
|
+
closed in `dispose`, so a torn-down plugin stops polling.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **The bundled preset and the built-in fallback no longer name a withdrawn
|
|
32
|
+
model.** `opencode-go/minimax-m3.1-flash-free` is `deprecated` on models.dev
|
|
33
|
+
and is hidden from `opencode models`; it is replaced by
|
|
34
|
+
`opencode-go/beam-free`, which is active, free and in a different model family.
|
|
35
|
+
`tests/models-snapshot.json` was refreshed for the new slot.
|
|
36
|
+
|
|
37
|
+
- **A build no longer unloads the pool from a process that starts during it.**
|
|
38
|
+
`npm run build` deleted `dist/` before rebuilding, so an opencode process that
|
|
39
|
+
imported `file://…/dist/index.js` in that window failed to load LaCode at all
|
|
40
|
+
and ran every `task` spawn on the caller's own model — `explore` appeared to
|
|
41
|
+
ignore the pool. Each entry is now staged and renamed into place, and stale
|
|
42
|
+
files are removed only after the live ones are in place.
|
|
43
|
+
|
|
44
|
+
- **A task spawn with no `subagent_type` is reported instead of silently
|
|
45
|
+
bypassing the pool.** opencode runs the default subagent, which carries no
|
|
46
|
+
pool model, so it inherits the caller's model; the hook cannot route it after
|
|
47
|
+
the fact, but it now logs a warning naming the fix (`subagent_type: "explore"`
|
|
48
|
+
or `"implement"`). An `@explore` mention or a direct session prompt has the
|
|
49
|
+
same limitation: the plugin API exposes the model read-only, so it cannot be
|
|
50
|
+
re-routed there either.
|
|
51
|
+
|
|
52
|
+
- **A provider limit the pool did not recognise left the task hanging until the
|
|
53
|
+
watchdog found it.** opencode reports a limit in two shapes: an `action` block
|
|
54
|
+
naming the provider and a reason, or — for every provider whose error body
|
|
55
|
+
opencode does not know — a retry status carrying nothing but the provider's
|
|
56
|
+
own words. The pool only read the first shape, and only when the reason
|
|
57
|
+
happened to be one of its own, so a limit that said "limit reached" was not a
|
|
58
|
+
limit to it: nothing was cooled, the session kept retrying the refused model,
|
|
59
|
+
and the task waited out the watchdog's minutes before anything killed it.
|
|
60
|
+
Worse, the retry went back to the same slot, because nothing had been put on
|
|
61
|
+
cooldown for the pool to route around.
|
|
62
|
+
|
|
63
|
+
The limit vocabulary now reads the provider's own phrasing — "limit reached",
|
|
64
|
+
"daily limit reached", "usage limit exceeded" — and a retry status is read for
|
|
65
|
+
its message as well as for its reason. The context family is deliberately
|
|
66
|
+
excluded: "context limit reached" is a prompt that is too long, not a
|
|
67
|
+
provider that has run out of allowance, and benching a model for it would take
|
|
68
|
+
a healthy provider out of rotation over the user's own prompt.
|
|
69
|
+
|
|
70
|
+
A limit that names its provider still benches the whole provider. A limit that
|
|
71
|
+
does not name a provider cools only the model the session was running on — the
|
|
72
|
+
one the refusal is evidence about — and either way the stalled session is
|
|
73
|
+
aborted at once, so the caller retries onto a slot that answers instead of
|
|
74
|
+
waiting for the watchdog.
|
|
75
|
+
|
|
76
|
+
- **A transient outage no longer benches a model as if it had run out of
|
|
77
|
+
allowance.** "Endpoint is unavailable" is a provider that is briefly down, not
|
|
78
|
+
one that has spent its quota: it lifts on its own. Reported in a retry
|
|
79
|
+
status, it took the quota path — fifteen minutes off the rotation, filed
|
|
80
|
+
under "rate-limited" — and it counted as a strike, so the next real limit on
|
|
81
|
+
that model started at strike two and got a thirty-minute window. An outage now
|
|
82
|
+
takes the flat one-minute window the assistant-error path has always given
|
|
83
|
+
it, and it is never counted as a strike: the model is benched, the session is
|
|
84
|
+
still killed, and the quota window that follows starts where it should.
|
|
85
|
+
|
|
86
|
+
- **Two processes could raise the same notification.** A notification is claimed
|
|
87
|
+
with an exclusive create, so only the process that wins the claim raises it,
|
|
88
|
+
and a claim older than its window may be taken over so a holder that died
|
|
89
|
+
mid-popup cannot silence every later one. The takeover deleted the stale claim
|
|
90
|
+
and then created its own — but the delete was unconditional and never
|
|
91
|
+
re-checked that the file it removed was still the stale claim it had judged a
|
|
92
|
+
moment earlier. Two processes that both saw the same stale claim both reached
|
|
93
|
+
the takeover: the first replaced it with a fresh claim, the second deleted
|
|
94
|
+
that fresh claim and made its own, and both returned "won", so both notified.
|
|
95
|
+
The takeover is now a compare-and-swap: an exclusive takeover lock, an
|
|
96
|
+
immediate refusal for whoever did not get it, and a re-read of the claim
|
|
97
|
+
inside the lock, so the loser leaves a live holder's fresh claim untouched.
|
|
98
|
+
The staleness rule itself is unchanged — a fresh claim is never stolen and a
|
|
99
|
+
stale one is still reclaimed.
|
|
100
|
+
|
|
101
|
+
- **A `busy` session counted as you answering a prompt.** The five-second floor
|
|
102
|
+
before a notification is raised exists so it does not land while you are
|
|
103
|
+
already at the keyboard, and it is meant to be reset by real interaction.
|
|
104
|
+
`session.status: busy` was counted as that interaction — but `busy` is raised
|
|
105
|
+
whenever the session starts working, including when opencode resumes on its
|
|
106
|
+
own, so the floor was refreshed without you touching anything and the popup
|
|
107
|
+
you were owed could be suppressed. The signal is now a `message.updated` with
|
|
108
|
+
the user's own role on it, plus the reply events for permissions and
|
|
109
|
+
questions; `busy` no longer counts.
|
|
110
|
+
|
|
111
|
+
- **A tunnel unit could be installed with a preflight it cannot run.** The unit
|
|
112
|
+
writer put `ExecStartPre=<preflight>` in unconditionally, so on a host where
|
|
113
|
+
the preflight was never installed systemd failed the unit and the tunnel never
|
|
114
|
+
came up — and the up-to-date check kept the broken unit because it demanded
|
|
115
|
+
the line. The line is now written only when the preflight is installed, which
|
|
116
|
+
degrades to "no preflight" rather than "no tunnel", and an already-broken unit
|
|
117
|
+
is repaired on the next run. The up-to-date check no longer requires the line.
|
|
118
|
+
|
|
119
|
+
- **A strike for an unavailable provider is filed where it belongs.** An outage
|
|
120
|
+
reported through the strike path could still be counted as a quota strike and
|
|
121
|
+
handed the long window, because only one of the two call sites delegated to
|
|
122
|
+
the outage handler. Both now do, and a strike never counts an outage. A limit
|
|
123
|
+
that names a provider is also no longer overridden by the model the session
|
|
124
|
+
happened to be running on.
|
|
125
|
+
|
|
126
|
+
- **Two acceptance criteria were corrected to match the shipped behaviour.**
|
|
127
|
+
US-6 criterion 4 stated the "a full slot is not the next one" rule without its
|
|
128
|
+
exception, so it contradicted the overflow path; it now says a full slot is
|
|
129
|
+
next only when no other slot is available and under its ceiling. US-19
|
|
130
|
+
criterion 3 stated a standing rule as if the product injected it, when it now
|
|
131
|
+
lives in the repo-level `AGENTS.md`.
|
|
132
|
+
|
|
133
|
+
### Added
|
|
134
|
+
|
|
135
|
+
- **The limit vocabulary is read from one place.** `pool.limitReason()` names
|
|
136
|
+
the limit a text reports, and the pool, the prober and the fail-over path all
|
|
137
|
+
take the reason from it, so a log line and a cooldown file never spell the
|
|
138
|
+
same fault two ways.
|
|
139
|
+
|
|
140
|
+
- **US-31 — a limit that hangs is killed, and the retry lands on a slot that
|
|
141
|
+
answers.** The contract is written down as a user story with acceptance
|
|
142
|
+
criteria in `docs/USER-STORIES.md`, and every criterion has a test in
|
|
143
|
+
`tests/stories/`.
|
|
144
|
+
|
|
145
|
+
- **Two retention sweeps take the old rows away before they become the whole
|
|
146
|
+
database.** `scripts/retire-old-sessions.mjs` archives then removes sessions
|
|
147
|
+
past a window (30 days, 24 hours of grace) and
|
|
148
|
+
`scripts/prune-old-events.mjs` deletes old transcript rows from `event`
|
|
149
|
+
(7 days, 1 hour of grace), in chunks, ordering by id — the id carries the
|
|
150
|
+
timestamp, so the primary-key index turns "older than" into a range scan
|
|
151
|
+
instead of a full table walk. Both refuse to touch a row that something still
|
|
152
|
+
depends on: a session an `event_sequence` cursor owns, a session bound to a
|
|
153
|
+
workspace, and anything inside the grace window. Neither ever removes or
|
|
154
|
+
resets a cursor. A session is written to a JSON archive before it is deleted,
|
|
155
|
+
and the archive is the recovery path if the window was wrong.
|
|
156
|
+
|
|
157
|
+
Both run by hand with `--dry-run`, support `--limit`, and the event prune also
|
|
158
|
+
takes `--vacuum`; a window larger than the id's own era is clamped, so a
|
|
159
|
+
mistyped number cannot be read as "delete everything". Each ships a systemd
|
|
160
|
+
service and timer (daily; `Persistent=true`, with a randomized delay on the
|
|
161
|
+
event prune so it does not run inside the session sweep's write window) and
|
|
162
|
+
appends one JSONL line per real run to a log under the state directory.
|
|
163
|
+
|
|
164
|
+
- **US-32 and US-33 document the sweeps, and the coverage guard reads the root
|
|
165
|
+
suites.** Both have acceptance criteria in `docs/USER-STORIES.md` and story
|
|
166
|
+
markers on their tests. The guard now scans the root-level `tests/*-test.mjs`
|
|
167
|
+
suites as well as `tests/stories/`, so a story whose tests legitimately live
|
|
168
|
+
outside the story directory is counted instead of reported uncovered.
|
|
169
|
+
|
|
170
|
+
- **Every acceptance criterion that could not be falsified now has a test that
|
|
171
|
+
fails when the behaviour regresses.** The audit found criteria whose tests
|
|
172
|
+
would have passed for the opposite behaviour: the least-loaded slot under a
|
|
173
|
+
full cooldown, a ceiling turn being skipped rather than consumed, a full slot
|
|
174
|
+
left unmarked, a macOS prompt carrying no buttons, a question carrying none,
|
|
175
|
+
a missing sound player costing only the sound, the bus path coming from the
|
|
176
|
+
local user rather than the remote, the sound playing where the notice is
|
|
177
|
+
rendered and never on the headless host, the self-update CLI, its argv and its
|
|
178
|
+
state file, cost control with no price information, the cartographer carrying
|
|
179
|
+
no model of its own, the map being reported rather than edited, an OmniRoute
|
|
180
|
+
gateway leaving a user's pool alone, and the notification doc leading with the
|
|
181
|
+
shipped path. Each new test was shown to fail by breaking the behaviour it
|
|
182
|
+
covers.
|
|
183
|
+
|
|
184
|
+
## [0.10.0] — a notification waits for you instead of timing out, and a permission can be answered from the popup
|
|
185
|
+
|
|
186
|
+
### Added
|
|
187
|
+
|
|
188
|
+
- **A permission prompt can be answered from the notification itself.** The
|
|
189
|
+
plugin reads the permission request id off opencode's `permission.asked`
|
|
190
|
+
event and puts it in the helper's environment (`LACODE_REQUEST_ID`), so the
|
|
191
|
+
notification carries three buttons: **Allow once**, **Always allow** and
|
|
192
|
+
**Deny**. Their names are opencode's own reply values — `once`, `always` and
|
|
193
|
+
`reject` ("reject" is the server's word for deny) — so a click is already a
|
|
194
|
+
valid answer and nothing has to be translated on the way.
|
|
195
|
+
|
|
196
|
+
The answer travels back over the same path the notification came. The desktop
|
|
197
|
+
end is an ssh forced command, so its stdout comes back down the ssh
|
|
198
|
+
connection and the plugin reads the chosen button's name off the helper's
|
|
199
|
+
stdout — no new key, no listener, no HTTP server. The hop was one-way until
|
|
200
|
+
now, and what made it two-way was a channel that was already there: the
|
|
201
|
+
forced command's stdout is carried to whoever opened the connection, so the
|
|
202
|
+
answer rides home on the connection that is already open. The plugin then
|
|
203
|
+
answers the permission through opencode's own API, and a permission that was
|
|
204
|
+
already answered in the terminal is recognised rather than reported as a
|
|
205
|
+
failure.
|
|
206
|
+
|
|
207
|
+
The wire payload is six newline-delimited fields on both wires —
|
|
208
|
+
`title\nmessage\nurgency\nicon\nsound\nrequestID`, the request id last — and a
|
|
209
|
+
payload with fewer than six fields carries no request id, so an older sender
|
|
210
|
+
still works.
|
|
211
|
+
|
|
212
|
+
Two limits, stated plainly. The scope is **permissions only**: a question is
|
|
213
|
+
not answerable this way, because opencode's v1 SDK client — the one this
|
|
214
|
+
plugin has — has no question API at all, and the v2 routes need a bound TCP
|
|
215
|
+
socket that the default opencode TUI does not have. And the buttons are
|
|
216
|
+
**Linux only**: `osascript` and `terminal-notifier` have no action support,
|
|
217
|
+
so a macOS desktop still gets a plain notice, and the answer there still has
|
|
218
|
+
to be given in the terminal.
|
|
219
|
+
|
|
220
|
+
### Changed
|
|
221
|
+
|
|
222
|
+
- **A desktop notification now waits for you instead of timing out on its own.**
|
|
223
|
+
`notify-send` is called with `--expire-time=0` — the freedesktop "never
|
|
224
|
+
expire" timeout — at all three call sites: `sendLocal` in
|
|
225
|
+
`src/tunnel/index.ts`, the tunnel's forced command
|
|
226
|
+
(`bin/oc-notify-receiver`), and `send_notify` in `bin/oc`. Without it the
|
|
227
|
+
desktop daemon's own timer dismissed the popup by itself: measured on KDE
|
|
228
|
+
Plasma 6.7.4, a normal-urgency notification is closed with reason 1
|
|
229
|
+
("expired") after about five seconds, so a notice the user had not acted on
|
|
230
|
+
within that window was simply gone — the difference between a notice you can
|
|
231
|
+
still act on and one you missed. A notification now stays on the desktop
|
|
232
|
+
until you close it.
|
|
233
|
+
|
|
234
|
+
Nothing else about it changed: the `--urgency` values are untouched (a
|
|
235
|
+
question or a permission prompt is deliberately not `critical`, so it does
|
|
236
|
+
not escalate to a modal on most desktops), and the whitelists and the
|
|
237
|
+
cooldown behaviour are exactly as they were.
|
|
238
|
+
|
|
239
|
+
### Documented
|
|
240
|
+
|
|
241
|
+
- **The npm publish notice does not say which dist-tag a release lands on.** npm
|
|
242
|
+
12 prints "Publishing to https://registry.npmjs.org/ with tag latest" for every
|
|
243
|
+
publish, including one whose `publishConfig.tag` sends it to `beta`: the
|
|
244
|
+
message reports the CLI's default, not the resolved tag. 0.9.0 was published
|
|
245
|
+
that way and landed on `beta`, with `latest` still at 0.4.2. The registry is
|
|
246
|
+
the authority, not the notice — after publishing, read
|
|
247
|
+
`npm view @lawaty/lacode dist-tags` before assuming anything about where the
|
|
248
|
+
release went.
|
|
10
249
|
|
|
11
250
|
## [0.9.0] — the cartographer keeps an active backlog, and the model stops naming slot variants
|
|
12
251
|
|
|
@@ -464,7 +703,8 @@ experimental; `latest` remains 0.4.2.
|
|
|
464
703
|
|
|
465
704
|
- First published release: pooled subagents over a shared set of models.
|
|
466
705
|
|
|
467
|
-
[Unreleased]: https://github.com/lawaty/lacode/compare/v0.
|
|
706
|
+
[Unreleased]: https://github.com/lawaty/lacode/compare/v0.10.0...HEAD
|
|
707
|
+
[0.10.0]: https://github.com/lawaty/lacode/compare/v0.9.0...v0.10.0
|
|
468
708
|
[0.9.0]: https://github.com/lawaty/lacode/compare/v0.8.0...v0.9.0
|
|
469
709
|
[0.8.0]: https://github.com/lawaty/lacode/compare/v0.7.1...v0.8.0
|
|
470
710
|
[0.7.1]: https://github.com/lawaty/lacode/compare/v0.7.0...v0.7.1
|
package/README.md
CHANGED
|
@@ -58,9 +58,11 @@ opencode plugin @lawaty/lacode -g
|
|
|
58
58
|
|
|
59
59
|
### 2. Restart opencode
|
|
60
60
|
|
|
61
|
-
**
|
|
62
|
-
restart the change does not take effect and the agent set will look
|
|
63
|
-
visible reason.
|
|
61
|
+
**Plugin code is not hot-reloaded.** The `plugin` array is read at startup, so
|
|
62
|
+
without a restart the change does not take effect and the agent set will look
|
|
63
|
+
stale for no visible reason. (The pool's slot files are the exception: editing
|
|
64
|
+
`pool.txt`, `pool.json` or the bundled preset is picked up live — see
|
|
65
|
+
[Configure](#configure).)
|
|
64
66
|
|
|
65
67
|
### 3. Check it loaded
|
|
66
68
|
|
|
@@ -91,6 +93,7 @@ To see what the pool is doing underneath:
|
|
|
91
93
|
|
|
92
94
|
```bash
|
|
93
95
|
/lacode-doctor # slots, model resolution, OmniRoute — report only, changes nothing
|
|
96
|
+
/pool-recheck # probe the benched models now; re-admit the ones that answer
|
|
94
97
|
lacode version # which LaCode is actually running, and whether opencode loads that one
|
|
95
98
|
```
|
|
96
99
|
|
|
@@ -192,15 +195,18 @@ it. Those windows are computed backoff rather than a promise from the provider,
|
|
|
192
195
|
and a quota-shaped one can run for hours — long enough for a provider that has
|
|
193
196
|
since recovered, or whose quota you reset in its own console, to stay excluded
|
|
194
197
|
while every delegated spawn queues behind the survivors. So LaCode asks instead
|
|
195
|
-
of guessing: every fifteen minutes,
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
198
|
+
of guessing: every fifteen minutes, on demand via the `pool_recheck` tool, and
|
|
199
|
+
the moment a pool file changes (where it validates every slot, cooling or not),
|
|
200
|
+
it probes each benched model with one real request on that exact model. A model
|
|
201
|
+
that answers is re-admitted immediately — nothing restarts, nothing reloads — and
|
|
202
|
+
a model that is still limited keeps exactly the window it had, because a check
|
|
203
|
+
must never be the thing that extends the wait. A model the provider no longer
|
|
204
|
+
serves at all is benched for the quota cap and named in the report. A change is
|
|
205
|
+
reported as a toast and a log line; a pass that finds nothing changed says
|
|
206
|
+
nothing at all.
|
|
207
|
+
|
|
208
|
+
Seven commands: `/context-init`, `/context-update`, `/context-review`,
|
|
209
|
+
`/lacode-doctor`, `/agent-instructions`, `/pool` and `/pool-recheck`. Everything is added **only
|
|
204
210
|
when your config does not already
|
|
205
211
|
define it** — an agent or command you wrote yourself is left byte-identical,
|
|
206
212
|
never patched or partially merged. `{"enabled": false}` turns the whole thing off
|
|
@@ -236,7 +242,8 @@ are reading:
|
|
|
236
242
|
/context-review # audit it for staleness, contradictions, duplication and bloat
|
|
237
243
|
/lacode-doctor # check the pool, model resolution and the OmniRoute gateway. Report only
|
|
238
244
|
/agent-instructions # apply a client instruction to the project's AGENTS.md — client-only, see below
|
|
239
|
-
/pool # rewrite the pool slot table in ~/.config/lacode/pool.txt;
|
|
245
|
+
/pool # rewrite the pool slot table in ~/.config/lacode/pool.txt; picked up automatically
|
|
246
|
+
/pool-recheck # probe the pool now; accepts an optional provider/model list
|
|
240
247
|
```
|
|
241
248
|
|
|
242
249
|
The map is local and untracked — it describes your checkout on your machine, so
|
|
@@ -346,9 +353,11 @@ lacode doctor # what the environment reso
|
|
|
346
353
|
```
|
|
347
354
|
|
|
348
355
|
`lacode notify` is fire-and-forget and always exits 0 — an unreachable desktop
|
|
349
|
-
must never be able to fail the process that tried to notify you.
|
|
350
|
-
|
|
351
|
-
|
|
356
|
+
must never be able to fail the process that tried to notify you. The one
|
|
357
|
+
exception is an actionable notification — one carrying a permission request id
|
|
358
|
+
— which waits for the click, because the answer is the whole point of it.
|
|
359
|
+
`lacode forward HOST -- COMMAND` runs a command on another host over ssh and
|
|
360
|
+
exits with its status.
|
|
352
361
|
|
|
353
362
|
The full CLI is `notify`, `forward`, `doctor`, `version`, `update`, `receive`
|
|
354
363
|
and `tunnel setup`. `version` reports which copy of LaCode is running — and
|
package/dist/commands.js
CHANGED
|
@@ -168,7 +168,7 @@ Print one PASS/FAIL line per check below, with the exact fix for any FAIL.
|
|
|
168
168
|
Change nothing. Print a final one-line summary of failures.`
|
|
169
169
|
},
|
|
170
170
|
"pool": {
|
|
171
|
-
description: "Modify this machine's LaCode pool from a short instruction \u2014 the shared free-model slot table in ~/.config/lacode/pool.txt.
|
|
171
|
+
description: "Modify this machine's LaCode pool from a short instruction \u2014 the shared free-model slot table in ~/.config/lacode/pool.txt. Picked up live; no restart.",
|
|
172
172
|
template: `Apply this change to the LaCode pool on this machine:
|
|
173
173
|
|
|
174
174
|
$ARGUMENTS
|
|
@@ -186,16 +186,27 @@ The pool is ONE global slot table shared by every pooled base (explore, implemen
|
|
|
186
186
|
|
|
187
187
|
3. Write the WHOLE file, not a patch. This file is an override, not an edit target: any slot you drop disappears, and slot numbers are line positions, so a partial write silently renumbers the rotation. Keep the existing comment header. If the write fails on a missing directory, create \`~/.config/lacode\` first and retry.
|
|
188
188
|
|
|
189
|
-
4. Report the before \u2192 after slot table
|
|
189
|
+
4. Report the before \u2192 after slot table and the resolved path. The running process watches the pool files: it reloads the table, re-binds the variant agents and validates every slot, so no restart is needed. If a slot names a model the provider does not serve, it will be benched and named in the log and toast \u2014 report that instead of claiming the swap succeeded.
|
|
190
190
|
|
|
191
191
|
Two traps that fail silently, so check and state them explicitly:
|
|
192
192
|
- A slot's provider must already exist in the user's opencode config, or that slot cannot resolve at all. If the instruction adds a model on a provider that is not configured, say so.
|
|
193
193
|
- If a slot model is \`omniroute/<id>\`, that id must also appear in the plugin's \`omniroute.models\` allowlist (or be the \`combo\` id, which needs no entry) \u2014 otherwise the pool resolves and every spawn fails with \`ProviderModelNotFoundError\`.
|
|
194
194
|
- A provider \`whitelist\` is an allowlist, and the plugin appends your model to it at config time. Nothing for you to do there.
|
|
195
195
|
|
|
196
|
-
If the instruction is to RESET or REVERT rather than to change models, delete \`${USER_MODELS_TXT}\` instead, and report that the pool falls back to the bundled preset \u2014
|
|
196
|
+
If the instruction is to RESET or REVERT rather than to change models, delete \`${USER_MODELS_TXT}\` instead, and report that the pool falls back to the bundled preset \u2014 the running process picks that up automatically too.
|
|
197
197
|
|
|
198
198
|
Change nothing else. In particular do not touch \`presets/free-tier.json\`, \`~/.config/lacode/pool.json\`, \`FALLBACK_SLOTS\` in \`src/lib/pool.ts\`, or \`tests/models-snapshot.json\`: this command owns the per-machine override and nothing else.`
|
|
199
|
+
},
|
|
200
|
+
"pool-recheck": {
|
|
201
|
+
description: "Probe the pool now \u2014 re-admit benched models that answer, keep the ones still limited, and bench a model the provider no longer serves.",
|
|
202
|
+
template: `Probe the LaCode pool now and report the result. Run inline; do not spawn a subagent and do not edit the pool.
|
|
203
|
+
|
|
204
|
+
$ARGUMENTS
|
|
205
|
+
|
|
206
|
+
- If the text above names one or more models, call the \`pool_recheck\` tool with those ids in \`models\` (provider/model-id).
|
|
207
|
+
- Otherwise call \`pool_recheck\` with no arguments; it probes every model that is currently cooling.
|
|
208
|
+
|
|
209
|
+
Report the tool's output as it came back, then add one short line naming what changed: re-admitted, still limited, or benched because the provider does not serve the model. If a model was benched as \`model-missing\`, say so and name the replacement in \`~/.config/lacode/pool.txt\` \u2014 but change nothing yourself.`
|
|
199
210
|
}
|
|
200
211
|
};
|
|
201
212
|
function applyCommands(cfg, commands = COMMANDS) {
|