pi-spark 0.22.2 → 0.23.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 +534 -0
- package/README.md +7 -1
- package/assets/screenshot-peak-off-peak-pricing.png +0 -0
- package/package.json +7 -6
- package/src/components/inline-text.ts +2 -2
- package/src/config/index.ts +1 -1
- package/src/features/credits/index.ts +19 -12
- package/src/features/credits/manager.ts +5 -5
- package/src/features/credits/pricing.ts +57 -0
- package/src/features/credits/providers/deepseek.ts +41 -2
- package/src/features/credits/providers/fireworks.ts +1 -0
- package/src/features/credits/providers/index.ts +1 -1
- package/src/features/credits/providers/kimi-code.ts +17 -12
- package/src/features/credits/providers/openai-codex.ts +3 -0
- package/src/features/credits/providers/openrouter.ts +1 -0
- package/src/features/credits/status.ts +10 -6
- package/src/features/credits/types.ts +6 -1
- package/src/features/editor/index.ts +2 -2
- package/src/features/footer/index.ts +1 -1
- package/src/features/recap/idle.ts +2 -2
- package/src/features/title/manager.ts +1 -1
- package/src/utils/format.ts +4 -4
- package/src/utils/model.ts +2 -2
- package/src/utils/usage.ts +2 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.23.0](https://github.com/zlliang/pi-spark/compare/v0.22.3...v0.23.0) (2026-09-11)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **credits:** support provider peak/off-peak pricing ([f0dbf19](https://github.com/zlliang/pi-spark/commit/f0dbf19dca6f7a3eb290490052489eb0649680d1))
|
|
9
|
+
|
|
10
|
+
## [0.22.3](https://github.com/zlliang/pi-spark/compare/v0.22.2...v0.22.3) (2026-09-10)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **credits:** add provider dashboard hyperlinks ([f7a7ec2](https://github.com/zlliang/pi-spark/commit/f7a7ec2b2394dd65e5c6ce20bc4b3d8fb5cc600e))
|
|
16
|
+
* **credits:** show reset times and convert Kimi balances to USD ([7c4a478](https://github.com/zlliang/pi-spark/commit/7c4a4782cecba8ecc557bfcf496b898347b5e2b5))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* correct cache probe config typing ([1695212](https://github.com/zlliang/pi-spark/commit/1695212c607163ebc2fbb721d6088377f36b9887))
|
|
22
|
+
* **credits:** update Codex usage settings link ([4f8d72c](https://github.com/zlliang/pi-spark/commit/4f8d72c6acbd519ab7850b8a086a09de5d83ca98))
|
|
23
|
+
|
|
24
|
+
## [0.22.2](https://github.com/zlliang/pi-spark/compare/v0.22.1...v0.22.2) (2026-09-04)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Miscellaneous Chores
|
|
28
|
+
|
|
29
|
+
* trigger release 0.22.2 ([b8463a8](https://github.com/zlliang/pi-spark/commit/b8463a887b9be6c81d8d55df7b6a31df6acdb21f))
|
|
30
|
+
|
|
31
|
+
## [0.22.1](https://github.com/zlliang/pi-spark/compare/v0.22.0...v0.22.1) (2026-09-04)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Bug Fixes
|
|
35
|
+
|
|
36
|
+
* **auth:** pass environment to background requests ([a89c943](https://github.com/zlliang/pi-spark/commit/a89c943c4190b659ac4cf3ffec49eff315008f94))
|
|
37
|
+
* **credits:** enable credits status in RPC mode ([#72](https://github.com/zlliang/pi-spark/issues/72)) ([58e9cc1](https://github.com/zlliang/pi-spark/commit/58e9cc16bbae4a06cc9ea89e694f7723f6abcd7d))
|
|
38
|
+
* **credits:** simplify mode check ([a58acd1](https://github.com/zlliang/pi-spark/commit/a58acd1ca7830f4a03941ad90778a1b4f2ae715c))
|
|
39
|
+
* **model:** avoid authenticating fallback models ([3a6f6a1](https://github.com/zlliang/pi-spark/commit/3a6f6a12bbf180079cf09333842cf4159583cf7e))
|
|
40
|
+
|
|
41
|
+
## [0.22.0](https://github.com/zlliang/pi-spark/compare/v0.21.1...v0.22.0) (2026-08-07)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
### ⚠ BREAKING CHANGES
|
|
45
|
+
|
|
46
|
+
* **fullscreen:** use Pi's native fullscreen mode
|
|
47
|
+
|
|
48
|
+
### Code Refactoring
|
|
49
|
+
|
|
50
|
+
* **fullscreen:** use Pi's native fullscreen mode ([3fbc092](https://github.com/zlliang/pi-spark/commit/3fbc09234f4baff627624b99714fd8057e179e80))
|
|
51
|
+
|
|
52
|
+
## [0.21.1](https://github.com/zlliang/pi-spark/compare/v0.21.0...v0.21.1) (2026-07-24)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
### Bug Fixes
|
|
56
|
+
|
|
57
|
+
* **credits:** extract token from Authorization header for OAuth providers ([06f7309](https://github.com/zlliang/pi-spark/commit/06f7309bd520e76947dc7a9523b6fc4e2855a779))
|
|
58
|
+
* **credits:** resolve OAuth tokens consistently ([e7fd9d8](https://github.com/zlliang/pi-spark/commit/e7fd9d8098cfaf96c79a7122b95d388016b6cefa))
|
|
59
|
+
* **presets:** skip unavailable presets when cycling ([72348da](https://github.com/zlliang/pi-spark/commit/72348da41a6037165a1d89381a98fa54e8209993))
|
|
60
|
+
|
|
61
|
+
## [0.21.0](https://github.com/zlliang/pi-spark/compare/v0.20.0...v0.21.0) (2026-07-23)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
### Features
|
|
65
|
+
|
|
66
|
+
* **credits:** retry HTTP requests with Ky ([ee0a304](https://github.com/zlliang/pi-spark/commit/ee0a304de314a7950e9289ea2cb1618caf4d67bb))
|
|
67
|
+
* **editor:** add model thinking level indicator ([d27059d](https://github.com/zlliang/pi-spark/commit/d27059df79f77d4087fe5c20218e3f76385781ae))
|
|
68
|
+
* **themes:** add GitHub default themes ([40838ed](https://github.com/zlliang/pi-spark/commit/40838ed1b00be8b66da335eb8d29fa059b0bf51b))
|
|
69
|
+
* **write:** add scrolling tail previews ([7542dde](https://github.com/zlliang/pi-spark/commit/7542dde92ac0c3973f9b284001d530b4115ff21c))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
### Bug Fixes
|
|
73
|
+
|
|
74
|
+
* **footer:** include all session usage costs ([ed69b31](https://github.com/zlliang/pi-spark/commit/ed69b314bd1d56072b55804f64b7ed0f738675d8))
|
|
75
|
+
* **modes:** handle non-TUI sessions correctly ([58e0246](https://github.com/zlliang/pi-spark/commit/58e02460109ca2cb494bc870ef1b8be0d2140e4f))
|
|
76
|
+
* **themes:** improve muted colors and code fence contrast ([b8a85d9](https://github.com/zlliang/pi-spark/commit/b8a85d96cec879c59cfc2d3eb9aeee0f6b4efb72))
|
|
77
|
+
* **title:** generate after first turn ([5fbf21e](https://github.com/zlliang/pi-spark/commit/5fbf21e0c2a2489d0582171a3b763dba045f1189))
|
|
78
|
+
* **write:** clarify omitted line summary ([702af59](https://github.com/zlliang/pi-spark/commit/702af59c5877147a05c4279b80e7258807a837c6))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
### Performance Improvements
|
|
82
|
+
|
|
83
|
+
* **fullscreen:** render transcript only once ([7eb60b9](https://github.com/zlliang/pi-spark/commit/7eb60b960d7acc544a8f5aa18b935f12c4bd9457))
|
|
84
|
+
|
|
85
|
+
## [0.20.0](https://github.com/zlliang/pi-spark/compare/v0.19.3...v0.20.0) (2026-07-19)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
### Features
|
|
89
|
+
|
|
90
|
+
* **credits:** add per-provider enable/disable config ([ec800ef](https://github.com/zlliang/pi-spark/commit/ec800ef0cc44abcfebb0f54577c648c2ab8f3a86))
|
|
91
|
+
* **credits:** improve Kimi usage reporting ([675d5f0](https://github.com/zlliang/pi-spark/commit/675d5f077e9cb16e491d25e243f5641fc11861b1))
|
|
92
|
+
* **footer:** add statusPosition config for inline/below extension statuses ([4bfec49](https://github.com/zlliang/pi-spark/commit/4bfec49c7bbe656404dd75f2e335939f3ec88d8c))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
### Bug Fixes
|
|
96
|
+
|
|
97
|
+
* **credits:** clarify Kimi extra usage balance ([685052c](https://github.com/zlliang/pi-spark/commit/685052c1e04e1ffbfdecd876305eb00f08dacb97))
|
|
98
|
+
* **fullscreen:** suppress idle status gap ([02cc41d](https://github.com/zlliang/pi-spark/commit/02cc41d522512b3c5fdf2111032f1d1d8b07ce56))
|
|
99
|
+
|
|
100
|
+
## [0.19.3](https://github.com/zlliang/pi-spark/compare/v0.19.2...v0.19.3) (2026-07-17)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
### Bug Fixes
|
|
104
|
+
|
|
105
|
+
* **credits:** handle missing used value in Kimi Code lane ([5eb86f8](https://github.com/zlliang/pi-spark/commit/5eb86f8f0606ef88e0adda73b65e2ec29b9ae57d))
|
|
106
|
+
* **footer:** hide cost below half a cent ([04c0f17](https://github.com/zlliang/pi-spark/commit/04c0f17f07ba6400b7a37d797b1d02dd7faf7c06))
|
|
107
|
+
|
|
108
|
+
## [0.19.2](https://github.com/zlliang/pi-spark/compare/v0.19.1...v0.19.2) (2026-07-17)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
### Bug Fixes
|
|
112
|
+
|
|
113
|
+
* support Pi 0.80.8 ([3f786a1](https://github.com/zlliang/pi-spark/commit/3f786a1db1c60ceb9a4563cab34e35ca55dba232))
|
|
114
|
+
|
|
115
|
+
## [0.19.1](https://github.com/zlliang/pi-spark/compare/v0.19.0...v0.19.1) (2026-07-13)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
### Bug Fixes
|
|
119
|
+
|
|
120
|
+
* **credits:** derive Codex window labels from limits ([65e7900](https://github.com/zlliang/pi-spark/commit/65e7900a22803c9e0db18837100bb11c6557a617))
|
|
121
|
+
|
|
122
|
+
## [0.19.0](https://github.com/zlliang/pi-spark/compare/v0.18.1...v0.19.0) (2026-07-11)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
### Features
|
|
126
|
+
|
|
127
|
+
* **credits:** add Codex reset redemption ([1c56eea](https://github.com/zlliang/pi-spark/commit/1c56eeae9fb3ed46f859fbe8c88e8ce78a388950))
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
### Bug Fixes
|
|
131
|
+
|
|
132
|
+
* **lifecycle:** wait for agent settlement ([7653b51](https://github.com/zlliang/pi-spark/commit/7653b513f8b6a91354fa1ba70f688639b142a206))
|
|
133
|
+
* **package:** include docs in published files ([14d96f9](https://github.com/zlliang/pi-spark/commit/14d96f9b5cb1aefda7d5d06ee9fb0cd3ccdf8d62))
|
|
134
|
+
* **title:** use sentence case for English titles ([747e41d](https://github.com/zlliang/pi-spark/commit/747e41db088ba80d617363e8f237f2ad505292a5))
|
|
135
|
+
|
|
136
|
+
## [0.18.1](https://github.com/zlliang/pi-spark/compare/v0.18.0...v0.18.1) (2026-07-10)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
### Bug Fixes
|
|
140
|
+
|
|
141
|
+
* **lockfile:** normalize pi-ai binary path ([9d9a641](https://github.com/zlliang/pi-spark/commit/9d9a64155105b114f1d6f3576622144ab18505fc))
|
|
142
|
+
* **model:** isolate background Codex sessions ([b240e53](https://github.com/zlliang/pi-spark/commit/b240e53672ef452ea17b962fb683a552c5ca3904))
|
|
143
|
+
|
|
144
|
+
## [0.18.0](https://github.com/zlliang/pi-spark/compare/v0.17.1...v0.18.0) (2026-07-10)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
### Features
|
|
148
|
+
|
|
149
|
+
* **config:** support max thinking level ([3d58e28](https://github.com/zlliang/pi-spark/commit/3d58e28a5da00c95a33bc5735b8f8e0bb1b66fad))
|
|
150
|
+
|
|
151
|
+
## [0.17.1](https://github.com/zlliang/pi-spark/compare/v0.17.0...v0.17.1) (2026-07-10)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
### Bug Fixes
|
|
155
|
+
|
|
156
|
+
* preserve routing for background model calls ([27b2c12](https://github.com/zlliang/pi-spark/commit/27b2c12f3d3c179dca4ce44978a34725d6f4db60))
|
|
157
|
+
|
|
158
|
+
## [0.17.0](https://github.com/zlliang/pi-spark/compare/v0.16.0...v0.17.0) (2026-07-09)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
### Features
|
|
162
|
+
|
|
163
|
+
* add kimi code provider ([#56](https://github.com/zlliang/pi-spark/issues/56)) ([8950df3](https://github.com/zlliang/pi-spark/commit/8950df3c8ac58158a817d7e4cc8a26d315c5a534))
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
### Bug Fixes
|
|
167
|
+
|
|
168
|
+
* **credits:** always show Kimi Code weekly usage lane ([985ba89](https://github.com/zlliang/pi-spark/commit/985ba8974fa07490941c7eb77a076ace20566d1f)), closes [#56](https://github.com/zlliang/pi-spark/issues/56)
|
|
169
|
+
* **credits:** fix Kimi Code weekly lane always rendering ([76e9227](https://github.com/zlliang/pi-spark/commit/76e922751d09e81f3619f4493c895306c255c34d)), closes [#56](https://github.com/zlliang/pi-spark/issues/56)
|
|
170
|
+
|
|
171
|
+
## [0.16.0](https://github.com/zlliang/pi-spark/compare/v0.15.0...v0.16.0) (2026-07-09)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
### Features
|
|
175
|
+
|
|
176
|
+
* **title:** name sessions automatically after the first exchange ([dcef9f5](https://github.com/zlliang/pi-spark/commit/dcef9f5a9d71d6f793383526dfb3d924d0937d93))
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
### Bug Fixes
|
|
180
|
+
|
|
181
|
+
* **presets:** use border color for preset selector border ([500a868](https://github.com/zlliang/pi-spark/commit/500a8681758533433e8d6da6d0eafa5c9774d141))
|
|
182
|
+
* update pi-ai provider imports ([ccf7f6d](https://github.com/zlliang/pi-spark/commit/ccf7f6dc292c0c11f114f19a48f3b72f58016345))
|
|
183
|
+
|
|
184
|
+
## [0.15.0](https://github.com/zlliang/pi-spark/compare/v0.14.7...v0.15.0) (2026-06-20)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
### Features
|
|
188
|
+
|
|
189
|
+
* **footer:** hide cost display for free models when cost is zero ([78e0a23](https://github.com/zlliang/pi-spark/commit/78e0a230d6a148b927d5cd598c0edee505c260c2))
|
|
190
|
+
* remove pi and web agent tools ([1acb837](https://github.com/zlliang/pi-spark/commit/1acb8377bbad437a31d8feb91112529f332964ab))
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
### Bug Fixes
|
|
194
|
+
|
|
195
|
+
* **config:** use shared config directory name ([bc5dae0](https://github.com/zlliang/pi-spark/commit/bc5dae08d8528a41e379c041cf0eaba2b176ae68))
|
|
196
|
+
* **tool:** handle missing composed tool action ([e93bf39](https://github.com/zlliang/pi-spark/commit/e93bf391c26afad3e8bf84123d26f1f2a9626a60))
|
|
197
|
+
|
|
198
|
+
## [0.14.7](https://github.com/zlliang/pi-spark/compare/v0.14.6...v0.14.7) (2026-06-17)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
### Bug Fixes
|
|
202
|
+
|
|
203
|
+
* **config:** isolate invalid feature config instead of disabling all ([c57addb](https://github.com/zlliang/pi-spark/commit/c57addb510ce88031c7def2ddde97a594ac1f39f))
|
|
204
|
+
* **credits:** cancel inflight request on session shutdown ([2695273](https://github.com/zlliang/pi-spark/commit/2695273e155da4aa1edd87098c3574363833aad0))
|
|
205
|
+
|
|
206
|
+
## [0.14.6](https://github.com/zlliang/pi-spark/compare/v0.14.5...v0.14.6) (2026-06-15)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
### Bug Fixes
|
|
210
|
+
|
|
211
|
+
* **pi:** trim name parameters and align state output ([2dd5efc](https://github.com/zlliang/pi-spark/commit/2dd5efc9888763399a8af4e6d96d5d7744959daa))
|
|
212
|
+
* **presets:** apply preset flag only on startup ([b0a2585](https://github.com/zlliang/pi-spark/commit/b0a2585a6fa63b3a72b14daf2802ad90eace1a3c))
|
|
213
|
+
* **presets:** improve selector layout and key hints ([9bf0c9f](https://github.com/zlliang/pi-spark/commit/9bf0c9f7b5f00dda84464fe8327054af00a9d26b))
|
|
214
|
+
* **utils:** prevent duplicate elapsed timing line in composed tools ([0a00fd5](https://github.com/zlliang/pi-spark/commit/0a00fd554c7eeadeb099f185507e63a850dd89c6))
|
|
215
|
+
|
|
216
|
+
## [0.14.5](https://github.com/zlliang/pi-spark/compare/v0.14.4...v0.14.5) (2026-06-14)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
### Bug Fixes
|
|
220
|
+
|
|
221
|
+
* **startup:** defer heavy gRPC and MCP imports to first use ([2080906](https://github.com/zlliang/pi-spark/commit/2080906515452d3ad4464b6cf2892dc765025458))
|
|
222
|
+
|
|
223
|
+
## [0.14.4](https://github.com/zlliang/pi-spark/compare/v0.14.3...v0.14.4) (2026-06-14)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
### Features
|
|
227
|
+
|
|
228
|
+
* **tools:** add per-action execution timing to composed tools ([b857f1d](https://github.com/zlliang/pi-spark/commit/b857f1d8ccd1b5b5713c3486291889b7d037e298))
|
|
229
|
+
|
|
230
|
+
## [0.14.3](https://github.com/zlliang/pi-spark/compare/v0.14.2...v0.14.3) (2026-06-14)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
### Code Refactoring
|
|
234
|
+
|
|
235
|
+
* **tools:** share composed-tool architecture across pi and web ([12e563b](https://github.com/zlliang/pi-spark/commit/12e563b357f9e5cf83fef04943c66feffd16c26f))
|
|
236
|
+
|
|
237
|
+
## [0.14.2](https://github.com/zlliang/pi-spark/compare/v0.14.1...v0.14.2) (2026-06-14)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
### Bug Fixes
|
|
241
|
+
|
|
242
|
+
* **pi:** simplify missing model display ([ab6389e](https://github.com/zlliang/pi-spark/commit/ab6389ea0e8e13360c221d6b9fc450e80514416d))
|
|
243
|
+
|
|
244
|
+
## [0.14.1](https://github.com/zlliang/pi-spark/compare/v0.14.0...v0.14.1) (2026-06-14)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
### Miscellaneous Chores
|
|
248
|
+
|
|
249
|
+
* trigger release 0.14.1 ([b966f0e](https://github.com/zlliang/pi-spark/commit/b966f0ecb4739cbf250051de652fc0f51fa9d5f9))
|
|
250
|
+
|
|
251
|
+
## [0.14.0](https://github.com/zlliang/pi-spark/compare/v0.13.1...v0.14.0) (2026-06-14)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
### Features
|
|
255
|
+
|
|
256
|
+
* add unified pi and web agent tools ([1229d93](https://github.com/zlliang/pi-spark/commit/1229d93e1beffd444767781c3c1332e26706dc8a))
|
|
257
|
+
|
|
258
|
+
## [0.13.1](https://github.com/zlliang/pi-spark/compare/v0.13.0...v0.13.1) (2026-06-14)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
### Documentation
|
|
262
|
+
|
|
263
|
+
* refresh README screenshots ([85948f7](https://github.com/zlliang/pi-spark/commit/85948f763b7fdcdf6020f17ebc425398a3133bc9))
|
|
264
|
+
|
|
265
|
+
## [0.13.0](https://github.com/zlliang/pi-spark/compare/v0.12.0...v0.13.0) (2026-06-14)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
### Code Refactoring
|
|
269
|
+
|
|
270
|
+
* consolidate features into a single extension ([5fd0f5c](https://github.com/zlliang/pi-spark/commit/5fd0f5c5a577fe0808a91b4d313a1cd569e9288f))
|
|
271
|
+
|
|
272
|
+
## [0.12.0](https://github.com/zlliang/pi-spark/compare/v0.11.2...v0.12.0) (2026-06-13)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
### ⚠ BREAKING CHANGES
|
|
276
|
+
|
|
277
|
+
* fold pi-credits into pi-spark and revert to single package
|
|
278
|
+
|
|
279
|
+
### Miscellaneous Chores
|
|
280
|
+
|
|
281
|
+
* release 0.12.0 ([e80081a](https://github.com/zlliang/pi-spark/commit/e80081a2d352da108feea446d44fb63f9ecc68ff))
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
### Code Refactoring
|
|
285
|
+
|
|
286
|
+
* fold pi-credits into pi-spark and revert to single package ([3a1cbe3](https://github.com/zlliang/pi-spark/commit/3a1cbe360feeab02a1d8b8313eaf6b06b4f8d400))
|
|
287
|
+
|
|
288
|
+
## [0.11.2](https://github.com/zlliang/pi-spark/compare/v0.11.1...v0.11.2) (2026-06-13)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
### Documentation
|
|
292
|
+
|
|
293
|
+
* **pi-spark:** simplify description ([ede2299](https://github.com/zlliang/pi-spark/commit/ede2299cee377401c066bd76d8e795c9f96d4f1d))
|
|
294
|
+
|
|
295
|
+
## [0.11.1](https://github.com/zlliang/pi-spark/compare/v0.11.0...v0.11.1) (2026-06-13)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
### Documentation
|
|
299
|
+
|
|
300
|
+
* **pi-spark:** polish README and metadata ([382de1a](https://github.com/zlliang/pi-spark/commit/382de1aeaa48de7d874d088cba60b6ff1e60d42d))
|
|
301
|
+
|
|
302
|
+
## [0.11.0](https://github.com/zlliang/pi-spark/compare/v0.10.5...v0.11.0) (2026-06-13)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
### Miscellaneous Chores
|
|
306
|
+
|
|
307
|
+
* **pi-spark:** release 0.11.0 ([c5124d4](https://github.com/zlliang/pi-spark/commit/c5124d4070c7caa96e0e1fc37e0a23b45232e38c))
|
|
308
|
+
|
|
309
|
+
## [0.10.5](https://github.com/zlliang/pi-spark/compare/v0.10.4...v0.10.5) (2026-06-13)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
### Bug Fixes
|
|
313
|
+
|
|
314
|
+
* **pi-spark:** reassert clearOnShrink each render in fullscreen ([55604b5](https://github.com/zlliang/pi-spark/commit/55604b59742b5300ef80102f9fbc3fc1c7a798cd))
|
|
315
|
+
|
|
316
|
+
## [0.10.4](https://github.com/zlliang/pi-spark/compare/v0.10.3...v0.10.4) (2026-06-13)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
### Bug Fixes
|
|
320
|
+
|
|
321
|
+
* add npm package homepage metadata ([9ff2e39](https://github.com/zlliang/pi-spark/commit/9ff2e39d609c3c0a39c45a6c6b456bde964fbaa8))
|
|
322
|
+
|
|
323
|
+
## [0.10.3](https://github.com/zlliang/pi-spark/compare/v0.10.2...v0.10.3) (2026-06-13)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
### Miscellaneous Chores
|
|
327
|
+
|
|
328
|
+
* **pi-spark:** release 0.10.3 ([c253364](https://github.com/zlliang/pi-spark/commit/c2533641a4c9fe3b91ad6d0a52077d65036a95f0))
|
|
329
|
+
|
|
330
|
+
## [0.10.2](https://github.com/zlliang/pi-spark/compare/v0.10.1...v0.10.2) (2026-06-13)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
### Bug Fixes
|
|
334
|
+
|
|
335
|
+
* simplify cost display ([4990a57](https://github.com/zlliang/pi-spark/commit/4990a57fd9688929e115d64121a009630988d93e))
|
|
336
|
+
|
|
337
|
+
## [0.10.1](https://github.com/zlliang/pi-spark/compare/v0.10.0...v0.10.1) (2026-06-13)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
### Miscellaneous Chores
|
|
341
|
+
|
|
342
|
+
* bump to 0.10.1 ([14a7b3d](https://github.com/zlliang/pi-spark/commit/14a7b3dc56d329095e478d0e0e1979a177bb1cfe))
|
|
343
|
+
|
|
344
|
+
## [0.10.0](https://github.com/zlliang/pi-spark/compare/v0.9.5...v0.10.0) (2026-06-13)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
### ⚠ BREAKING CHANGES
|
|
348
|
+
|
|
349
|
+
* **models:** the model tool's "current" action is renamed to "active", and the scope/provider/model params are replaced by "query".
|
|
350
|
+
|
|
351
|
+
### Features
|
|
352
|
+
|
|
353
|
+
* **models:** filter model lists with Liqe queries ([877479b](https://github.com/zlliang/pi-spark/commit/877479b51a715b7a5a05381b207ecd03837e433d))
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
### Styles
|
|
357
|
+
|
|
358
|
+
* **name:** use complete sentences in tool prose ([3430899](https://github.com/zlliang/pi-spark/commit/343089946f685b70c1bcda0291cf511f536a0711))
|
|
359
|
+
|
|
360
|
+
## [0.9.5](https://github.com/zlliang/pi-spark/compare/v0.9.4...v0.9.5) (2026-06-12)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
### Bug Fixes
|
|
364
|
+
|
|
365
|
+
* **models:** keep unavailable model labels visible ([feab815](https://github.com/zlliang/pi-spark/commit/feab8159210033ad362a4951e835247e28325063))
|
|
366
|
+
|
|
367
|
+
## [0.9.4](https://github.com/zlliang/pi-spark/compare/v0.9.3...v0.9.4) (2026-06-12)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
### Bug Fixes
|
|
371
|
+
|
|
372
|
+
* **models:** expose unavailable models by scope ([8df5053](https://github.com/zlliang/pi-spark/commit/8df5053b1b92aaa78a366088b9414e7f197c4072))
|
|
373
|
+
|
|
374
|
+
## [0.9.3](https://github.com/zlliang/pi-spark/compare/v0.9.2...v0.9.3) (2026-06-12)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
### Bug Fixes
|
|
378
|
+
|
|
379
|
+
* **models:** align action description order ([3de17c1](https://github.com/zlliang/pi-spark/commit/3de17c1d5f341eeb8673c70060a8dcb8febe8f5c))
|
|
380
|
+
* update model and name extension exports ([68b5191](https://github.com/zlliang/pi-spark/commit/68b51916c89717ac26ccafb863d420d16dd0423c))
|
|
381
|
+
|
|
382
|
+
## [0.9.2](https://github.com/zlliang/pi-spark/compare/v0.9.1...v0.9.2) (2026-06-12)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
### Bug Fixes
|
|
386
|
+
|
|
387
|
+
* **models:** show expand hint in model tool call ([a7ac029](https://github.com/zlliang/pi-spark/commit/a7ac02943e0621e04093c5cc0a81a192fe440801))
|
|
388
|
+
|
|
389
|
+
## [0.9.1](https://github.com/zlliang/pi-spark/compare/v0.9.0...v0.9.1) (2026-06-12)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
### Bug Fixes
|
|
393
|
+
|
|
394
|
+
* **models:** polish model tool prompt guideline ([eb01e6b](https://github.com/zlliang/pi-spark/commit/eb01e6b5a3b5dd180d32b9a9c8e06e4b90f42036))
|
|
395
|
+
|
|
396
|
+
## [0.9.0](https://github.com/zlliang/pi-spark/compare/v0.8.0...v0.9.0) (2026-06-12)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
### Features
|
|
400
|
+
|
|
401
|
+
* **models:** add model inspection tool ([4d2625e](https://github.com/zlliang/pi-spark/commit/4d2625e2e2bd2e5637c50e563efff7b271a5a21d))
|
|
402
|
+
|
|
403
|
+
## [0.8.0](https://github.com/zlliang/pi-spark/compare/v0.7.0...v0.8.0) (2026-06-10)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
### Features
|
|
407
|
+
|
|
408
|
+
* **credits:** generalize codex-usage into multi-provider credits ([84ffc8e](https://github.com/zlliang/pi-spark/commit/84ffc8e25256619a6b82759095be554433de15d0))
|
|
409
|
+
* move credits extension to the dedicated pi-credits package ([2d1e868](https://github.com/zlliang/pi-spark/commit/2d1e868f2c541c3707c9564ccdedaf80f40a7be5))
|
|
410
|
+
|
|
411
|
+
## [0.7.0](https://github.com/zlliang/pi-spark/compare/v0.6.3...v0.7.0) (2026-06-10)
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
### Code Refactoring
|
|
415
|
+
|
|
416
|
+
* remove trust-all extension ([2a783f9](https://github.com/zlliang/pi-spark/commit/2a783f9d3aa3031f0c204b047546c8f5d1aa6728))
|
|
417
|
+
|
|
418
|
+
## [0.6.3](https://github.com/zlliang/pi-spark/compare/v0.6.2...v0.6.3) (2026-06-09)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
### Code Refactoring
|
|
422
|
+
|
|
423
|
+
* rename session naming tool ([49f8502](https://github.com/zlliang/pi-spark/commit/49f850226a175e280b0337f96fbaa649276d1153))
|
|
424
|
+
|
|
425
|
+
## [0.6.2](https://github.com/zlliang/pi-spark/compare/v0.6.1...v0.6.2) (2026-06-09)
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
### Code Refactoring
|
|
429
|
+
|
|
430
|
+
* adjust set-session-name reason handling ([6d1b0b8](https://github.com/zlliang/pi-spark/commit/6d1b0b8ba3859b0a8b991758f01582caf65d6c8c))
|
|
431
|
+
|
|
432
|
+
## [0.6.1](https://github.com/zlliang/pi-spark/compare/v0.6.0...v0.6.1) (2026-06-09)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
### Code Refactoring
|
|
436
|
+
|
|
437
|
+
* **editor:** show preset name in bold without prefix ([79a5fe2](https://github.com/zlliang/pi-spark/commit/79a5fe2910647094503bca9ccbc69326c6ade12a))
|
|
438
|
+
|
|
439
|
+
## [0.6.0](https://github.com/zlliang/pi-spark/compare/v0.5.2...v0.6.0) (2026-06-09)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
### Features
|
|
443
|
+
|
|
444
|
+
* add trust-all extension ([2784e47](https://github.com/zlliang/pi-spark/commit/2784e4719689280029864c45455bdac1d515669a))
|
|
445
|
+
* **codex-usage:** refresh after usage events ([bc53364](https://github.com/zlliang/pi-spark/commit/bc533646d085966376278ec6672b7d51d11c3e2d))
|
|
446
|
+
* **editor:** add pulse spinner preset ([9033de9](https://github.com/zlliang/pi-spark/commit/9033de9552630b16481dcb37aa9556bd37ab1bc7))
|
|
447
|
+
|
|
448
|
+
## [0.5.2](https://github.com/zlliang/pi-spark/compare/v0.5.1...v0.5.2) (2026-06-08)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
### Bug Fixes
|
|
452
|
+
|
|
453
|
+
* **codex-usage:** round status percentages ([a641329](https://github.com/zlliang/pi-spark/commit/a64132919bd99d67d2dd039206ab80a41cfab720))
|
|
454
|
+
|
|
455
|
+
## [0.5.1](https://github.com/zlliang/pi-spark/compare/v0.5.0...v0.5.1) (2026-06-08)
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
### Bug Fixes
|
|
459
|
+
|
|
460
|
+
* **codex-usage:** polish usage error message ([99edccf](https://github.com/zlliang/pi-spark/commit/99edccf6d19b3fdb334414b46f59a25d542bc03d))
|
|
461
|
+
|
|
462
|
+
## [0.5.0](https://github.com/zlliang/pi-spark/compare/v0.4.0...v0.5.0) (2026-06-08)
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
### Features
|
|
466
|
+
|
|
467
|
+
* **codex-usage:** add Codex rate-limit usage status ([6778581](https://github.com/zlliang/pi-spark/commit/67785818e3cb2304b16b1feaf1dfb58520cb1542))
|
|
468
|
+
|
|
469
|
+
## [0.4.0](https://github.com/zlliang/pi-spark/compare/v0.3.1...v0.4.0) (2026-06-08)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
### Features
|
|
473
|
+
|
|
474
|
+
* **set-session-name:** add session naming tool ([bc60e90](https://github.com/zlliang/pi-spark/commit/bc60e9049e17e1b82fc0d936731235f2bd4e94dd))
|
|
475
|
+
|
|
476
|
+
## [0.3.1](https://github.com/zlliang/pi-spark/compare/v0.3.0...v0.3.1) (2026-06-08)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
### Bug Fixes
|
|
480
|
+
|
|
481
|
+
* **fullscreen:** keep clearOnShrink enabled across reload ([e1adde0](https://github.com/zlliang/pi-spark/commit/e1adde0eca052319dd9231f6e22456ae33109289))
|
|
482
|
+
|
|
483
|
+
## [0.3.0](https://github.com/zlliang/pi-spark/compare/v0.2.1...v0.3.0) (2026-06-06)
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
### Features
|
|
487
|
+
|
|
488
|
+
* **footer:** shorten cwd display ([8ae3525](https://github.com/zlliang/pi-spark/commit/8ae3525af6311de94c4b85f07e3f9c6df5369011))
|
|
489
|
+
* **fullscreen:** clear session on exit ([b48b997](https://github.com/zlliang/pi-spark/commit/b48b9972365da5c24d2f90f36d1ba0e945d87e94))
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
### Bug Fixes
|
|
493
|
+
|
|
494
|
+
* **fullscreen:** style exit session label ([b7fcd80](https://github.com/zlliang/pi-spark/commit/b7fcd80113e52efd6c46bcd617c3c0520fcb2fb0))
|
|
495
|
+
|
|
496
|
+
## [0.2.1](https://github.com/zlliang/pi-spark/compare/v0.2.0...v0.2.1) (2026-06-04)
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
### Miscellaneous Chores
|
|
500
|
+
|
|
501
|
+
* release 0.2.1 ([d296d5d](https://github.com/zlliang/pi-spark/commit/d296d5de0eb890b1f9c10dd5edd7343224a58819))
|
|
502
|
+
|
|
503
|
+
## [0.2.0](https://github.com/zlliang/pi-spark/compare/v0.1.2...v0.2.0) (2026-06-04)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
### Features
|
|
507
|
+
|
|
508
|
+
* **editor:** show working status in border ([b72b127](https://github.com/zlliang/pi-spark/commit/b72b12738b209bb3016009c88e2efd1544d770f6))
|
|
509
|
+
* **footer:** include statuses in main line ([93a18de](https://github.com/zlliang/pi-spark/commit/93a18de6fbd53e7751dbecb01f9efa7b954d8bdf))
|
|
510
|
+
* **presets:** support preset CLI flag ([435c164](https://github.com/zlliang/pi-spark/commit/435c164b61ce231074fdbdee636d45af1845bebf))
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
### Bug Fixes
|
|
514
|
+
|
|
515
|
+
* **editor:** clear tool status after executions ([780395f](https://github.com/zlliang/pi-spark/commit/780395f79e078dd7c4af596f5dd66b344ce3ef34))
|
|
516
|
+
* **editor:** track running tools by call id ([72fbe37](https://github.com/zlliang/pi-spark/commit/72fbe370995296988bcd3100d2c25721b08af62b))
|
|
517
|
+
|
|
518
|
+
## [0.1.2](https://github.com/zlliang/pi-spark/compare/v0.1.1...v0.1.2) (2026-06-03)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
### Bug Fixes
|
|
522
|
+
|
|
523
|
+
* **editor:** add tildes spinner preset ([523f6fc](https://github.com/zlliang/pi-spark/commit/523f6fcd982455fbfe202982c5dfb7a62590b6dd))
|
|
524
|
+
|
|
525
|
+
## [0.1.1](https://github.com/zlliang/pi-spark/compare/v0.1.0...v0.1.1) (2026-06-02)
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
### Miscellaneous Chores
|
|
529
|
+
|
|
530
|
+
* release 0.1.1 ([09bfd96](https://github.com/zlliang/pi-spark/commit/09bfd96eec21dc9467bbbf10946bc5bb9cf858f7))
|
|
531
|
+
|
|
532
|
+
## [0.1.0](https://github.com/zlliang/pi-spark/releases/tag/v0.1.0) (2026-06-02)
|
|
533
|
+
|
|
534
|
+
Initial release of pi-spark, including the Editor, Footer, Fullscreen, Presets, and Recap extensions, plus shared TUI components and spark configuration support.
|
package/README.md
CHANGED
|
@@ -56,6 +56,12 @@ Banked rate-limit resets are saved benefits that can reset eligible Codex usage
|
|
|
56
56
|
|
|
57
57
|

|
|
58
58
|
|
|
59
|
+
#### Peak/off-peak pricing
|
|
60
|
+
|
|
61
|
+
Pi does not currently support time-based pricing, so its cost estimates can be inaccurate for providers with peak/off-peak rates, such as [DeepSeek](https://api-docs.deepseek.com/quick_start/pricing). pi-spark updates model cost metadata so subsequent usage calculations reflect the current rate.
|
|
62
|
+
|
|
63
|
+

|
|
64
|
+
|
|
59
65
|
### Presets
|
|
60
66
|
|
|
61
67
|
pi-spark lets you define named model presets in `spark.json` (see [Configuration](#configuration)), so you can switch between models and thinking levels without retyping provider details. The active preset is shown on the editor's top border.
|
|
@@ -145,7 +151,7 @@ All fields are optional. Each top-level feature runs with the defaults below unl
|
|
|
145
151
|
|
|
146
152
|
#### `CreditsConfig`
|
|
147
153
|
|
|
148
|
-
All supported providers are enabled by default. Set a provider to `false` to disable its credits status, or to `true` to override a global `false` in project config.
|
|
154
|
+
All supported providers are enabled by default. Set a provider to `false` to disable its credits status and pricing updates, or to `true` to override a global `false` in project config.
|
|
149
155
|
|
|
150
156
|
```json
|
|
151
157
|
{
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-spark",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Pi package that polishes your daily experience",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-coding-agent",
|
|
@@ -13,13 +13,14 @@
|
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"type": "module",
|
|
15
15
|
"files": [
|
|
16
|
-
"index.ts",
|
|
17
|
-
"src",
|
|
18
|
-
"themes",
|
|
19
16
|
"assets",
|
|
20
17
|
"docs",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
18
|
+
"src",
|
|
19
|
+
"themes",
|
|
20
|
+
"CHANGELOG.md",
|
|
21
|
+
"index.ts",
|
|
22
|
+
"LICENSE.md",
|
|
23
|
+
"README.md"
|
|
23
24
|
],
|
|
24
25
|
"pi": {
|
|
25
26
|
"extensions": [
|
|
@@ -19,8 +19,8 @@ const DEFAULT_INLINE_TEXT_OPTIONS: Required<InlineTextOptions> = {
|
|
|
19
19
|
/**
|
|
20
20
|
* Text component that renders inline and truncates it to fit the given width.
|
|
21
21
|
*
|
|
22
|
-
* The given width is a budget, not a guaranteed rendered width. After rendering,
|
|
23
|
-
*
|
|
22
|
+
* The given width is a budget, not a guaranteed rendered width. After rendering, check the result
|
|
23
|
+
* with visibleWidth(), because the rendered width may be smaller than the budget.
|
|
24
24
|
*/
|
|
25
25
|
export class InlineText implements Component {
|
|
26
26
|
private text: string;
|
package/src/config/index.ts
CHANGED
|
@@ -12,7 +12,7 @@ const CONFIG_FILE = "spark.json";
|
|
|
12
12
|
|
|
13
13
|
const cache = new Map<string, SparkConfig>();
|
|
14
14
|
|
|
15
|
-
/**
|
|
15
|
+
/** Loads and validates spark.json once per session lifecycle; later calls return the cached result. */
|
|
16
16
|
export function loadConfig(ctx: ExtensionContext): SparkConfig {
|
|
17
17
|
const cached = cache.get(ctx.cwd);
|
|
18
18
|
if (cached) return cached;
|
|
@@ -1,24 +1,15 @@
|
|
|
1
1
|
import { CreditsManager } from "./manager";
|
|
2
2
|
import { getEnabledProviders, registerProviderExtensions } from "./providers";
|
|
3
3
|
import { loadConfig } from "../../config";
|
|
4
|
-
import { isUsage } from "../../utils/usage";
|
|
5
4
|
|
|
6
|
-
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
7
5
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
8
6
|
|
|
9
|
-
function hasCost(message: AgentMessage): boolean {
|
|
10
|
-
const usage = (message as { usage?: unknown }).usage;
|
|
11
|
-
if (!isUsage(usage)) return false;
|
|
12
|
-
|
|
13
|
-
return usage.cost.total > 0 || usage.input > 0 || usage.output > 0;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
7
|
export function registerCredits(pi: ExtensionAPI): void {
|
|
17
8
|
let creditsManager: CreditsManager | undefined = undefined;
|
|
18
9
|
|
|
19
10
|
pi.on("session_start", (_event, ctx) => {
|
|
20
11
|
const config = loadConfig(ctx).credits;
|
|
21
|
-
if (!
|
|
12
|
+
if (!config) return;
|
|
22
13
|
|
|
23
14
|
const providers = getEnabledProviders(config);
|
|
24
15
|
if (providers.length === 0) return;
|
|
@@ -33,9 +24,19 @@ export function registerCredits(pi: ExtensionAPI): void {
|
|
|
33
24
|
creditsManager?.refresh(ctx);
|
|
34
25
|
});
|
|
35
26
|
|
|
36
|
-
pi.on("
|
|
37
|
-
|
|
27
|
+
pi.on("context", (_event, ctx) => {
|
|
28
|
+
creditsManager?.refresh(ctx);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
pi.on("turn_end", (_event, ctx) => {
|
|
32
|
+
creditsManager?.refresh(ctx);
|
|
33
|
+
});
|
|
38
34
|
|
|
35
|
+
pi.on("agent_settled", (_event, ctx) => {
|
|
36
|
+
creditsManager?.refresh(ctx);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
pi.on("session_before_compact", (_event, ctx) => {
|
|
39
40
|
creditsManager?.refresh(ctx);
|
|
40
41
|
});
|
|
41
42
|
|
|
@@ -43,6 +44,12 @@ export function registerCredits(pi: ExtensionAPI): void {
|
|
|
43
44
|
creditsManager?.refresh(ctx);
|
|
44
45
|
});
|
|
45
46
|
|
|
47
|
+
pi.on("session_before_tree", (event, ctx) => {
|
|
48
|
+
if (!event.preparation.userWantsSummary) return;
|
|
49
|
+
|
|
50
|
+
creditsManager?.refresh(ctx);
|
|
51
|
+
});
|
|
52
|
+
|
|
46
53
|
pi.on("session_tree", (event, ctx) => {
|
|
47
54
|
if (!event.summaryEntry) return;
|
|
48
55
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getAuthToken } from "../../utils/auth";
|
|
2
2
|
import { findProvider } from "./providers";
|
|
3
|
+
import { applyPricingRules } from "./pricing";
|
|
3
4
|
import { renderCredits, renderError } from "./status";
|
|
4
5
|
|
|
5
6
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
@@ -17,6 +18,7 @@ export class CreditsManager {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
async refresh(ctx: ExtensionContext): Promise<void> {
|
|
21
|
+
this.providers.forEach((provider) => applyPricingRules(ctx, provider, Date.now()));
|
|
20
22
|
this.inflight?.abort();
|
|
21
23
|
|
|
22
24
|
const provider = findProvider(this.providers, ctx.model?.provider);
|
|
@@ -55,12 +57,10 @@ export class CreditsManager {
|
|
|
55
57
|
return;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
const credits = await provider.fetch(token, signal);
|
|
60
|
+
const credits = await provider.fetch(token, signal, ctx.model?.cost);
|
|
61
|
+
if (signal.aborted || ctx.model?.provider !== provider.id) return;
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
if (ctx.model?.provider !== provider.id) return;
|
|
62
|
-
|
|
63
|
-
ctx.ui.setStatus(STATUS_KEY, renderCredits(ctx.ui.theme, provider.label, credits));
|
|
63
|
+
ctx.ui.setStatus(STATUS_KEY, renderCredits(ctx.ui.theme, provider.label, credits, provider.link));
|
|
64
64
|
} catch (error) {
|
|
65
65
|
if (signal.aborted) return;
|
|
66
66
|
if (ctx.model?.provider !== provider.id) return;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import type { CreditsProvider } from "./types";
|
|
4
|
+
|
|
5
|
+
export type Cost = Model<Api>["cost"] & { label?: string };
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A time-of-day price window. `start`/`end` are `HH:MM` in UTC. Bounds are [start, end); equal
|
|
9
|
+
* bounds mean all day. When `start > end` the window wraps past midnight (e.g., 16:30 → 00:30).
|
|
10
|
+
*/
|
|
11
|
+
interface PricingWindow {
|
|
12
|
+
start: string;
|
|
13
|
+
end: string;
|
|
14
|
+
days?: number[];
|
|
15
|
+
cost: Cost;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PricingRule {
|
|
19
|
+
model: string;
|
|
20
|
+
defaultCost: Cost;
|
|
21
|
+
windows: PricingWindow[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Updates registry and active model prices without changing the provider's rule data. */
|
|
25
|
+
export function applyPricingRules(ctx: ExtensionContext, provider: CreditsProvider, timestamp: number): void {
|
|
26
|
+
(provider.pricingRules ?? []).forEach((rule) => {
|
|
27
|
+
const cost = resolveCost(rule, timestamp);
|
|
28
|
+
const model = ctx.modelRegistry.find(provider.id, rule.model);
|
|
29
|
+
|
|
30
|
+
if (model) model.cost = { ...cost };
|
|
31
|
+
if (ctx.model?.provider === provider.id && ctx.model.id === rule.model) ctx.model.cost = { ...cost };
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function resolveCost(rule: PricingRule, timestamp: number): Cost {
|
|
36
|
+
const date = new Date(timestamp);
|
|
37
|
+
const minutes = date.getUTCHours() * 60 + date.getUTCMinutes();
|
|
38
|
+
|
|
39
|
+
const cost = rule.windows.reduce((result, window) => {
|
|
40
|
+
if (window.days && !window.days.includes(date.getUTCDay())) return result;
|
|
41
|
+
if (inDailyRange(minutes, parseHHMM(window.start), parseHHMM(window.end))) return window.cost;
|
|
42
|
+
return result;
|
|
43
|
+
}, rule.defaultCost);
|
|
44
|
+
|
|
45
|
+
return cost;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function parseHHMM(hhmm: string): number {
|
|
49
|
+
const [hours, minutes] = hhmm.split(":");
|
|
50
|
+
return Number(hours) * 60 + Number(minutes);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function inDailyRange(minutes: number, start: number, end: number): boolean {
|
|
54
|
+
if (start === end) return true;
|
|
55
|
+
if (start < end) return minutes >= start && minutes < end;
|
|
56
|
+
return minutes >= start || minutes < end;
|
|
57
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { convertToUSD, toNumber } from "../../../utils/format";
|
|
2
2
|
import { http, withAuth } from "../../../utils/http";
|
|
3
3
|
|
|
4
|
+
import type { Cost } from "../pricing";
|
|
4
5
|
import type { Credits, CreditsProvider } from "../types";
|
|
5
6
|
|
|
6
7
|
const PROVIDER = "deepseek";
|
|
@@ -18,12 +19,50 @@ interface DeepSeekBalanceInfo {
|
|
|
18
19
|
export const deepseekProvider: CreditsProvider = {
|
|
19
20
|
id: PROVIDER,
|
|
20
21
|
label: "DeepSeek",
|
|
22
|
+
link: "https://platform.deepseek.com/usage",
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
// See https://api-docs.deepseek.com/quick_start/pricing/.
|
|
25
|
+
pricingRules: [
|
|
26
|
+
{
|
|
27
|
+
model: "deepseek-flash",
|
|
28
|
+
defaultCost: { input: 0.15, output: 0.6, cacheRead: 0.003, cacheWrite: 0, label: "off-peak" },
|
|
29
|
+
windows: [
|
|
30
|
+
{ start: "01:00", end: "04:00", days: [1, 2, 3, 4, 5], cost: { input: 0.3, output: 1.2, cacheRead: 0.006, cacheWrite: 0, label: "peak" } },
|
|
31
|
+
{ start: "06:00", end: "10:00", days: [1, 2, 3, 4, 5], cost: { input: 0.3, output: 1.2, cacheRead: 0.006, cacheWrite: 0, label: "peak" } },
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
model: "deepseek-v4-flash",
|
|
36
|
+
defaultCost: { input: 0.15, output: 0.6, cacheRead: 0.003, cacheWrite: 0, label: "off-peak" },
|
|
37
|
+
windows: [
|
|
38
|
+
{ start: "01:00", end: "04:00", days: [1, 2, 3, 4, 5], cost: { input: 0.3, output: 1.2, cacheRead: 0.006, cacheWrite: 0, label: "peak" } },
|
|
39
|
+
{ start: "06:00", end: "10:00", days: [1, 2, 3, 4, 5], cost: { input: 0.3, output: 1.2, cacheRead: 0.006, cacheWrite: 0, label: "peak" } },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
model: "deepseek-v4-flash-vision-exp",
|
|
44
|
+
defaultCost: { input: 0.15, output: 0.6, cacheRead: 0.003, cacheWrite: 0, label: "off-peak" },
|
|
45
|
+
windows: [
|
|
46
|
+
{ start: "01:00", end: "04:00", days: [1, 2, 3, 4, 5], cost: { input: 0.3, output: 1.2, cacheRead: 0.006, cacheWrite: 0, label: "peak" } },
|
|
47
|
+
{ start: "06:00", end: "10:00", days: [1, 2, 3, 4, 5], cost: { input: 0.3, output: 1.2, cacheRead: 0.006, cacheWrite: 0, label: "peak" } },
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
// Update to Flash rates when V4 Pro retires on September 14, 2026 at 04:00 UTC.
|
|
52
|
+
model: "deepseek-v4-pro",
|
|
53
|
+
defaultCost: { input: 0.66, output: 1.98, cacheRead: 0.022, cacheWrite: 0, label: "off-peak" },
|
|
54
|
+
windows: [
|
|
55
|
+
{ start: "01:00", end: "04:00", days: [1, 2, 3, 4, 5], cost: { input: 1.32, output: 3.96, cacheRead: 0.044, cacheWrite: 0, label: "peak" } },
|
|
56
|
+
{ start: "06:00", end: "10:00", days: [1, 2, 3, 4, 5], cost: { input: 1.32, output: 3.96, cacheRead: 0.044, cacheWrite: 0, label: "peak" } },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
|
|
61
|
+
async fetch(apiKey, signal, cost?: Cost): Promise<Credits> {
|
|
23
62
|
const payload = await withAuth(http, apiKey).get(URL, { signal }).json<DeepSeekBalanceResponse>();
|
|
24
63
|
const balance = payload.balance_infos?.find((entry) => entry.currency === "USD") ?? payload.balance_infos?.[0];
|
|
25
64
|
const remaining = await convertToUSD(toNumber(balance?.total_balance), balance?.currency, signal);
|
|
26
65
|
|
|
27
|
-
return { type: "balance", remaining };
|
|
66
|
+
return { type: "balance", remaining, suffix: cost?.label ? `(${cost.label})` : undefined };
|
|
28
67
|
},
|
|
29
68
|
};
|
|
@@ -107,6 +107,7 @@ function moneyToNumber(money: Money | null | undefined): number | undefined {
|
|
|
107
107
|
export const fireworksProvider: CreditsProvider = {
|
|
108
108
|
id: PROVIDER,
|
|
109
109
|
label: "Fireworks",
|
|
110
|
+
link: "https://app.fireworks.ai/account/billing",
|
|
110
111
|
|
|
111
112
|
async fetch(apiKey, signal): Promise<Credits> {
|
|
112
113
|
const name = await resolveAccount(apiKey, signal);
|
|
@@ -31,5 +31,5 @@ export function findProvider(providers: CreditsProvider[], provider?: string): C
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export function registerProviderExtensions(pi: ExtensionAPI, ctx: ExtensionContext, providers: CreditsProvider[], refresh: RefreshCredits): void {
|
|
34
|
-
|
|
34
|
+
providers.forEach((provider) => provider.register?.(pi, ctx, refresh));
|
|
35
35
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { toNumber } from "../../../utils/format";
|
|
1
|
+
import { convertToUSD, toNumber } from "../../../utils/format";
|
|
2
2
|
import { http, withAuth } from "../../../utils/http";
|
|
3
3
|
|
|
4
4
|
import type { Credits, CreditsLane, CreditsProvider } from "../types";
|
|
@@ -11,6 +11,7 @@ interface KimiCodeUsage {
|
|
|
11
11
|
limit?: string | number;
|
|
12
12
|
used?: string | number;
|
|
13
13
|
remaining?: string | number;
|
|
14
|
+
resetTime?: string | null;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
interface KimiCodeLimit {
|
|
@@ -24,6 +25,8 @@ interface KimiCodeBoosterWallet {
|
|
|
24
25
|
amount?: string | number;
|
|
25
26
|
amountLeft?: string | number;
|
|
26
27
|
} | null;
|
|
28
|
+
monthlyChargeLimit?: { currency?: string } | null;
|
|
29
|
+
monthlyUsed?: { currency?: string } | null;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
interface KimiCodeUsageResponse {
|
|
@@ -50,24 +53,24 @@ function formatWindowLabel(window?: { duration?: number; timeUnit?: string } | n
|
|
|
50
53
|
if (!window?.duration) return "Limit";
|
|
51
54
|
|
|
52
55
|
const duration = window.duration;
|
|
53
|
-
const unit = window.timeUnit
|
|
56
|
+
const unit = window.timeUnit;
|
|
54
57
|
|
|
55
|
-
if (unit === "TIME_UNIT_MINUTE") {
|
|
56
|
-
if (duration >= 60 && duration % 60 === 0) return `${duration / 60}h`;
|
|
57
|
-
return `${duration}m`;
|
|
58
|
-
}
|
|
58
|
+
if (unit === "TIME_UNIT_MINUTE") return (duration >= 60 && duration % 60 === 0) ? `${duration / 60}h` : `${duration}m`;
|
|
59
59
|
if (unit === "TIME_UNIT_HOUR") return `${duration}h`;
|
|
60
60
|
if (unit === "TIME_UNIT_DAY") return `${duration}d`;
|
|
61
|
-
if (unit === "
|
|
62
|
-
return
|
|
61
|
+
if (unit === "TIME_UNIT_WEEK") return `${duration * 7}d`;
|
|
62
|
+
return "Limit";
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function buildLane(label: string, detail: KimiCodeUsage): CreditsLane | undefined {
|
|
66
66
|
const percent = toPercent(detail);
|
|
67
|
-
|
|
67
|
+
if (percent === undefined) return undefined;
|
|
68
|
+
|
|
69
|
+
const resetAt = Date.parse(detail.resetTime ?? "");
|
|
70
|
+
return { label, percent, resetAt: Number.isFinite(resetAt) ? resetAt : undefined };
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
function formatSuffix(wallet
|
|
73
|
+
async function formatSuffix(wallet: KimiCodeBoosterWallet | null | undefined, signal: AbortSignal): Promise<string | undefined> {
|
|
71
74
|
if (wallet?.balance?.type !== "BOOSTER") return undefined;
|
|
72
75
|
|
|
73
76
|
const amount = toNumber(wallet.balance.amount);
|
|
@@ -75,7 +78,9 @@ function formatSuffix(wallet?: KimiCodeBoosterWallet | null): string | undefined
|
|
|
75
78
|
|
|
76
79
|
const amountLeft = Math.max(0, toNumber(wallet.balance.amountLeft) ?? 0);
|
|
77
80
|
const remaining = Math.round(amountLeft / BOOSTER_FIXED_POINT_CENTS) / 100;
|
|
78
|
-
|
|
81
|
+
const currency = wallet.monthlyChargeLimit?.currency || wallet.monthlyUsed?.currency || "USD";
|
|
82
|
+
const remainingUSD = await convertToUSD(remaining, currency, signal);
|
|
83
|
+
return !!remainingUSD ? `(Extra Usage $${remainingUSD.toFixed(2)})` : undefined;
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
export const kimiCodeProvider: CreditsProvider = {
|
|
@@ -100,6 +105,6 @@ export const kimiCodeProvider: CreditsProvider = {
|
|
|
100
105
|
|
|
101
106
|
if (lanes.length === 0) throw new Error("no usage data");
|
|
102
107
|
|
|
103
|
-
return { type: "windows", lanes, suffix: formatSuffix(payload.boosterWallet) };
|
|
108
|
+
return { type: "windows", lanes, suffix: await formatSuffix(payload.boosterWallet, signal) };
|
|
104
109
|
},
|
|
105
110
|
};
|
|
@@ -30,6 +30,7 @@ interface CodexUsageResponse {
|
|
|
30
30
|
interface CodexRateWindow {
|
|
31
31
|
limit_window_seconds: number;
|
|
32
32
|
used_percent?: number | string;
|
|
33
|
+
reset_at?: number;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
export interface BankedRateLimitReset {
|
|
@@ -120,6 +121,7 @@ function toLane(window: CodexRateWindow): CreditsLane {
|
|
|
120
121
|
return {
|
|
121
122
|
label: prettyMilliseconds(window.limit_window_seconds * 1_000, { compact: true }),
|
|
122
123
|
percent: parseUsedPercent(window),
|
|
124
|
+
resetAt: !!window.reset_at ? window.reset_at * 1_000 : undefined,
|
|
123
125
|
};
|
|
124
126
|
}
|
|
125
127
|
|
|
@@ -131,6 +133,7 @@ function parseUsedPercent(window?: CodexRateWindow | null): number | undefined {
|
|
|
131
133
|
export const openaiCodexProvider: CreditsProvider = {
|
|
132
134
|
id: PROVIDER,
|
|
133
135
|
label: "Codex",
|
|
136
|
+
link: "https://chatgpt.com/#settings/Usage",
|
|
134
137
|
|
|
135
138
|
async fetch(apiKey, signal): Promise<Credits> {
|
|
136
139
|
const client = createClient(apiKey);
|
|
@@ -16,6 +16,7 @@ interface OpenRouterCreditsResponse {
|
|
|
16
16
|
export const openrouterProvider: CreditsProvider = {
|
|
17
17
|
id: PROVIDER,
|
|
18
18
|
label: "OpenRouter",
|
|
19
|
+
link: "https://openrouter.ai/settings/credits",
|
|
19
20
|
|
|
20
21
|
async fetch(apiKey, signal): Promise<Credits> {
|
|
21
22
|
const payload = await withAuth(http, apiKey).get(URL, { signal }).json<OpenRouterCreditsResponse>();
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { hyperlink } from "@earendil-works/pi-tui";
|
|
2
|
+
import prettyMilliseconds from "pretty-ms";
|
|
3
|
+
|
|
1
4
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
5
|
import type { Credits, CreditsLane } from "./types";
|
|
3
6
|
|
|
@@ -6,12 +9,13 @@ const WINDOWS_ERROR = 90;
|
|
|
6
9
|
const BALANCE_WARNING = 10;
|
|
7
10
|
const BALANCE_ERROR = 5;
|
|
8
11
|
|
|
9
|
-
export function renderCredits(theme: Theme, label: string, credits: Credits): string {
|
|
12
|
+
export function renderCredits(theme: Theme, label: string, credits: Credits, link?: string): string {
|
|
10
13
|
const styledLabel = theme.fg("dim", label ? `${label} ` : "");
|
|
11
14
|
const value = credits.type === "windows" ? renderWindows(theme, credits) : renderBalance(theme, credits);
|
|
12
15
|
const suffix = credits.suffix ? ` ${theme.fg("dim", credits.suffix)}` : "";
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
const text = `${styledLabel}${value}${suffix}`;
|
|
18
|
+
return link ? hyperlink(text, link) : text;
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
export function renderError(theme: Theme, label: string, message: string): string {
|
|
@@ -26,11 +30,11 @@ function renderWindows(theme: Theme, credits: Extract<Credits, { type: "windows"
|
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
function renderLane(theme: Theme, lane: CreditsLane): string {
|
|
29
|
-
const
|
|
33
|
+
const percent = `${lane.label} ${lane.percent === undefined ? "?" : lane.percent.toFixed(0)}%`;
|
|
34
|
+
const color = lane.percent && lane.percent > WINDOWS_ERROR ? "error" : lane.percent && lane.percent > WINDOWS_WARNING ? "warning" : "success";
|
|
35
|
+
const resetAt = !!lane.resetAt ? ` ${theme.fg("dim", `[⟳ ${prettyMilliseconds(Math.max(0, lane.resetAt - Date.now()), { compact: true })}]`)}` : "";
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
if (lane.percent && lane.percent > WINDOWS_WARNING) return theme.fg("warning", text);
|
|
33
|
-
return theme.fg("success", text);
|
|
37
|
+
return `${theme.fg(color, percent)}${resetAt}`;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
function renderBalance(theme: Theme, credits: Extract<Credits, { type: "balance" }>): string {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { CreditsProviderId } from "./config";
|
|
3
|
+
import type { Cost, PricingRule } from "./pricing";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Normalized credits/usage for a provider.
|
|
@@ -15,6 +16,8 @@ export type Credits = (
|
|
|
15
16
|
export interface CreditsLane {
|
|
16
17
|
label: string;
|
|
17
18
|
percent: number | undefined;
|
|
19
|
+
/** Timestamp in milliseconds. */
|
|
20
|
+
resetAt?: number;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
export type RefreshCredits = (ctx: ExtensionContext) => Promise<void>;
|
|
@@ -23,6 +26,8 @@ export type RefreshCredits = (ctx: ExtensionContext) => Promise<void>;
|
|
|
23
26
|
export interface CreditsProvider {
|
|
24
27
|
readonly id: CreditsProviderId;
|
|
25
28
|
readonly label: string;
|
|
26
|
-
|
|
29
|
+
readonly link?: string;
|
|
30
|
+
readonly pricingRules?: PricingRule[];
|
|
31
|
+
fetch(apiKey: string, signal: AbortSignal, cost?: Cost): Promise<Credits>;
|
|
27
32
|
register?(pi: ExtensionAPI, ctx: ExtensionContext, refresh: RefreshCredits): void;
|
|
28
33
|
}
|
|
@@ -53,12 +53,12 @@ class Editor extends CustomEditor {
|
|
|
53
53
|
const lines = super.render(width);
|
|
54
54
|
if (lines.length === 0) return lines;
|
|
55
55
|
|
|
56
|
-
lines[0] = this.
|
|
56
|
+
lines[0] = this.getTopBorder(width);
|
|
57
57
|
|
|
58
58
|
return lines;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
private
|
|
61
|
+
private getTopBorder(width: number): string {
|
|
62
62
|
const theme = this.ctx.ui.theme;
|
|
63
63
|
|
|
64
64
|
const left = this.getLeft();
|
|
@@ -63,7 +63,7 @@ class FooterComponent implements Component {
|
|
|
63
63
|
return [statusesText, styledCostText, styledContextUsageText].filter(Boolean).join(this.theme.fg("dim", " · "));
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
/**
|
|
66
|
+
/** Gets extension statuses, sorted alphabetically by key. */
|
|
67
67
|
private getStatusesText(): string {
|
|
68
68
|
const extensionStatuses = this.footerData.getExtensionStatuses();
|
|
69
69
|
if (extensionStatuses.size === 0) return "";
|
|
@@ -3,7 +3,7 @@ import * as z from "zod";
|
|
|
3
3
|
|
|
4
4
|
const MIN_IDLE_MS = 5_000;
|
|
5
5
|
|
|
6
|
-
/**
|
|
6
|
+
/** Accepts a millisecond number or a human-readable duration (e.g., "3m"), normalized to milliseconds. */
|
|
7
7
|
export const idleTimeoutSchema = z
|
|
8
8
|
.union([z.number(), z.string()])
|
|
9
9
|
.transform((value, ctx) => {
|
|
@@ -76,7 +76,7 @@ export class IdleListener<T> {
|
|
|
76
76
|
this.enterCallbacks.forEach((callback) => callback(ctx));
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
/**
|
|
79
|
+
/** Emits on every wake signal, even when the listener is already active. */
|
|
80
80
|
wake(ctx: T): void {
|
|
81
81
|
this.stop();
|
|
82
82
|
this.state = "active";
|
|
@@ -30,7 +30,7 @@ export class TitleManager {
|
|
|
30
30
|
this.config = config;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/**
|
|
33
|
+
/** Generates and sets the session title once, silently, from the current context. */
|
|
34
34
|
async run(ctx: ExtensionContext): Promise<void> {
|
|
35
35
|
if (this.pi.getSessionName() || this.inflight) return;
|
|
36
36
|
|
package/src/utils/format.ts
CHANGED
|
@@ -36,7 +36,7 @@ export function formatCwd(cwd: string, home: string): string {
|
|
|
36
36
|
return shortenPath(displayCwd);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
/**
|
|
39
|
+
/** Shortens every path component except the first and last to one character, like fish `prompt_pwd`. */
|
|
40
40
|
function shortenPath(path: string): string {
|
|
41
41
|
const parts = path.split(sep);
|
|
42
42
|
const lastIndex = parts.length - 1;
|
|
@@ -47,7 +47,7 @@ function shortenPath(path: string): string {
|
|
|
47
47
|
}).join(sep);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
/**
|
|
50
|
+
/** Replaces newlines, tabs, and carriage returns with spaces, then collapses multiple spaces. */
|
|
51
51
|
export function sanitizeText(text: string): string {
|
|
52
52
|
return text
|
|
53
53
|
.replace(/[\r\n\t]/g, " ")
|
|
@@ -55,7 +55,7 @@ export function sanitizeText(text: string): string {
|
|
|
55
55
|
.trim();
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
/**
|
|
58
|
+
/** Coerces a possibly-stringified numeric value to a finite number, or `undefined`. */
|
|
59
59
|
export function toNumber(value?: string | number | null): number | undefined {
|
|
60
60
|
if (value === undefined || value === null) return undefined;
|
|
61
61
|
const parsed = typeof value === "number" ? value : Number(value);
|
|
@@ -68,7 +68,7 @@ interface FrankfurterRateResponse {
|
|
|
68
68
|
rate?: string | number;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
/**
|
|
71
|
+
/** Converts an amount in the given currency to USD via the Frankfurter API. */
|
|
72
72
|
export async function convertToUSD(amount: number | undefined, currency: string | undefined, signal: AbortSignal): Promise<number | undefined> {
|
|
73
73
|
if (amount === undefined) return undefined;
|
|
74
74
|
if (!currency || currency === "USD") return amount;
|
package/src/utils/model.ts
CHANGED
|
@@ -27,7 +27,7 @@ type ThinkingLevelSelection = {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
30
|
+
* Completes a one-shot background request, using an isolated session for OpenAI Codex models.
|
|
31
31
|
* See [the investigation](../../docs/background-model-calls-and-openai-codex-sessions.md).
|
|
32
32
|
*/
|
|
33
33
|
export const completeBackground: typeof completeSimple = async (model, context, options) => {
|
|
@@ -43,7 +43,7 @@ export const completeBackground: typeof completeSimple = async (model, context,
|
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
46
|
+
* Resolves the model and thinking level for a background feature (recap, title, ...).
|
|
47
47
|
*
|
|
48
48
|
* `feature` names the config section so warnings can point at the offending fields. When the
|
|
49
49
|
* feature's model config is incomplete or unavailable, this falls back to the session's main
|
package/src/utils/usage.ts
CHANGED
|
@@ -26,9 +26,9 @@ export function isUsage(value: unknown): value is Usage {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
29
|
+
* Extracts usage from a session entry.
|
|
30
30
|
*
|
|
31
|
-
* `usage`
|
|
31
|
+
* `usage` lives in different fields depending on the entry type:
|
|
32
32
|
*
|
|
33
33
|
* - `message` carries them on `message`.
|
|
34
34
|
* - `custom` on `data`.
|