pi-kimi-keepalive 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 oliverlyu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # pi-kimi-keepalive
2
+
3
+ Prompt-cache keepalive for [Kimi](https://www.kimi.com/) (`kimi-coding` provider) sessions in the [Pi coding agent](https://github.com/earendil-works/pi-coding-agent).
4
+
5
+ Kimi's automatic prompt cache has an observed TTL of ~5 minutes. Once it expires, the next request re-reads the full context at full input price. This extension captures the last real provider request and replays it on a fixed interval while the session is idle, so the cached prefix stays warm and subsequent requests are billed at cache-read rates.
6
+
7
+ Replayed requests are sent directly to the provider endpoint. They do not pass through the Pi session pipeline: no synthetic messages, no model turns, no changes to conversation history. Only aggregate statistics are surfaced.
8
+
9
+ [中文文档](README.zh-CN.md)
10
+
11
+ ## Requirements
12
+
13
+ - Node ≥ 20, pi ≥ 0.84 (exposes the `before_provider_headers` / `before_provider_request` hooks)
14
+ - `kimi-coding` provider (OAuth subscription recommended), `kimi-openai-completions` API
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ git clone https://github.com/realoliversama/pi-kimi-keepalive
20
+ pi install /path/to/pi-kimi-keepalive
21
+ ```
22
+
23
+ For a single session without installing:
24
+
25
+ ```bash
26
+ pi -e /path/to/pi-kimi-keepalive/src/index.ts
27
+ ```
28
+
29
+ After the package is published to npm: `pi install npm:pi-kimi-keepalive`.
30
+
31
+ ## Setup
32
+
33
+ On the first start with no `~/.pi/cache-keepalive/state.json` present and an interactive UI, the extension runs a setup wizard that configures the four guardrails below. Leaving a prompt empty or pressing Esc keeps the default. The wizard can be rerun with `/keepalive setup`; in headless sessions it is skipped and defaults are kept.
34
+
35
+ | Step | Setting | Command | Default | Description |
36
+ | --- | --- | --- | --- | --- |
37
+ | 1 | Max idle cutoff | `maxidle` | `30m` | Probing stops after this much idle time; `0` disables the cutoff. |
38
+ | 2 | Miss pause threshold | `miss` | `1` | Pause after N consecutive probes that do not hit the prompt cache. A hit resets the count. |
39
+ | 3 | Error circuit breaker | `errors` | `3` | Pause after N consecutive probe failures (network errors, HTTP 5xx). HTTP 401/403 always pauses immediately. |
40
+ | 4 | Session spend cap | `cap` | `$1.00` | Ceiling on estimated USD probe spend per session; `0` removes the cap. |
41
+
42
+ The wizard ends with a prompt to enable keepalive. Probing starts after the next real turn, which provides the captured request.
43
+
44
+ Two operating points are worth knowing. The default cadence is **7 minutes** — deliberately past the ~5-minute cache TTL. A 7-minute probe never hits the cache: it runs once at full input price, confirms the cache has expired, and the default `miss=1` stops probing immediately, so the worst case is a single cold read per idle period. For an always-warm session instead, set a cadence inside the TTL (`/keepalive interval=4m45s`): every probe is then billed at cache-read rates (~1/10 of full price) and the loop keeps running until `maxidle` cuts it off.
45
+
46
+ ## Commands
47
+
48
+ ```
49
+ /keepalive status
50
+ /keepalive setup rerun the setup wizard
51
+ /keepalive on|off enable / disable (persisted)
52
+ /keepalive now one manual probe (bypasses pauses)
53
+ /keepalive resume clear a sticky pause
54
+ /keepalive interval=4m45s probe cadence (≥ 30s; ≤ 5m to stay inside the cache TTL)
55
+ /keepalive maxidle=30m idle cutoff (0 = disabled)
56
+ /keepalive miss=1 pause after N consecutive cache misses
57
+ /keepalive errors=3 pause after N consecutive probe failures
58
+ /keepalive cap=1.0 session probe-spend ceiling in USD (0 = none)
59
+ /keepalive token=512 minimum prompt size for miss classification
60
+ /keepalive maxoutput=16 probe max_tokens clamp
61
+ /keepalive reset zero the session stats
62
+ ```
63
+
64
+ All settings persist to `~/.pi/cache-keepalive/state.json`. Statistics are per-session.
65
+
66
+ ## How it works
67
+
68
+ 1. **Capture.** The `before_provider_request` hook snapshots a `structuredClone` of the payload of each real `kimi-coding` request. In-memory only; nothing is written to disk.
69
+ 2. **Replay.** While the agent is idle and enabled, the captured request is POSTed to `{baseUrl}/chat/completions` (kimi-openai-completions) with only terminal parameters changed:
70
+
71
+ | Change | Reason |
72
+ | --- | --- |
73
+ | remove `stream` / `stream_options` | returns usage as a single JSON body |
74
+ | remove `thinking` | avoids thinking-budget constraints against the output clamp below |
75
+ | remove `store` | probe output is unused; no need to persist it |
76
+ | `max_completion_tokens: 16` | bounds the probe's output cost |
77
+ | HTTP 400 retry | drops `prompt_cache_retention` (a terminal parameter, not part of the prefix) |
78
+
79
+ `messages`, `tools`, and Kimi's `prompt_cache_key` / `prompt_cache_retention` are kept byte-identical — the inputs to the provider's prefix-cache key — so the replay matches the existing cache entry and restarts its TTL at cache-read pricing. Verified live: a probe against a 48k-token session reports `use.prompt_tokens_details.cached_tokens × 48,116 / 48,116`.
80
+ 3. **Authentication.** pi injects the OAuth bearer token after the `before_provider_headers` hook fires, so captured headers usually lack auth. Probes read the current `kimi-coding.access` token from pi's auth store (`~/.pi/agent/auth.json`) at probe time, staying in sync with pi's token refreshes.
81
+ 4. **Classification.** Response usage (`prompt_tokens_details.cached_tokens`, falling back to Anthropic-style fields) classifies each probe as a hit or a miss. The response is otherwise discarded.
82
+
83
+ Captured headers are merged in minus hop-by-hop and length headers; the bearer token always comes from the auth store.
84
+
85
+ ## Guardrails
86
+
87
+ - Probes are skipped while a turn is running; the timer rearms on `agent_settled`.
88
+ - `maxIdle` cutoff stops probing during extended idle periods.
89
+ - `miss` / `errors` streaks and HTTP 401/403 pause probing until the next real turn recaptures credentials and resets state.
90
+ - Session probe spend is estimated from model pricing and pauses at the cap.
91
+ - Timers are `unref()`'d and never keep the process alive.
92
+ - All pauses are cleared automatically by the next real provider request.
93
+
94
+ ## Cost accounting
95
+
96
+ Kimi K3 list prices (USD per 1M tokens): input 3, output 15, cache read 0.3, cache write 0.
97
+
98
+ - A probe that hits the prefix cache costs approximately the cache-read price of the full context: ~$0.015 at 50k tokens, ~$0.03 at 100k, ~$0.06 at 200k.
99
+ - A probe that misses re-reads the prefix at full input price. Consecutive misses pause the loop (threshold: `miss`).
100
+ - The `saved` counter estimates `cacheReadTokens × (input − cacheRead) / 1M` — what an equivalent cold resume would have cost at full input price.
101
+
102
+ On a Kimi subscription, billing is quota-based and USD figures are indicative only.
103
+
104
+ ## Limitations
105
+
106
+ - The ~5 minute TTL and the pricing above are observed behavior, not an API contract. The `saved` estimate is informational, not a guaranteed saving.
107
+ - Only the `kimi-coding` provider (`kimi-openai-completions` API, with an `anthropic-messages` fallback) is supported. Other providers have different cache-key semantics and are out of scope.
108
+ - Captured payloads and headers live only in process memory; probes are sent only to `https://` endpoints.
109
+
110
+ ## Development
111
+
112
+ ```bash
113
+ npm install
114
+ npm run typecheck
115
+ npm test # 26 tests; stubbed fetch, temp $HOME, no network
116
+ ```
117
+
118
+ Tests run on Node's native TypeScript stripping (Node ≥ 22.18 / 24).
119
+
120
+ ## License
121
+
122
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,118 @@
1
+ # pi-kimi-keepalive
2
+
3
+ English | [中文文档](README.zh-CN.md)
4
+
5
+ Prompt-cache keepalive for [Kimi](https://www.kimi.com/) (`kimi-coding` provider) sessions in the [Pi coding agent](https://github.com/earendil-works/pi-coding-agent).
6
+
7
+ Kimi 的自动 prompt 缓存 TTL 实测约 5 分钟。TTL 过期后恢复会话时,整个上下文以全价 input($3 / 1M)重新计算。本扩展捕获最后一条真实 provider 请求,在会话空闲期间以固定间隔将其重放至同一端点,使缓存前缀在 TTL 内保持有效,后续请求按 cache-read 价格($0.3 / 1M)计费。
8
+
9
+ 重放请求直接发送到 provider 端点,不经过 Pi 会话管道:不产生合成消息、不产生模型回合、不改动对话历史,仅在界面上展示聚合统计。
10
+
11
+ ## 环境要求
12
+
13
+ - Node ≥ 20,pi ≥ 0.84(提供 `before_provider_headers` / `before_provider_request` 钩子)
14
+ - `kimi-coding` provider(建议 OAuth 订阅),`kimi-openai-completions` API
15
+
16
+ ## 安装
17
+
18
+ ```bash
19
+ git clone https://github.com/realoliversama/pi-kimi-keepalive
20
+ pi install /path/to/pi-kimi-keepalive
21
+ ```
22
+
23
+ 单次会话试运行(不安装):
24
+
25
+ ```bash
26
+ pi -e /path/to/pi-kimi-keepalive/src/index.ts
27
+ ```
28
+
29
+ npm 发布后可使用 `pi install npm:pi-kimi-keepalive`。
30
+
31
+ ## 初始化
32
+
33
+ 首次启动(`~/.pi/cache-keepalive/state.json` 不存在)且有交互 UI 时,扩展运行初始化向导配置四项护栏。提示符留空或按 Esc 保留默认值;之后可用 `/keepalive setup` 重新运行;headless 会话自动跳过并保留默认值。
34
+
35
+ | 步骤 | 设置项 | 命令 | 默认值 | 说明 |
36
+ | --- | --- | --- | --- | --- |
37
+ | 1 | Max idle cutoff | `maxidle` | `30m` | 空闲超过该时长后停止探测;`0` 表示不设限 |
38
+ | 2 | Miss pause threshold | `miss` | `2` | 连续 N 次探测未命中前缀缓存后暂停;命中会重置计数 |
39
+ | 3 | Error circuit breaker | `errors` | `3` | 连续 N 次探测失败(网络错误、HTTP 5xx)后暂停;HTTP 401/403 不受此值约束,直接暂停 |
40
+ | 4 | Session spend cap | `cap` | `$1.00` | 会话探测花费(估算 USD)上限;`0` 表示不设上限 |
41
+
42
+ 向导结束时询问是否立即启用 keepalive。探测在下一次真实请求(完成捕获)之后开始。全部配置持久化到 `~/.pi/cache-keepalive/state.json`。
43
+
44
+ ## 命令
45
+
46
+ ```
47
+ /keepalive 查看状态
48
+ /keepalive setup 重新运行初始化向导
49
+ /keepalive on|off 启用 / 停用(持久化)
50
+ /keepalive now 手动探测一次(绕过暂停)
51
+ /keepalive resume 清除 sticky 暂停
52
+ /keepalive interval=4m45s 探测间隔(≥ 30s;应 ≤ 5m 以保持缓存命中)
53
+ /keepalive maxidle=30m 空闲上限(0 = 不设限)
54
+ /keepalive miss=1 连续 N 次缓存 miss 后暂停
55
+ /keepalive errors=3 连续 N 次探测失败后熔断
56
+ /keepalive cap=1.0 会话探测花费上限(USD,0 = 无上限)
57
+ /keepalive token=512 判定 miss 的最小 prompt token 数
58
+ /keepalive maxoutput=16 探测 max_tokens
59
+ /keepalive reset 清零会话统计
60
+ ```
61
+
62
+ 统计(hits / misses / spend)为会话级;配置项持久化。`PI_KEEPALIVE_DEBUG=1` 输出调试日志到 stderr。
63
+
64
+ ## 工作机制
65
+
66
+ 1. **捕获**。`before_provider_request` 钩子快照每条真实 `kimi-coding` 请求的 payload(`structuredClone`)。只存内存,不落盘。
67
+ 2. **重放**。捕获的请求 POST 至 `{baseUrl}/chat/completions`(kimi-openai-completions 路由),仅修改终端参数:
68
+
69
+ | 修改 | 原因 |
70
+ | --- | --- |
71
+ | 移除 `stream` / `stream_options` | 非流式响应中 usage 可直接解析 |
72
+ | 移除 `thinking` / `store` | 与输出收紧参数兼容性未知;非前缀组成部分 |
73
+ | `max_completion_tokens: 16` | 限制探测输出成本 |
74
+ | HTTP 400 时重试 | 去掉 `prompt_cache_retention`(终端参数,非缓存键) |
75
+
76
+ `messages` / `tools` / `prompt_cache_key` / `prompt_cache_retention` 保持 byte-identical——这些是 provider 前缀缓存键的输入,重放因此命中既有缓存条目并重置 TTL,按 cache-read 计费。实测对 48k 上下文的探测实现 48,116/48,116 全量命中。
77
+ 3. **认证**。pi 在 `before_provider_headers` 钩子之后才注入 OAuth 凭据,捕获的 headers 通常不含认证;探测时从 pi 的凭据存储(`~/.pi/agent/auth.json` 的 `kimi-coding.access`)读取当前 token,与 pi 的自动刷新保持同步。
78
+ 4. **判定**。解析响应 usage(`prompt_tokens_details.cached_tokens`,回退 Anthropic 风格字段)将探测分类为 hit 或 miss;响应其余部分丢弃。
79
+
80
+ 捕获的业务 headers 原样合并(去除 hop-by-hop 与长度头)。
81
+
82
+ ## 护栏
83
+
84
+ - 回合运行中跳过探测;`agent_settled` 重新 arm 定时器。
85
+ - `maxIdle` 在长空闲期停止探测。
86
+ - `miss` / `errors` 连续次数达到阈值,或 HTTP 401/403,暂停探测;下一次真实请求重新捕获凭据并自动解除暂停。
87
+ - 会话探测花费按模型定价估算,达到上限即暂停。
88
+ - 定时器 `unref()`,不阻止进程退出。
89
+
90
+ ## 成本
91
+
92
+ Kimi K3 官方单价($/1M tokens):input 3,output 15,cache read 0.3,cache write 0。
93
+
94
+ - 命中的探测约等于整个上下文的 cache-read 费用:50k tokens ≈ $0.015,100k ≈ $0.03,200k ≈ $0.06。
95
+ - 未命中的探测按全价 input 重读同一前缀;连续 miss 达到阈值即暂停。
96
+ - `saved` 统计 = `cacheReadTokens × (input − cacheRead) / 1M`,即等效冷恢复在全价下的开销的估算。
97
+
98
+ Kimi 订阅按 quota 计费,USD 数值仅供参考。
99
+
100
+ ## 限制
101
+
102
+ - ~5 分钟 TTL 与上述定价为观测行为,非 API 契约;`saved` 仅为估算。
103
+ - 仅支持 `kimi-coding`(`kimi-openai-completions` API,含 `anthropic-messages` 回退);其他 provider 缓存键语义不同,不在范围内。
104
+ - 捕获内容仅存内存;探测仅发往 `https://` 端点。
105
+
106
+ ## 开发
107
+
108
+ ```bash
109
+ npm install
110
+ npm run typecheck
111
+ npm test # 26 项测试;stub fetch、临时 $HOME、无网络
112
+ ```
113
+
114
+ 测试依赖 Node 原生 TypeScript 剥离(Node ≥ 22.18 / 24)。
115
+
116
+ ## License
117
+
118
+ MIT,见 [LICENSE](LICENSE)。
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "pi-kimi-keepalive",
3
+ "version": "0.1.0",
4
+ "description": "Prompt-cache keepalive for Kimi (kimi-coding) sessions in the Pi coding agent. Replays the last real provider request while idle so the automatic prefix cache never expires.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "realOliverSama <159797106+realOliverSama@users.noreply.github.com>",
8
+ "homepage": "https://github.com/realoliversama/pi-kimi-keepalive#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/realoliversama/pi-kimi-keepalive.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/realoliversama/pi-kimi-keepalive/issues"
15
+ },
16
+ "keywords": [
17
+ "pi-package",
18
+ "pi-extension",
19
+ "kimi",
20
+ "moonshot",
21
+ "prompt-cache",
22
+ "cache",
23
+ "keepalive"
24
+ ],
25
+ "pi": {
26
+ "extensions": ["./src/index.ts"]
27
+ },
28
+ "files": [
29
+ "src",
30
+ "README.md",
31
+ "README.zh-CN.md",
32
+ "LICENSE"
33
+ ],
34
+ "scripts": {
35
+ "test": "node --test test/keepalive.test.ts",
36
+ "typecheck": "tsc --noEmit"
37
+ },
38
+ "engines": {
39
+ "node": ">=20"
40
+ },
41
+ "devDependencies": {
42
+ "@earendil-works/pi-coding-agent": "0.84.4",
43
+ "@types/node": "^24",
44
+ "typescript": "^5"
45
+ }
46
+ }