opencode-lossless 0.1.2 → 0.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # opencode-lossless
2
2
 
3
+ ## 0.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 9c0fdc5: Harden `lossless-gc` against deleting sidecars that are live in another process. Sweeps skip stores active within the last 60 minutes and name them. Bare `--older-than 0` without `--session` is refused. `--session` on a store active within 60 minutes needs `--force`. Candidates younger than 24 hours carry a warning.
8
+ - 470a7b7: Verify file bytes and sha256 against the linked message text before inserting file rows on import. A repeated entry id with different text, or a file row whose text differs from the stored message, also refuses the import without writing. Existing rows stay untouched.
9
+ - 470a7b7: Add a mechanical tier for noisy tools. Name tools in `mechanicalTools` in summarizer-config, `/lcm` config, or the OpenCode plugin options. Their spans compact into deterministic `mechanical` leaves with zero summarizer spend at compaction time. Every message stays stored verbatim and covered, so the integrity coverage check passes, and heal re-derives a skipped leaf from provenance on demand. There is no ingest filtering.
10
+ - 9c0fdc5: Change the default `sidecarDir` to the auto-detected global path. The plugin uses `XDG_DATA_HOME` with a `~/.local/share` fallback, uses `LOCALAPPDATA` with an `APPDATA` fallback and then a `<home>/AppData/Local` fallback on Windows, and uses `./.lossless` when no home resolves. To keep per-project storage, set `"sidecarDir": "./.lossless"`. After upgrading a project that used `./.lossless`, keep that setting to keep its history: `/lossless-migrate` upgrades in place in the configured directory only (dry run by default) and moves nothing, so it cannot recover history left in `./.lossless`. To carry history across, export per session under the old config and import into an empty store under the new one, or stop the server first and copy the `.sqlite`, `-wal`, and `-shm` files across and then migrate. The plugin never moves sidecars.
11
+ - 470a7b7: Report orphan sidecars in doctor. `integritySnapshot` now lists sidecar files and stale `wal` and `shm` files with no live session (`.sqlite` by default, `.db` for Pi). `checkIntegrity` reports them as a read-only `orphan-sidecar` finding through `lcm doctor` and the diagnose bundle. Report only. Deletion stays manual.
12
+ - 470a7b7: Upgrade recall search. `lcm_grep` takes `role`, `date_start`/`date_end`, and `tool_name` filters. Role and time are SQL predicates on the stored columns with a covering index. Tool scope is an FTS rewrite against `[tool:Name]` markers with no new table or index. `lcm_expand` takes an `anchor` entry id that centers the listing window with edge clamping. The recall manifest and the retrieval brief order by stop-word stripped query-word overlap over summary headers. A read-only files view lists each linked file once with an incomplete-coverage flag and a `large-inline` mark. No migration and no new writer.
13
+ - 470a7b7: Add a read-only static sidecar dump. `/lcm dump` and `/lossless-dump` open the session sidecar `queryOnly` and reuse the recall views and metric rows. The dump redacts displayed seams, labels removed rows, and reports old generations as named skips. Output is capped at 16 KB.
14
+ - 0c70748: Ask providers for a whole number of tokens. A leaf target was half of the calibrated chunk budget, so it was fractional whenever that budget was odd, and a provider that validates `max_tokens` as an unsigned integer rejected the call with a 422. The ladder then stored the deterministic truncate: in one week of Pi sessions that was 1,315 of 5,358 summaries, with 540 rejected calls naming values such as `max_tokens: 5543.5`. The request builder now rounds to nearest, never below one, and treats a non-finite budget as that floor, before the reasoning headroom is added. Integer budgets produce the same request as before. No config key, migration or metric row changes.
15
+
3
16
  ## 0.1.2
4
17
 
5
18
  ### Patch Changes
package/README.md CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  **Lossless Context Management (LCM) for OpenCode v2. Long sessions keep working past the point where the model would forget, and every verbatim message stays searchable and readable.**
4
4
 
5
- Install it, and the adapter mirrors every prompt, assistant reply, and tool exchange into a per-session SQLite sidecar. The model reads that history back through `lcm_grep`, `lcm_describe`, and `lcm_expand`; with `assembly` true (the default) it also gets `lcm_expand_query`, which answers through a child session without pulling the originals into the parent. With assembly on, the adapter also owns the context boundary: past the swap point the DAG projection replaces the aged history, and when the host compacts, the adapter hands it the frontier as the checkpoint instead of letting it summarize.
5
+ Install it, and the adapter mirrors every prompt, assistant reply, and tool exchange into a per-session SQLite sidecar. The model reads that history back through `lcm_grep`, `lcm_describe`, and `lcm_expand`. With `assembly` true (the default) it also gets `lcm_expand_query`, which answers through a child session without pulling the originals into the parent. With assembly on, the adapter also owns the context boundary. Past the swap point the DAG projection replaces the aged history, and when the host compacts, the adapter hands it the frontier as the checkpoint instead of letting it summarize.
6
6
 
7
7
  This package is the OpenCode v2 adapter over
8
8
  [`lossless-core`](https://npmx.dev/package/lossless-core), the engine that
9
9
  ingests the history and builds the summaries. Verified against OpenCode v2 in
10
- live sessions: the plugin loads from its directory, all mirrored text matches
10
+ live sessions. The plugin loads from its directory, all mirrored text matches
11
11
  verbatim, and host compactions store the DAG frontier as their checkpoint.
12
12
 
13
13
  ## Install
@@ -20,14 +20,16 @@ The equivalent entry in `opencode.json`:
20
20
 
21
21
  ```json
22
22
  {
23
- "plugins": [{ "package": "opencode-lossless", "options": { "sidecarDir": "./.lossless" } }]
23
+ "plugins": [{ "package": "opencode-lossless" }]
24
24
  }
25
25
  ```
26
26
 
27
+ To keep sidecars inside the project, set `sidecarDir` to `"./.lossless"`.
28
+
27
29
  ## Quick start
28
30
 
29
- Zero config required: passive mirroring starts immediately, and swaps begin
30
- once context crosses the swap point. Ten commands, all `lossless-*`:
31
+ The plugin needs no configuration. Passive mirroring starts immediately, and swaps begin
32
+ once context crosses the swap point. The commands all start with `lossless-*`:
31
33
 
32
34
  ```sh
33
35
  /lossless-status # message and compaction summary counts per live sidecar
@@ -40,11 +42,14 @@ once context crosses the swap point. Ten commands, all `lossless-*`:
40
42
  /lossless-diagnose # redacted, pasteable diagnostics block; --save writes it at mode 0600
41
43
  /lossless-migrate # upgrade older-schema sidecars in place (dry-run by default)
42
44
  /lossless-gc # list old sidecars, or export and delete them (never the current session)
45
+ /lossless-dump # read-only dump of this session's sidecar; --include-removed keeps removed rows, --save writes mode 0600
43
46
  ```
44
47
 
45
- The model gets four recall tools: `lcm_grep` (search this session, `scope`
46
- reaches past sessions), `lcm_describe` (inspect one summary without expanding
47
- it), `lcm_expand` (read the originals a summary covers), and
48
+ The model gets recall tools: `lcm_grep` (search this session, `scope`
49
+ reaches past sessions, `role`, `date_start`, `date_end`, and `tool_name`
50
+ filter rows), `lcm_describe` (inspect one summary without expanding
51
+ it), `lcm_expand` (read the originals a summary covers, `anchor` centers
52
+ the listing on one entry), and
48
53
  `lcm_expand_query` (a bounded child session reads and answers, so the
49
54
  originals never enter the parent's context).
50
55
 
@@ -58,12 +63,12 @@ originals never enter the parent's context).
58
63
  }
59
64
  ```
60
65
 
61
- `sidecarDir` holds one SQLite file per session (0700 dir, 0600 files; delete
62
- one to forget that session). `assembly: false` leaves compaction to the host
63
- and drops `/lossless-pass` and `lcm_expand_query`. Every other key (thresholds,
64
- summarizer chain and budgets, heal window, reader timeout) lives on the
66
+ The example stores sidecars in the project. Without `sidecarDir`, the plugin uses the global auto-detected path, which holds sessions from every project. The [config reference](https://stainless-code.com/lossless/reference/opencode/config) lists the exact default paths. `sidecarDir` holds one SQLite file per session (0700 dir, 0600 files).
67
+ Delete one file to forget that session. `assembly: false` leaves compaction to the host
68
+ and drops `/lossless-pass` and `lcm_expand_query`. The
65
69
  [config reference](https://stainless-code.com/lossless/reference/opencode/config)
66
- with its default.
70
+ documents every other key and its default: thresholds,
71
+ summarizer chain and budgets, heal window, and reader timeout.
67
72
 
68
73
  ## Verify
69
74
 
package/dist/index.d.mts CHANGED
@@ -252,91 +252,44 @@ interface OpencodeContext {
252
252
  readonly generate?: GenerateText;
253
253
  readonly model?: ModelDomain;
254
254
  }
255
- /** Options read from the plugin's `plugins[].options` entry. Every field is
256
- * optional; an unknown or malformed value keeps its default rather than
257
- * failing the plugin load. */
258
255
  interface OpencodeLosslessOptions {
259
- /** Master flag. Nothing registers, mirrors, or mounts until true, unless
260
- * the flag is absent, in which case listing the plugin is intent enough.
261
- * @default true */
256
+ /** @default true */
262
257
  readonly enabled?: boolean;
263
- /** Directory for one sidecar file per session. Created when missing.
264
- * @default "./.lossless" (resolved from the server working directory) */
258
+ /** @default global sidecar dir, `./.lossless` only as fallback */
265
259
  readonly sidecarDir?: string;
266
- /** Wire the context projection, the blocking checkpoint, the reader, and the
267
- * on-demand `lossless-pass` command. `false` leaves compaction to the host and
268
- * registers only the mirror, the three recall tools, and the commands.
269
- * @default true
260
+ /** @default true
270
261
  * @example { assembly: false } */
271
262
  readonly assembly?: boolean;
272
- /** Models the pass tries for its summaries, in order. `auto` is the session
273
- * model; this host has no synchronous model lookup, so a concrete id falls
274
- * back to the session model.
275
- * @default "auto" */
263
+ /** @default "auto" */
276
264
  readonly summarizer?: string | string[];
277
- /** Tokens one summarizer request may cost, estimate plus output cap, before
278
- * it is refused rather than paid for.
279
- * @default 60000 */
265
+ /** @default 60000 */
280
266
  readonly summarizerTokensPerCall?: number;
281
- /** Tokens one pass may spend across all of its calls, counted from what the
282
- * provider reported.
283
- * @default 250000 */
267
+ /** @default 250000 */
284
268
  readonly summarizerTokensPerPass?: number;
285
- /** Tokens one `/lossless-heal --apply` run may spend across all of its calls.
286
- * A dry run spends nothing.
287
- * @default 120000 */
269
+ /** @default 120000 */
288
270
  readonly summarizerTokensPerHeal?: number;
289
- /** The occupancy share at which a request swaps in the projection. The
290
- * engine clamps it to 0.9 of the window.
291
- * @default 0.7 */
271
+ /** @default [] */
272
+ readonly mechanicalTools?: readonly string[];
273
+ /** @default 0.7 */
292
274
  readonly swapAtRatio?: number;
293
- /** The occupancy share at which a new cut replaces the pinned one.
294
- * @default 0.85 */
275
+ /** @default 0.85 */
295
276
  readonly recutAtRatio?: number;
296
- /** Absolute token count where model quality starts to degrade, used in
297
- * place of `swapAtRatio`. The recut is derived from it.
298
- * @default unset */
277
+ /** @default unset */
299
278
  readonly smartZone?: number;
300
- /** Per-model threshold overrides, keyed `provider/model`. */
301
279
  readonly zones?: Readonly<Record<string, ThresholdConfig>>;
302
- /** Tokens the projection keeps verbatim after the cut.
303
- * @default 20000 */
280
+ /** @default 20000 */
304
281
  readonly keepRecentTokens?: number;
305
- /** Tokens one summary aims for.
306
- * @default 1500 */
282
+ /** @default 1500 */
307
283
  readonly summaryTokens?: number;
308
- /** Estimated tokens one heal node may read as source. Defaults to
309
- * `8 * summaryTokens`, clamped to 3,000 through 24,000 and to
310
- * `summarizerTokensPerCall`. It is a whole-node window, not a per-message
311
- * clip. Raise it to repair summaries over unusually large source messages.
312
- * @default 8 x summaryTokens, clamped 3000..24000 */
284
+ /** @default 8 x summaryTokens, clamped 3000..24000 */
313
285
  readonly healSourceTokens?: number;
314
- /** Window used when the host catalogue cannot name this model's limit.
315
- * @default 128000 */
286
+ /** @default 128000 */
316
287
  readonly contextWindow?: number;
317
- /** How long one `lcm_expand_query` child may run before it is interrupted.
318
- * @default 120000 */
288
+ /** @default 120000 */
319
289
  readonly readerTimeoutMs?: number;
320
290
  }
321
- /** Wire lossless memory into an OpenCode v2 plugin context: mirror prompts,
322
- * assistant text, tool traffic, and compaction boundaries into per-session
323
- * sidecars, and register the recall tools plus the `/lossless-status`,
324
- * `/lossless-report`, `/lossless-pass`, and `/lossless-heal` commands. With
325
- * `assembly` (the default), also register the context projection and the blocking checkpoint
326
- * (`session.compaction`); `/lossless-pass` runs one DAG pass over the session's
327
- * stored span on demand. The host stops summarizing for itself once the sidecar
328
- * covers the aged span: it stores the DAG frontier as its checkpoint, and the
329
- * next request's projection continues from it. The sidecar keeps every address
330
- * the reader can name. Resolves to the cleanup that unregisters everything and
331
- * closes the sidecars. The context is the local structural subset
332
- * (`./host.ts`), so the real host context flows in without the bundle naming
333
- * the host SDK. `rawOptions` is validated at runtime; a malformed value
334
- * degrades to defaults rather than throwing into the host load. */
291
+ /** Wire lossless memory into an OpenCode v2 plugin context: mirror traffic into per-session sidecars and register recall tools plus lifecycle commands. Resolves to the cleanup that unregisters everything and closes the sidecars. */
335
292
  export declare function setupOpencodeLossless(ctx: OpencodeContext, rawOptions?: unknown): Promise<() => Promise<void>>;
336
- /** The plugin definition the host loads. Options come from the config entry,
337
- * so the default export reads them late, at setup time. The setup parameter
338
- * is the local context subset; the host's richer context satisfies it, which
339
- * `test/host-compat.test.ts` pins against the real host types. */
340
293
  declare const definition: {
341
294
  id: string;
342
295
  setup: (ctx: OpencodeContext) => Promise<() => Promise<void>>;