@vikrant82/opencode-cache-keepalive 0.1.5 → 0.2.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.
Files changed (46) hide show
  1. package/MIGRATION.md +57 -0
  2. package/README.md +75 -48
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1097 -614
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/config.d.ts +8 -25
  7. package/dist/lib/config.d.ts.map +1 -1
  8. package/dist/lib/control.d.ts +10 -4
  9. package/dist/lib/control.d.ts.map +1 -1
  10. package/dist/lib/paths.d.ts +5 -12
  11. package/dist/lib/paths.d.ts.map +1 -1
  12. package/dist/lib/replay-warmer.d.ts +95 -0
  13. package/dist/lib/replay-warmer.d.ts.map +1 -0
  14. package/dist/lib/state.d.ts +15 -114
  15. package/dist/lib/state.d.ts.map +1 -1
  16. package/dist/lib/tui/commands.d.ts +2 -0
  17. package/dist/lib/tui/commands.d.ts.map +1 -1
  18. package/dist/lib/tui/footer.d.ts +5 -5
  19. package/dist/lib/tui/footer.d.ts.map +1 -1
  20. package/dist/lib/tui/format.d.ts +8 -0
  21. package/dist/lib/tui/format.d.ts.map +1 -1
  22. package/dist/lib/types.d.ts +123 -0
  23. package/dist/lib/types.d.ts.map +1 -0
  24. package/dist/tui.d.ts +3 -0
  25. package/dist/tui.d.ts.map +1 -1
  26. package/lib/config.ts +106 -74
  27. package/lib/control.ts +69 -20
  28. package/lib/paths.ts +12 -14
  29. package/lib/replay-warmer.ts +1200 -0
  30. package/lib/state.ts +172 -175
  31. package/lib/tui/commands.tsx +76 -0
  32. package/lib/tui/footer.tsx +55 -130
  33. package/lib/tui/format.ts +40 -0
  34. package/lib/types.ts +130 -0
  35. package/package.json +3 -2
  36. package/tui.tsx +40 -0
  37. package/dist/lib/keepalive.d.ts +0 -85
  38. package/dist/lib/keepalive.d.ts.map +0 -1
  39. package/dist/lib/model.d.ts +0 -8
  40. package/dist/lib/model.d.ts.map +0 -1
  41. package/dist/lib/system.d.ts +0 -11
  42. package/dist/lib/system.d.ts.map +0 -1
  43. package/lib/keepalive.ts +0 -636
  44. package/lib/model.ts +0 -19
  45. package/lib/system.ts +0 -19
  46. package/lib/tui/commands.ts +0 -59
package/MIGRATION.md ADDED
@@ -0,0 +1,57 @@
1
+ # 0.2.0 migration and release notes
2
+
3
+ > These notes describe the 0.2.0 package contents prepared for release. They do not imply that the package has been published, nor validated savings or provider cache TTLs.
4
+
5
+ ## What changes
6
+
7
+ The prior synthetic `~` ping approach is replaced with replay of the latest eligible real model request. The request is sent through the original `fetch` unchanged and may be retained only in process memory for replay. Replays stop at API-specific SSE checkpoints, rather than completing a generated response. No request URL, headers, body, credentials, or conversation text are persisted.
8
+
9
+ The wrapper observes calls to global `fetch`, then forwards them to the original `fetch` with the same arguments and returns its result. It records only requests that pass eligibility checks; unrelated global traffic is forwarded unchanged. Replay payload capture is limited to POSTs at allowed host suffixes and supported API paths ending in `/v1/messages`, `/v1/responses` or `/responses`, or `/chat/completions`, with a recognized session ID (`X-Session-Id`, `x-session-affinity`, or `X-Interaction-Id`), a JSON request body, and a model containing an `intervals` key. The default host suffix is `githubcopilot.com`; default model substrings are `claude` (285,000 ms) and `gpt` (1,680,000 ms). First matching interval key wins, case-insensitively. Child sessions are excluded unless `includeChildSessions` is enabled. These are code eligibility rules, not a claim of provider-wide support.
10
+
11
+ An observed request for an enabled session on an allowed host begins a replay gap and resets its successful replay count; only eligible POSTs on supported paths become replay payloads. A successful replay is capped by `maxReplaysPerGap`; `"auto"` calculates `floor((missFactor - cacheReadFactor) / cacheReadFactor)` (9 at defaults). This is a configurable input-cost estimate, not verified provider pricing or net savings. The displayed avoided cache-write tokens are gross; refresh cached-read tokens are separate usage and gross display values do not subtract that cost. Token usage may be API-specific or unavailable. No universal replay expiry is promised: stop/lapse conditions include a fresh real request, reaching the cap, HTTP rejection, a Messages cache-creation-only replay, retry exhaustion, session disablement, or request-payload eviction at the in-memory storage budget. Network/timeout/other non-OK failures retry once after 10 seconds, then lapse; HTTP 400/401/403 stop immediately. The default replay timeout is 60 seconds and `maxStoredBytes` defaults to 67,108,864 bytes (64 MiB).
12
+
13
+ Session `/keepalive-on`, `/keepalive-off`, and `/keepalive-toggle` are runtime controls. The global `enabled` option remains the master switch. Disabling clears the saved request payload; re-enabling does not recover it, so a fresh eligible real request is required. Per-session controls use `control-<directory-hash>.json` under OpenCode's plugin storage directory and expire after 30 days. Version 1 folder-wide controls are ignored. Version 2 state snapshots hold telemetry/process totals, not replay payloads; state files are associated with a process and stale-process cleanup, while `server.log` may contain session IDs, model/API/host/path, replay status/latency, and token-usage metadata.
14
+
15
+ ## Option changes
16
+
17
+ Plugin options explicitly supplied in `opencode.json` take precedence over matching environment variables; environment values otherwise override defaults. These old options are ignored with deprecation warnings:
18
+
19
+ - `intervalMs` / `intervalSeconds` → configure `intervals` (model substring to milliseconds).
20
+ - `windowMs` / `windowMinutes` → no universal warm-window setting; warming follows the gap, cap, stop, and lapse rules above.
21
+ - `providerAllowlist` / `modelAllowlist` → configure `hosts` / `intervals`.
22
+ - `revertPing`, `pingToken`, `injectSystemInstruction`, `claudeBusyWarm`, `claudeBusyWarmIntervalMs`, `claudeBusyWarmWindowMs` → removed with synthetic pings.
23
+ - `/keepalive-interval` → removed; configure `intervals` before startup.
24
+
25
+ Existing runtime control files with version 1 folder-level `enabled` values are ignored; they are not migrated. State v1 files are ignored and not deleted by v2 cleanup. Back up any settings you need, configure the new options, and use session controls as desired.
26
+
27
+ ## Loading and updating
28
+
29
+ The package exposes a server entry (`.` / `./server`) and a separate TUI module (`./tui`). The loader contract is source-verified against OpenCode v1.18.33: `opencode.json` loads server plugins, `tui.json`/`tui.jsonc` loads TUI plugins, and package `./server` or `./tui` exports are selected by plugin kind. The plugin config accepts a package string or `[package, options]`; options belong in the server registration. See [TUI configuration docs](https://opencode.ai/docs/tui/), [server config schema](https://opencode.ai/config.json), [TUI config schema](https://opencode.ai/tui.json), and [v1.18.33 plugin loader source](https://github.com/anomalyco/opencode/tree/v1.18.33/packages/opencode/src/plugin). This source verification does not establish live plugin integration.
30
+
31
+ After 0.2.0 is published, register the same pinned package specifier (`@vikrant82/opencode-cache-keepalive@0.2.0`) in both files; the specifier is available only after publication. For local development, `opencode.json` can use `./dist/index.js`, while `tui.json` can use `./tui.tsx` relative to that config file; the package's `./tui` export intentionally targets TypeScript source, not an emitted JavaScript bundle.
32
+
33
+ The v1.18.33 source contract supports the README's server plugin-options tuple. It differs from the installed SDK declaration previously inspected (`Config.plugin: string[]`); the source-tagged OpenCode schema and loader are the basis for this documentation.
34
+
35
+ The previously stated OpenCode `>=1.4.3` peer baseline and Node `>=18` README baseline are not validated support ranges (`package.json` has no Node `engines` field). The observed check environment was Node 22.22.3, OpenCode 1.18.33, `@opencode-ai/plugin` 1.17.16, and `@opencode-ai/sdk` 1.17.16; live integration remains untested.
36
+
37
+ After 0.2.0 is published, an installation update can be pinned explicitly, for example:
38
+
39
+ ```bash
40
+ npm install @vikrant82/opencode-cache-keepalive@0.2.0
41
+ ```
42
+
43
+ This is an example for use only after publication; it does not assert that publication has occurred. Package metadata in this source tree is prepared as 0.2.0.
44
+
45
+ ## Uncompleted release checklist
46
+
47
+ - [x] Verify separate server/TUI loading contract from OpenCode v1.18.33 source and published config documentation.
48
+ - [ ] **Accepted/deferred risk:** live footer visibility and session toggles were observed by the human, but final isolated-build loading/integration was not established. Loader contract verification alone does not confirm plugin integration.
49
+ - [x] **Review finding — Request body capture:** fixed by synchronously cloning eligible `Request` bodies before forwarding and verified with a consuming-transport replay regression.
50
+ - [x] **Review finding — multi-instance routing:** fixed by selecting unknown-session owners against host/model eligibility and keeping known ownership authoritative; disjoint-host, overlapping-config, and disabled-owner regressions pass.
51
+ - [x] **Request stream rejection after immediate disposal:** attached an immediate rejection observer while preserving rejection for recorder error handling; focused stream-error regression passes.
52
+ - [ ] Verify migration behavior and config precedence in the release package, including ignored legacy options and v1 control/state files.
53
+ - [ ] Review privacy boundaries in persisted state and logs for the release build.
54
+ - [ ] Validate replay lifecycle, API checkpoints, rejection/retry behavior, storage cap, and cost/usage reporting against intended supported environments; do not claim TTL, savings, or provider support without evidence.
55
+ - [x] Prepare package metadata and lockfile for 0.2.0 and include `MIGRATION.md` in package files; publication remains a separate delivery action.
56
+ - [ ] In a dedicated disposable TTY session, smoke-test idle/tool-busy replay and active-model suppression; cap, rejection, and restart behavior; absence of synthetic conversation turns; sanitized state files and logs. Final isolated-build loading was not established; retain the accepted/deferred live UI integration risk until verified.
57
+ - [ ] After publication, verify the published archive/version; no publication is performed here.
package/README.md CHANGED
@@ -4,22 +4,21 @@
4
4
  [![License: AGPL-3.0-or-later](https://img.shields.io/badge/License-AGPL--3.0--or--later-blue.svg)](./LICENSE)
5
5
  [![OpenCode Plugin](https://img.shields.io/badge/OpenCode-Plugin-purple.svg)](https://github.com/opencode-ai/plugin)
6
6
 
7
- Keep LLM prompt caches warm during idle periods with invisible pings and a live TUI readout.
7
+ Replay the last real model request to keep supported prompt caches warm during idle and tool-busy gaps.
8
8
 
9
9
  ## Why This Exists
10
10
 
11
- - Provider prompt caches (Copilot, Anthropic, etc.) expire after ~5-60 minutes of inactivity
12
- - Next turn pays full input token cost + latency penalty on cache miss
13
- - This plugin sends minimal `~` pings during idle windows to keep the cached prefix alive at negligible cost
11
+ - Prompt caches expire after provider-specific inactivity windows; a later turn can pay full input cost
12
+ - A global `fetch` wrapper passes every request to the original `fetch` unchanged; it only records eligible requests for later replay
13
+ - Replays are bounded per gap by a break-even cost cap; ordinary real requests are not delayed, rewritten, or replaced
14
14
 
15
15
  ## Features
16
16
 
17
- - **Automatic warm window**: arms after real turns, expires after configurable window (default 55min)
18
- - **Live TUI footer**: idle time, pings sent/left, last ping cache hit/miss with token counts
19
- - **Synthetic ping reversion**: removes `~`/`~` turns from conversation history
17
+ - **Idle and tool-busy warming**: refreshes the current session's replayed cache without adding conversation turns
18
+ - **Live TUI footer**: cache state, next refresh, refresh count, gross avoided cache-write tokens, and resume hit ratio
19
+ - **Privacy-first persistence**: session telemetry only; request URL, headers, body, credentials, and conversation text are not stored
20
20
  - **Runtime toggle**: `/keepalive-toggle`, `/keepalive-on`, `/keepalive-off` slash commands
21
- - **Secure mode support**: works with `OPENCODE_SERVER_PASSWORD`
22
- - **Configurable provider/model allowlists**
21
+ - **Per-model timing**: Claude and GPT model substrings have configurable intervals; allowed host suffixes limit eligible capture
23
22
 
24
23
  ## Installation
25
24
 
@@ -27,7 +26,7 @@ Keep LLM prompt caches warm during idle periods with invisible pings and a live
27
26
  npm install @vikrant82/opencode-cache-keepalive
28
27
  ```
29
28
 
30
- Register in your `opencode.json`:
29
+ Register the server plugin in `opencode.json`:
31
30
 
32
31
  ```json
33
32
  {
@@ -35,6 +34,16 @@ Register in your `opencode.json`:
35
34
  }
36
35
  ```
37
36
 
37
+ Register the same package in `tui.json` to load its separate TUI entry:
38
+
39
+ ```json
40
+ {
41
+ "plugin": ["@vikrant82/opencode-cache-keepalive"]
42
+ }
43
+ ```
44
+
45
+ OpenCode's v1.18.33 loader reads plugin lists from these separate config files and resolves package `./server` and `./tui` exports by plugin kind. When installing the published 0.2.0 release, use the same pinned package specifier in both files; availability depends on publication.
46
+
38
47
  ### Local Development
39
48
 
40
49
  To use a locally built version during development:
@@ -45,11 +54,19 @@ To use a locally built version during development:
45
54
  }
46
55
  ```
47
56
 
48
- Run `npm run build` first, then restart opencode. Rebuild and restart after code changes.
57
+ In `tui.json`, load the source TUI entry separately:
58
+
59
+ ```json
60
+ {
61
+ "plugin": ["./tui.tsx"]
62
+ }
63
+ ```
64
+
65
+ Paths are relative to the declaring config file. Run `npm run build` for the server bundle, then restart OpenCode; `./tui` is intentionally exported as TypeScript source for the TUI loader, not an emitted `dist/tui.js` file.
49
66
 
50
67
  ## Configuration
51
68
 
52
- All options can be set via plugin config in `opencode.json` or environment variables. Env vars take precedence.
69
+ Options can be set in the plugin options in `opencode.json` or with the listed environment variables. An explicitly provided plugin option takes precedence over its environment variable; otherwise the environment variable overrides the default.
53
70
 
54
71
  ```json
55
72
  {
@@ -57,9 +74,8 @@ All options can be set via plugin config in `opencode.json` or environment varia
57
74
  [
58
75
  "@vikrant82/opencode-cache-keepalive",
59
76
  {
60
- "intervalMs": 300000,
61
- "windowMs": 3600000,
62
- "providerAllowlist": ["copilot", "anthropic"],
77
+ "intervals": { "claude": 285000, "gpt": 1680000 },
78
+ "hosts": ["githubcopilot.com"],
63
79
  "debug": true
64
80
  }
65
81
  ]
@@ -67,57 +83,68 @@ All options can be set via plugin config in `opencode.json` or environment varia
67
83
  }
68
84
  ```
69
85
 
70
- | Option | Default | Env Var | Description |
71
- | ------------------------- | ------------------------------------------------------ | -------------------------------- | ----------------------------------------------------------------- |
72
- | `enabled` | `true` | `OPENCODE_KEEPALIVE_ENABLED` | Master switch. When false, no timers or hooks are registered. |
73
- | `intervalMs` | `270000` (4.5min) | `OPENCODE_KEEPALIVE_INTERVAL_MS` | Milliseconds between pings. Must stay under provider cache TTL. |
74
- | `windowMs` | `3300000` (55min) | `OPENCODE_KEEPALIVE_WINDOW_MS` | How long to keep warming after last real response. |
75
- | `pingToken` | `"~"` | — | Single token sent as user message; model replies with same token. |
76
- | `revertPing` | `true` | `OPENCODE_KEEPALIVE_REVERT_PING` | Remove synthetic ping turns from session history. |
77
- | `injectSystemInstruction` | `true` | — | Append stable instruction so model answers `~` with single token. |
78
- | `includeChildSessions` | `false` | — | Warm subagent/child sessions too. Off by default. |
79
- | `debug` | `false` | `OPENCODE_KEEPALIVE_DEBUG` | Enable verbose logging to server.log. |
80
- | `providerAllowlist` | `["copilot"]` | — | Provider ID substrings eligible for warming. |
81
- | `modelAllowlist` | `["claude","anthropic","sonnet","opus","haiku","gpt"]` | — | Model ID substrings eligible for warming. |
86
+ The tuple-with-options form is supported by OpenCode's v1.18.33 plugin config schema (`string` or `[package, options]`). The options example applies to the server plugin configuration in `opencode.json`.
87
+
88
+ | Option | Default | Env Var | Description |
89
+ | ---------------------- | ----------------------------- | ------------------------------------------- | ----------------------------------------------------------------------------- |
90
+ | `enabled` | `true` | `OPENCODE_KEEPALIVE_ENABLED` | Master switch. |
91
+ | `intervals` | `{claude:285000,gpt:1680000}` | `OPENCODE_KEEPALIVE_INTERVALS` | Milliseconds per matching model substring; first case-insensitive match wins. |
92
+ | `hosts` | `["githubcopilot.com"]` | `OPENCODE_KEEPALIVE_HOSTS` | Allowed hostname suffixes. |
93
+ | `cacheReadFactor` | `0.1` | `OPENCODE_KEEPALIVE_CACHE_READ_FACTOR` | Relative cost of cached input. |
94
+ | `missFactor` | `1.0` | `OPENCODE_KEEPALIVE_MISS_FACTOR` | Relative cost of uncached input. |
95
+ | `maxReplaysPerGap` | `"auto"` (9) | `OPENCODE_KEEPALIVE_MAX_REPLAYS_PER_GAP` | Auto cap is `floor((missFactor-cacheReadFactor)/cacheReadFactor)`. |
96
+ | `includeChildSessions` | `false` | `OPENCODE_KEEPALIVE_INCLUDE_CHILD_SESSIONS` | Include child sessions. |
97
+ | `replayTimeoutMs` | `60000` | `OPENCODE_KEEPALIVE_REPLAY_TIMEOUT_MS` | Maximum time for one replay attempt. |
98
+ | `maxStoredBytes` | `67108864` (64 MiB) | `OPENCODE_KEEPALIVE_MAX_STORED_BYTES` | Maximum in-memory request-body storage for replay targets. |
99
+ | `debug` | `false` | `OPENCODE_KEEPALIVE_DEBUG` | Verbose server log output. |
82
100
 
83
101
  ## TUI Integration
84
102
 
85
- The plugin registers a sidebar footer showing live keepalive status:
103
+ The package contains separate server and TUI entries. Source-verified against OpenCode v1.18.33: server plugins load from `opencode.json`; TUI plugins load from `tui.json` (or `tui.jsonc`) using the package's `./tui` export. See the [TUI configuration docs](https://opencode.ai/docs/tui/), [server config schema](https://opencode.ai/config.json), [TUI config schema](https://opencode.ai/tui.json), and [v1.18.33 plugin loader source](https://github.com/anomalyco/opencode/tree/v1.18.33/packages/opencode/src/plugin). The human has observed the footer and commands in a live UI; loading the final isolated build and its UI integration were not established, so treat that integration as an open release risk.
104
+
105
+ When loaded, the sidebar footer is hidden until a session has a persisted replay entry. It reports:
86
106
 
87
107
  ```
88
- keepalive armed
89
- idle 03:42 · sent 2 · left 10
90
- ✓ hit input 12.4k · read 11.8k · write 0
108
+ keepalive idle · 2/9 · next 4:45
109
+ refreshes 2 · hits 1/1 · saved ~49k tok
91
110
  ```
92
111
 
93
112
  Metrics:
94
113
 
95
- - **idle**: Time since last real assistant response
96
- - **sent**: Ping attempts in current warm window (includes failures)
97
- - **left**: Estimated pings remaining before window expires
98
- - **hit/miss**: Cache hit status of last ping with token breakdown
114
+ - **state**: `keepalive standby · model working`, `keepalive idle/busy`, a refresh stop/expiry reason, or `keepalive off`
115
+ - **refresh**: Time until next refresh, or successful refresh count in the current gap
116
+ - **metrics**: Session refresh count, resume hits/count when available, and gross cache-write tokens avoided. “Saved” is gross and does not subtract refresh cost; replay cache-read token usage is logged in `server.log` as `cacheRead` when the API exposes it, and resume accounting logs `replayRead`.
99
117
 
100
118
  Slash commands (available in TUI palette):
101
119
 
102
- - `/keepalive-toggle` — Toggle keepalive on/off at runtime
103
- - `/keepalive-on` — Enable keepalive
104
- - `/keepalive-off` — Disable keepalive
120
+ - `/keepalive-toggle` — Toggle keepalive for the current session only
121
+ - `/keepalive-on` — Enable keepalive for the current session
122
+ - `/keepalive-off` — Disable keepalive for the current session
123
+
124
+ Runtime overrides are stored by session in the project's `control-<directory-hash>.json` file. `/keepalive-off` aborts scheduled/in-flight replays and discards the in-memory replay payload. `/keepalive-on` permits future capture but does not restore that payload: make a fresh eligible real request to resume warming. Legacy folder-wide version 1 `enabled` values are ignored; the config `enabled` option remains the global master switch. Child sessions are excluded unless `includeChildSessions` is enabled.
105
125
 
106
126
  ## How It Works
107
127
 
108
- 1. Real turn completes → `session.idle` event → engine arms warm window
109
- 2. Every `intervalMs` (with ±15s jitter), engine sends `~` as user message
110
- 3. Model replies with `~` (single token); engine reads `tokens.cache.read` to detect hit
111
- 4. Synthetic `~`/`~` turn is reverted from session history (if `revertPing` enabled)
112
- 5. Window closes after `windowMs` or when a new real turn starts
113
- 6. Server plugin persists state to disk; TUI plugin polls it every 1s for live readout
128
+ 1. A request passes through the wrapper to the original `fetch` unchanged. An eligible request is a POST to a configured host suffix and supported API path, with a session ID and a model matching an `intervals` key; child sessions are excluded by default. Supported path suffixes are `/v1/messages`, `/v1/responses` or `/responses`, and `/chat/completions` (optional trailing slash).
129
+ 2. During idle or tool-busy gaps, the engine replays the current session request at its model's interval. It aborts after an API-specific SSE checkpoint: Messages `message_start`, Responses' first event other than `response.created`/`response.in_progress`, or the first Chat Completions chunk.
130
+ 3. A new real request starts a gap and resets its replay counter; actual eligible requests replace the saved payload. Successful replays stop at the auto (or numeric) cap. Network errors, timeouts, and other non-OK statuses get one retry after 10 seconds, then lapse; HTTP 400/401/403 reject and stop immediately. For Messages, a replay reporting zero cache-read and positive cache-creation tokens lapses that session. These are event/status-driven stops, not a universal TTL; the footer's “cache expired” label indicates a lapse, not a measured provider TTL.
131
+ 4. The next real completion for that session reports whether the cache hit; “saved” counts gross cache-write tokens avoided (prompt tokens on a hit, zero on a miss). Refresh cache-read cost is reported separately in `server.log` as `replayRead`.
132
+ 5. The server writes sanitized v2 per-process telemetry files; payloads (including URL, headers, and body) remain only in memory. The TUI reads the freshest session entry and polls every second. Process state files are cleaned up with their process lifecycle/stale-process cleanup; session control overrides are separately persisted and ignored after 30 days.
133
+
134
+ ### Cost model and cache cap
135
+
136
+ With cached reads costing `cacheReadFactor` and a full miss costing `missFactor`, the automatic replay cap is `floor((missFactor - cacheReadFactor) / cacheReadFactor)`. At defaults, one full-cache miss costs 1.0 input units, a cached read costs 0.1, and the maximum is 9 successful replays per gap. The estimate is an input-token-equivalent comparison, not a billing guarantee; provider pricing and cache-write rules can differ.
137
+
138
+ ### Privacy and upgrade notes
139
+
140
+ The replay request (including its URL, headers, and body) is held only in process memory and is never written to disk. State files contain session telemetry and process totals; `server.log` can contain session IDs, model/API/host/path, replay outcomes/status/latency, and token-usage metadata, but not request bodies, credentials, or conversation text. Legacy/v1 state files are ignored and not removed by v2 cleanup. Version 0.2.0 removes synthetic `~` messages and their system prompt instruction. This is a breaking behavior change: old `intervalMs`/`intervalSeconds`, `windowMs`/`windowMinutes`, `revertPing`, `pingToken`, `injectSystemInstruction`, provider/model allowlists, and Claude busy-warm options are ignored with warnings; runtime `/keepalive-interval` is removed. Configure `intervals` and `hosts` instead. See [MIGRATION.md](./MIGRATION.md) for migration notes and release checks.
114
141
 
115
142
  ## Compatibility
116
143
 
117
- - **OpenCode**: >=1.4.3
118
- - **Node**: >=18 (ESM)
119
- - **Supported providers**: Copilot (default), Anthropic, OpenAI (via allowlists)
120
- - **Models**: Claude, GPT families (via allowlists)
144
+ - **Previously stated/declared baseline, not tested support range**: OpenCode >=1.4.3 (peer dependency); Node >=18 was previously stated but `package.json` has no `engines` field. This checkout's observed environment was Node 22.22.3, OpenCode 1.18.33, `@opencode-ai/plugin` 1.17.16, and `@opencode-ai/sdk` 1.17.16. Live plugin/TUI integration is untested, and compatibility across the declared ranges has not been validated.
145
+ - **Supported hosts by default**: `githubcopilot.com` and its subdomains; configure other allowed host suffixes explicitly
146
+ - **Supported API paths**: `/v1/messages`, `/v1/responses` or `/responses`, and `/chat/completions`
147
+ - **Eligible models**: request model string must contain a configured `intervals` key; defaults are `claude` and `gpt` (case-insensitive, first matching key wins)
121
148
 
122
149
  ## Development
123
150
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAYjD,QAAA,MAAM,MAAM,EAAE,MA8Cb,CAAA;AAED,eAAe,MAAM,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAUjD,QAAA,MAAM,MAAM,EAAE,MAwBb,CAAA;AAED,eAAe,MAAM,CAAA"}