@lawaty/lacode 0.11.0 → 1.0.1

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 CHANGED
@@ -6,6 +6,91 @@ 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
+
10
+ ## [1.0.1] — notification sounds you can hear, and a title that names the workspace
11
+
12
+ ### Fixed
13
+
14
+ - **Notification sounds play again.** The plugin preferred the bash `oc-notify`
15
+ helper, which discards the sound field, so a chime was never played on that
16
+ path; it now prefers the lacode CLI, whose `sendLocal` plays
17
+ `sounds/<outcome>.wav`. The bash helper stays as a last-resort fallback.
18
+ - **The four chimes survive a suspended sink.** Each WAV was 0.20–0.32 s at 42%
19
+ full-scale, which a suspended Bluetooth output swallowed whole while the link
20
+ woke. They are now 1.0–1.46 s at 90% peak, and a test pins both the duration
21
+ and the peak so the regression cannot return.
22
+ - **A popup no longer fades on its own.** `interacted` is scoped to the popup it
23
+ was set on: a new popup starts un-interacted and auto-dismisses only when the
24
+ user interacts in that session while it is live. Previously every popup in a
25
+ session the user had ever typed in closed after five seconds.
26
+ - **The published `bin` entry uses the `./dist/…` form** the packaging check
27
+ requires, so the release tarball passes its own adoption test.
28
+
29
+ ### Changed
30
+
31
+ - **Notification titles name the workspace as well as the session.** The title
32
+ is now `{workspace} - {session}`, where the workspace is the basename of the
33
+ session's directory, falling back to the plugin directory. The popup header
34
+ stays `LaCode`. Internal cartographer sessions are still suppressed, because
35
+ the check runs on the session title before the prefix is added.
36
+
37
+ ## [1.0.0] — images, ordered slot fill, and a limit that fails over
38
+
39
+ ### Added
40
+
41
+ - **Image attachments from the clipboard or from files.** `/image-in-cb` reads
42
+ the system clipboard through `wl-paste`/`xclip` on Linux, `pngpaste` on macOS,
43
+ or PowerShell on Windows, and `/image` attaches one or more image files. Paths
44
+ honour quotes, `~` and globs; identical bytes are deduped within a message;
45
+ each file is capped at 4 MiB, at 10 images, and at 20 MiB total. Images are
46
+ archived content-addressed under `~/.local/share/opencode/lacode/images` and
47
+ delivered to the model as `FilePart`s. Configure with `imagePaste`
48
+ (`enabled`, `maxBytes`, `maxImages`, `dir`) or `LACODE_IMAGE_DIR`.
49
+ - **A system-prompt guard against slot-variant names.** Every system string is
50
+ scrubbed by `scrubSlotNames()` before the model sees it, so a stale prompt
51
+ cannot teach the model `explore-1` and friends. The transform logs once when
52
+ it has to strip a literal.
53
+
54
+ ### Changed
55
+
56
+ - **Slot routing fills slots in declaration order in up to two passes.** A
57
+ slot's `weight` is its active-session budget for one pass. Pass 1 fills each
58
+ usable slot to its weight in declaration order; once every usable slot has
59
+ reached its weight, pass 2 fills each to twice its weight. Live claims decide
60
+ the next slot, a released claim immediately reopens its slot, and overflow
61
+ goes to the least-loaded usable slot. There is no stored position.
62
+ - **A hand-named slot variant is coerced to its base, not refused.** The task
63
+ hook rewrites `explore-1` to `explore` before routing, so a caller cannot pin
64
+ a provider or skip ordered fill. Injected agent prompts no longer name
65
+ variants, and `expert` is explicitly opt-in.
66
+ - **Rate-limit failover uses the authenticated plugin client.** Aborting a
67
+ limited child goes through `client.session.abort`, not a bare `fetch`, so an
68
+ `opencode serve` protected by `OPENCODE_SERVER_USERNAME/PASSWORD` no longer
69
+ answers 401 and leaves the child running. A failed abort holds the claim for
70
+ the reaper to retry instead of releasing the slot while the session lives.
71
+ - **A limit on a pooled child with no local claim is identified and handled.**
72
+ The failover path reads the session's `parentID` and user message to recognise
73
+ a pooled variant (`implement-1`, `explore-2`, `context-N`), cools that slot,
74
+ and aborts the child. Root, non-pooled, and unidentifiable sessions are left
75
+ alone.
76
+
77
+ ### Fixed
78
+
79
+ - **A limited or hung session no longer survives its slot.** The rate-limit path
80
+ aborts it immediately, including the transient-outage path in
81
+ `message.updated`, and a failed abort no longer frees the slot.
82
+ - **`pool_status` says a cooling slot is unavailable.** A cooling slot is marked
83
+ `UNAVAILABLE (COOLING …)`, and the summary reads `unavailable/cooling: …`.
84
+ - **A `done` popup is raised once per idle period, and an `error` suppresses the
85
+ `done` that follows it in the same turn.** The per-session completion marker
86
+ is cleared when the session goes busy again or is deleted.
87
+
88
+ ### Documented
89
+
90
+ - README documents `/image`, `/image-in-cb`, `imagePaste`, the `latest` dist-tag
91
+ for 1.0.0, and the ordered two-pass fill. `OPERATIONS.md`, `presets/README.md`
92
+ and the preset comments describe the same routing rule.
93
+
9
94
  ## [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
95
 
11
96
  ### Added
@@ -703,7 +788,9 @@ experimental; `latest` remains 0.4.2.
703
788
 
704
789
  - First published release: pooled subagents over a shared set of models.
705
790
 
706
- [Unreleased]: https://github.com/lawaty/lacode/compare/v0.10.0...HEAD
791
+ [Unreleased]: https://github.com/lawaty/lacode/compare/v1.0.0...HEAD
792
+ [1.0.0]: https://github.com/lawaty/lacode/compare/v0.11.0...v1.0.0
793
+ [0.11.0]: https://github.com/lawaty/lacode/compare/v0.10.0...v0.11.0
707
794
  [0.10.0]: https://github.com/lawaty/lacode/compare/v0.9.0...v0.10.0
708
795
  [0.9.0]: https://github.com/lawaty/lacode/compare/v0.8.0...v0.9.0
709
796
  [0.8.0]: https://github.com/lawaty/lacode/compare/v0.7.1...v0.8.0
package/README.md CHANGED
@@ -2,10 +2,8 @@
2
2
 
3
3
  > Ultimate opencode setup for Go users
4
4
 
5
- > **Beta — experimental, changing fast.** LaCode ships on the npm **`beta`**
6
- > dist-tag rather than `latest`, so nothing pulls it by accident: install
7
- > `@lawaty/lacode@beta`, or pin an exact version. Interfaces here can change
8
- > between releases without a major bump.
5
+ > LaCode 1.0.0 ships on the npm **`latest`** dist-tag. Prereleases use
6
+ > `beta`: install `@lawaty/lacode@beta`, or pin an exact version.
9
7
 
10
8
  LaCode is an opencode plugin. It routes delegated work onto a weighted pool of
11
9
  cheap models instead of the expensive one, reaps subagents that hang without
@@ -29,8 +27,13 @@ is also on disk at `node_modules/@lawaty/lacode/CHANGELOG.md`.
29
27
  | Per-model cooldowns and failover | A provider rate-limits mid-session and the whole thing hard-fails, while other providers sit idle. |
30
28
  | Hang reaper | A wedged subagent holds its slot forever, so the session stalls with no error and no timeout. |
31
29
  | Desktop notifications | With several sessions open you cannot see which one finished, or which is blocked on a question. |
30
+ | Permission answers on the popup | An approval blocks a background session, and finding that session just to click Allow is the interruption. |
31
+ | Distinct notification sounds | A popup says something happened, but not at a glance whether it is a finish, a question or a block. |
32
32
  | SSH notification tunnel | Work on a remote host raises its notification on that host, which has no desktop. |
33
- | Repository context map | Every task starts by exploring your codebase cold, and every agent re-discovers the same layout, the same entry points and the same boundaries — paid for again on every run. |
33
+ | Repository context map | Every task starts by exploring your codebase cold, re-deriving the same layout, entry points and boundaries. |
34
+ | Active backlog | What is half-done, and what can be dropped, lives in chat history the next session cannot see. |
35
+ | Standing instructions | The same direction gets repeated every session, or buried in config that is not per-clone. |
36
+ | Image attachments anywhere | A TUI with no image support locks vision models out entirely. |
34
37
 
35
38
  ## Getting started
36
39
 
@@ -132,6 +135,9 @@ option is optional:
132
135
  | `selfUpdate` | `true` | Check for a newer release at most once per ~6h and install it in the background; it applies on the next opencode start. `false` — or `LACODE_NO_SELFUPDATE=1` — turns the check off. |
133
136
  | `sound` | `true` | Play a short tone with each notification, one per outcome, so you can tell a finished session from a question or a permission prompt without reading the popup. It is opt-out: `false` — or `LACODE_SOUND=0` (`false`, `off`, `no` work too, and beat the option) — turns the sound off, and anything else, a typo included, leaves it on. |
134
137
  | `omniroute` | — | Optional [OmniRoute](docs/OMNIROUTE.md) gateway integration. Absent or `{ enabled: false }` changes nothing. |
138
+ | `watchMs` | `1000` | How often the pool files are checked for changes. `0` disables live reload. |
139
+ | `watchDebounceMs` | `250` | Collapse a burst of editor writes into one reload. |
140
+ | `imagePaste` | — | Image attachments: `maxBytes` (4 MiB), `maxImages` (10) and `dir`. |
135
141
 
136
142
  ### Staying up to date
137
143
 
@@ -146,6 +152,12 @@ The running session keeps its loaded code; the new version takes effect on the
146
152
  next start, and the log says so. Run `lacode update` to do it on demand, or
147
153
  `lacode update --check` to just report.
148
154
 
155
+ 1.0.0 is published to the `latest` dist-tag; prereleases use `beta`. This check
156
+ follows `latest`. A beta install is therefore reported as current even when a
157
+ newer beta exists,
158
+ and `lacode update` will not move it: pin the version you want in the `plugin`
159
+ entry, because a new specifier is what forces a fresh resolve.
160
+
149
161
  ### Which LaCode is running
150
162
 
151
163
  `lacode version` answers it — and the answer is often not the version number you
@@ -156,7 +168,7 @@ lacode version
156
168
  ```
157
169
 
158
170
  ```
159
- version 0.4.1
171
+ version 1.0.0
160
172
  root /home/you/lacode
161
173
  source checkout
162
174
  commit <hash> (dirty)
@@ -182,16 +194,16 @@ When it reports `stale`, rebuild with `npm run build`. No network is touched;
182
194
 
183
195
  ## What you get
184
196
 
185
- Three agents, pooled and rotated: `explore`, `implement` and `context`. You spawn
186
- the base name and the pool picks a cheap model slot for it. Behind each base sits
187
- up to four hidden variants — one per slot — and a spawn of the bare name is the
188
- only thing that works: naming a variant lets the caller choose the provider
189
- directly, which is exactly what the rotation and the per-slot ceiling exist to
190
- prevent, so it is refused.
197
+ Three pooled agents: `explore`, `implement` and `context`. You spawn the base
198
+ name and the pool picks a cheap model slot for it. Behind each base sits up to
199
+ four hidden variants — one per slot — and a spawn of the bare name is the only
200
+ thing that works: a hand-named variant such as `explore-1` is coerced to its
201
+ base and routed by the pool, so naming one cannot pin a provider or skip
202
+ ordered slot filling.
191
203
 
192
- A model that rate-limits or runs out of quota is benched and taken out of the
193
- rotation until its window ends, so one sick provider does not take the pool with
194
- it. Those windows are computed backoff rather than a promise from the provider,
204
+ A model that rate-limits or runs out of quota is benched until its window ends,
205
+ so one sick provider does not take the pool with it.
206
+ Those windows are computed backoff rather than a promise from the provider,
195
207
  and a quota-shaped one can run for hours — long enough for a provider that has
196
208
  since recovered, or whose quota you reset in its own console, to stay excluded
197
209
  while every delegated spawn queues behind the survivors. So LaCode asks instead
@@ -200,30 +212,42 @@ the moment a pool file changes (where it validates every slot, cooling or not),
200
212
  it probes each benched model with one real request on that exact model. A model
201
213
  that answers is re-admitted immediately — nothing restarts, nothing reloads — and
202
214
  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
215
+ must never be the thing that extends the wait. A model the provider does not
216
+ serve at all is benched for the quota cap and named in the report.
217
+ A change is reported as a toast and a log line; a pass that finds nothing changed says
206
218
  nothing at all.
207
219
 
208
- Seven commands: `/context-init`, `/context-update`, `/context-review`,
209
- `/lacode-doctor`, `/agent-instructions`, `/pool` and `/pool-recheck`. Everything is added **only
210
- when your config does not already
220
+ Nine commands: `/context-init`, `/context-update`, `/context-review`,
221
+ `/lacode-doctor`, `/agent-instructions`, `/pool`, `/pool-recheck`, `/image-in-cb`
222
+ and `/image`. Everything is added **only when your config does not already
211
223
  define it** — an agent or command you wrote yourself is left byte-identical,
212
224
  never patched or partially merged. `{"enabled": false}` turns the whole thing off
213
225
  without leaving a trace. The exact injection list, and the five keys that are
214
226
  deliberately *not* injected, are in
215
227
  [docs/CONFIGURATION.md](docs/CONFIGURATION.md#what-gets-injected).
216
228
 
229
+ Two more surfaces that need no configuration:
230
+
231
+ - **`pool_status`** — the live view `/lacode-doctor` uses: per-slot load against
232
+ its ceiling, which slot the next spawn takes, cooldowns, hung claims and
233
+ whether the watcher is on. Ask for it when you want to know what the pool is
234
+ doing; it never changes slot selection.
235
+ - **Notifications that wait** — a notification is never auto-dismissed; it stays
236
+ until you close it. On Linux a permission request carries **Allow once**,
237
+ **Always allow** and **Deny** buttons, and LaCode waits up to ten minutes for
238
+ the click.
239
+
217
240
  ## Context map
218
241
 
219
242
  Agents are stateless between tasks, and exploring your repository is the most
220
243
  expensive part of any task — so every agent pays for it again, re-deriving the
221
244
  same layout, entry points and boundaries from scratch. LaCode keeps a short,
222
- maintained map of the repository in `.opencode/context/` (five navigation files:
223
- `architecture`, `contexts`, `conventions`, `workflows`, `decisions`, plus
224
- `active.md` — the active backlog) and injects
225
- a standing rule that every agent reads the relevant file before exploring and
226
- starts from the canonical entry points it lists. The map holds conclusions, not
245
+ maintained map of the repository in `.opencode/context/` — five navigation files
246
+ (`architecture`, `contexts`, `conventions`, `workflows`, `decisions`) plus
247
+ `active.md`, the active backlog, where a finished task stays a week and an open
248
+ one stays until done — and injects a standing rule that every agent reads the
249
+ relevant file before exploring and starts from the canonical entry points it
250
+ lists. The map holds conclusions, not
227
251
  inventories: it answers "what should I look at first?" and nothing else, which
228
252
  is what makes reading it cheaper than the exploration it replaces.
229
253
 
@@ -244,6 +268,8 @@ are reading:
244
268
  /agent-instructions # apply a client instruction to the project's AGENTS.md — client-only, see below
245
269
  /pool # rewrite the pool slot table in ~/.config/lacode/pool.txt; picked up automatically
246
270
  /pool-recheck # probe the pool now; accepts an optional provider/model list
271
+ /image-in-cb # attach the clipboard image to this message; macOS needs pngpaste, Linux needs wl-paste or xclip
272
+ /image # attach one or more image files to this message; paths honour quotes, ~ and globs
247
273
  ```
248
274
 
249
275
  The map is local and untracked — it describes your checkout on your machine, so
@@ -252,11 +278,16 @@ nothing and carry on without it. What this buys is tokens not spent
252
278
  re-discovering structure; as with everything else here, no savings percentage is
253
279
  published, because none has been independently measured. The mechanism, the
254
280
  permission rule that makes one writer possible, and what the log lines mean are
255
- in [docs/CONTEXT.md](docs/CONTEXT.md).
281
+ in [docs/CONTEXT.md](docs/CONTEXT.md). Automatic map runs also clean up after
282
+ themselves: each run's session is deleted once its transcript is written, and
283
+ the last 20 transcripts are kept under
284
+ `~/.local/share/opencode/lacode/cartographer` (`LACODE_TRANSCRIPT_DIR` overrides
285
+ the directory).
256
286
 
257
287
  ## Standing instructions
258
288
 
259
- The project's root `AGENTS.md` is your own statement of intent: it records
289
+ The project's root `AGENTS.md` — created on first use by `/agent-instructions` —
290
+ is your own statement of intent: it records
260
291
  what you want worked on and how you want it approached. It says nothing about
261
292
  where code lives, so it can never contradict the code, and within that domain
262
293
  it outranks the generated context files and any agent's inference about what
@@ -345,7 +376,8 @@ which explains the harness and deliberately contains no results.
345
376
  ## Remote hosts
346
377
 
347
378
  Notifications raised on a remote host need a way back to your desktop. The
348
- reverse SSH forward is what carries them:
379
+ default hop is a direct `ssh` to your desktop over one warm `ControlMaster`
380
+ connection; a reverse forward is the fallback used when `OC_LOCAL_HOST` is unset:
349
381
 
350
382
  ```bash
351
383
  lacode notify "build finished" "4 files changed" # local desktop, or forwarded if remote
@@ -356,6 +388,8 @@ lacode doctor # what the environment reso
356
388
  must never be able to fail the process that tried to notify you. The one
357
389
  exception is an actionable notification — one carrying a permission request id
358
390
  — which waits for the click, because the answer is the whole point of it.
391
+ That click path is Linux-only: the popup carries **Allow once**, **Always allow**
392
+ and **Deny**, and macOS gets a plain notice without buttons.
359
393
  `lacode forward HOST -- COMMAND` runs a command on another host over ssh and
360
394
  exits with its status.
361
395
 
package/dist/agents.js CHANGED
@@ -78,9 +78,12 @@ Every question of the form "what does this file do", "where is X handled", or
78
78
  that proves it. Never one whole refactor handed over in a single piece \u2014
79
79
  see \`# Task size\` below.
80
80
 
81
- Spawn the BARE pooled base \u2014 \`explore\`, \`implement\` or \`context\`. The pool
82
- refuses a \`-N\` variant, and a refusal means spawn the bare base, never a
83
- different number.
81
+ Spawn the pooled base \u2014 \`explore\`, \`implement\` or \`context\`. The plugin decides
82
+ which model runs the work; do not try to choose one yourself.
83
+
84
+ \`expert\` is not part of the pool and is expensive: spawn it only when the user
85
+ explicitly asks for it. Never escalate to it on your own, and never as a
86
+ fallback when a pooled spawn fails.
84
87
 
85
88
  # When the pool is unavailable
86
89
 
@@ -89,12 +92,12 @@ pool cannot serve a spawn \u2014 every slot cooling or exhausted, spawns repeate
89
92
  failing, or a runtime message saying no slot is available \u2014 that is not a reason
90
93
  to do the work yourself, and not a reason to stop and ask.
91
94
 
92
- **Retry.** A refused or cooling slot is transient and costs you nothing but a
93
- turn. Spawn the bare base again. Do not escalate to a numbered variant after a
94
- refusal \u2014 the refusal means the pool has a routing problem, not that you
95
- picked the wrong number.
95
+ **Retry.** A cooling model is transient and costs you nothing but a turn. Spawn
96
+ the same base again. If a spawn fails, do not switch to a different name \u2014 a
97
+ failure means the pool has a routing problem, not that you picked the wrong one.
96
98
 
97
- Retry is always available and never wrong, which is why it is the default. But
99
+ Retry is always available
100
+ and never wrong, which is why it is the default. But
98
101
  "never wrong" is not "sufficient": looping silently against a pool with every
99
102
  model cooling goes nowhere and burns the slot doing it. So separate the two
100
103
  failures. **Transient** \u2014 a cooldown, a rate limit, one slot at its ceiling \u2014
@@ -102,7 +105,8 @@ means retry. **Persistent or structural** \u2014 every slot cooling, the same sp
102
105
  failing repeatedly with the same error, a malformed model id \u2014 means stop
103
106
  retrying and *report* it: say what failed (slot, model, error) and continue
104
107
  with whatever else the task needs, or ask the user. Surface the failure; do not
105
- spin on it and do not let it end the whole task.
108
+ spin on it and do not let it end the whole task. Do not escalate to \`expert\` as
109
+ a workaround; it is opt-in by the user.
106
110
 
107
111
  **Never fall back to reading, grepping, exploring or implementing on your own.**
108
112
  Do not "just quickly" inspect a file because a spawn failed. A failed spawn is
@@ -219,8 +223,11 @@ When you need to understand the codebase:
219
223
  2. Invoke @explore with specific, focused instructions (e.g. "read src/auth.ts", "grep for 'migration' in *.py files", "list the src/ directory")
220
224
  3. @explore will return its findings \u2014 you process them and plan accordingly
221
225
 
222
- Spawn the BARE pooled base (\`explore\`, \`implement\`, \`context\`); the pool
223
- refuses \`-N\` variants, and a refusal means spawn the bare base, not another number.
226
+ Spawn the pooled base (\`explore\`, \`implement\`, \`context\`); the plugin decides
227
+ which model runs the work. Do not try to choose one yourself.
228
+
229
+ \`expert\` is not part of the pool and is expensive: spawn it only when the user
230
+ explicitly asks for it.
224
231
 
225
232
  If the pool cannot serve a spawn \u2014 every slot cooling or exhausted, or repeated
226
233
  failures \u2014 do NOT fall back to reading or planning from your own inspection.
@@ -548,7 +555,7 @@ Trigger: \`.opencode/context/\` exists and work has just been done here.
548
555
  - an architectural decision
549
556
  - a new dependency between subsystems
550
557
  - a renamed or relocated subsystem
551
- - a previously undocumented invariant
558
+ - an invariant not yet recorded in the map
552
559
 
553
560
  Do not update documentation merely because source files changed. Do not
554
561
  rewrite files wholesale. Do not make timestamp-only edits. Do not add a fact
@@ -854,7 +861,7 @@ function buildAgentSet() {
854
861
  for (let slot = 1; slot <= MAX_SLOTS; slot++) {
855
862
  const variant = {
856
863
  ...def,
857
- description: `Hidden pool variant ${base}-${slot}: slot ${slot}. Its model is NOT set here -- LaCode injects it from the resolved pool preset at config time, so the preset is the only place a pool model is written down. Spawn '${base}' and let the pool route.`,
864
+ description: `Hidden pool slot ${slot} for ${base}. Its model is NOT set here -- LaCode injects it from the resolved pool preset at config time, so the preset is the only place a pool model is written down. Spawn '${base}' and let the pool route.`,
858
865
  hidden: true,
859
866
  // `mode: "primary"` is what keeps a variant OUT of the model's system
860
867
  // prompt. `hidden` cannot do it.
@@ -866,9 +873,10 @@ function buildAgentSet() {
866
873
  // and by default-agent resolution. A variant left as `mode: "subagent"`
867
874
  // is therefore listed to the model under its own name -- "explore-1:
868
875
  // Hidden pool variant explore-1" -- and the model copies that name
869
- // straight into its next spawn. The pool then refuses it, because
870
- // naming a variant is how a caller would pick the provider and skip the
871
- // rotation. The refusal is right; the leak that provokes it is not.
876
+ // straight into its next spawn. The task hook coerces a hand-named
877
+ // variant back to its base, because naming one is how a caller would
878
+ // pick the provider and skip ordered slot fill and the slot ceiling. The coercion is right; the
879
+ // leak that provokes it is not.
872
880
  //
873
881
  // `mode` is the only field that filter respects, and setting it here is
874
882
  // safe: the task tool never reads an agent's mode -- it resolves the
package/dist/commands.js CHANGED
@@ -93,7 +93,7 @@ context file only if this work introduced or revealed information that will
93
93
  materially help future agents \u2014 a new boundary or module, a new or moved
94
94
  canonical entry point, a changed workflow, a new convention, an architectural
95
95
  decision, a new dependency between subsystems, a renamed or moved subsystem,
96
- or a previously undocumented invariant.
96
+ or an invariant not yet recorded.
97
97
 
98
98
  Do not edit documentation just because source files changed. No
99
99
  timestamp-only edits. If nothing material changed, make no edits and report
@@ -180,11 +180,11 @@ The pool is ONE global slot table shared by every pooled base (explore, implemen
180
180
  2. Resolve the instruction above into a target slot table. These are the rules the loader enforces \u2014 never write a file that breaks one:
181
181
  - one slot per line: \`<provider>/<model-id> [weight]\`
182
182
  - at most ${MAX_SLOTS} slots, and a model may appear only once
183
- - weight is optional (default 1) and must be a number in (0, ${MAX_WEIGHT}]. It is the slot's share of the rotation \u2014 how many turns it takes per cycle \u2014 and its concurrency ceiling is twice that. NOT a priority; pick order follows line order.
183
+ - weight is optional (default 1) and must be a number in (0, ${MAX_WEIGHT}]. It is the slot's active-session budget for one pass \u2014 how many turns it takes per cycle \u2014 and its concurrency ceiling is twice that. NOT a priority; pick order follows line order.
184
184
  - \`#\` starts a comment (whole-line or trailing); blank lines are ignored.
185
185
  If the instruction names a model ambiguously, or would need more than ${MAX_SLOTS} slots, stop and ask rather than guessing.
186
186
 
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.
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 slots. Keep the existing comment header. If the write fails on a missing directory, create \`~/.config/lacode\` first and retry.
188
188
 
189
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
 
@@ -207,6 +207,14 @@ $ARGUMENTS
207
207
  - Otherwise call \`pool_recheck\` with no arguments; it probes every model that is currently cooling.
208
208
 
209
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.`
210
+ },
211
+ "image-in-cb": {
212
+ description: "Attach the clipboard image to this message \u2014 macOS needs pngpaste, Linux needs wl-paste or xclip.",
213
+ template: "$ARGUMENTS"
214
+ },
215
+ "image": {
216
+ description: "Attach one or more image files to this message",
217
+ template: "$ARGUMENTS"
210
218
  }
211
219
  };
212
220
  function applyCommands(cfg, commands = COMMANDS) {