@xinghe-labs/pi-context-stats 1.0.1 → 1.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/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.1.0] - 2026-10-27
6
+
7
+ ### Added
8
+ - **预算/阈值提醒**:本会话花费达 $10 或上下文占用 ≥85% 时 `warning` 提醒一次;压缩后占用警报解除可再次触发
9
+ - **压缩前预警**(`session_before_compact`):显示 reason / 压缩前 tokens / 是否 willRetry —— 这是判断「要不要主动 /compact」的最后时机
10
+ - **turn_end 时序修正**:状态栏改为整个回合结束时刷新一次,消除一个回合多条 assistant 消息时的中间值闪烁
11
+
5
12
  ## [1.0.0] - 2026-10-27
6
13
 
7
14
  ### Added
package/README.md CHANGED
@@ -42,7 +42,7 @@ pi 扩展:追踪上下文压缩、prompt 缓存命中与 token 花费。**零依
42
42
  ## 安装
43
43
 
44
44
  ```bash
45
- pi install npm:pi-context-stats
45
+ pi install npm:@xinghe-labs/pi-context-stats
46
46
  ```
47
47
 
48
48
  或从本地路径:
@@ -51,6 +51,14 @@ pi install npm:pi-context-stats
51
51
  pi install /path/to/context-stats
52
52
  ```
53
53
 
54
+ ## 提醒与预警
55
+
56
+ | 时机 | 触发条件 | 行为 |
57
+ |---|---|---|
58
+ | `message_end` / `turn_end` | 会话花费 ≥ $10 | `warning`:💰 花费达阈值(每会话一次) |
59
+ | `message_end` / `turn_end` | 上下文占用 ≥ 85% | `warning`:🧠 占用阈值,建议提前落盘重要结论(每会话一次;压缩后重置可再触发) |
60
+ | `session_before_compact` | pi 即将压缩 | `info`:📦 准备压缩(reason · tokens · willRetry)|
61
+
54
62
  ## 统计口径
55
63
 
56
64
  ### 计入的用量来源
@@ -170,6 +170,26 @@ export default function (pi: ExtensionAPI) {
170
170
  // 压缩原因只存在于 live 事件,条目里没有;写入自定义条目以便 resume 后回填
171
171
  const REASON_LOG = "context-stats.compaction";
172
172
 
173
+ // 预算/阈值提醒:默认 $10 / 上下文 85%,各提醒一次后不再重复(新会话重置)
174
+ const DEFAULT_LIMITS = { cost: 10, contextPercent: 85 };
175
+ let alerted = { cost: false, context: false };
176
+
177
+ function checkThresholds(ctx: ExtensionContext): void {
178
+ if (!ctx.hasUI || (alerted.cost && alerted.context)) return;
179
+ if (!alerted.cost && stats.cost >= DEFAULT_LIMITS.cost) {
180
+ alerted.cost = true;
181
+ ctx.ui.notify(`💰 本会话花费已达 $${stats.cost.toFixed(2)}(阈值 $${DEFAULT_LIMITS.cost})`, "warning");
182
+ }
183
+ const cu = ctx.getContextUsage?.();
184
+ if (!alerted.context && cu && cu.percent !== null && cu.percent >= DEFAULT_LIMITS.contextPercent) {
185
+ alerted.context = true;
186
+ ctx.ui.notify(
187
+ `🧠 上下文占用 ${cu.percent.toFixed(0)}%(阈值 ${DEFAULT_LIMITS.contextPercent}%),可能即将压缩,重要结论建议先落盘到文件`,
188
+ "warning",
189
+ );
190
+ }
191
+ }
192
+
173
193
  function newStats(): Stats {
174
194
  return {
175
195
  compactions: [],
@@ -249,6 +269,7 @@ export default function (pi: ExtensionAPI) {
249
269
 
250
270
  pi.on("session_start", async (_event, ctx) => {
251
271
  stats = newStats();
272
+ alerted = { cost: false, context: false };
252
273
  const entries = ctx.sessionManager.getBranch();
253
274
  // 先用自定义条目建 id → reason 映射(压缩条目本身不记录原因)
254
275
  const reasons = new Map<string, string>();
@@ -287,7 +308,23 @@ export default function (pi: ExtensionAPI) {
287
308
  const m = event.message as { role?: string; usage?: UsageLike };
288
309
  if (m.role !== "assistant") return;
289
310
  accumulate(m.usage);
311
+ checkThresholds(ctx);
312
+ });
313
+
314
+ // turn_end:整个回合结束才刷新状态栏,避免一个回合多条 assistant 消息时中间值闪烁
315
+ pi.on("turn_end", async (_event, ctx) => {
290
316
  updateStatus(ctx);
317
+ checkThresholds(ctx);
318
+ });
319
+
320
+ // 压缩前预警:这是判断「要不要主动 /compact」的最后时机
321
+ pi.on("session_before_compact", async (event, ctx) => {
322
+ if (!ctx.hasUI) return;
323
+ const tokensBefore = event.preparation?.tokensBefore ?? 0;
324
+ ctx.ui.notify(
325
+ `📦 准备压缩(reason=${event.reason} · 压缩前 ${fmt(tokensBefore)} tokens${event.willRetry ? " · 完成后重试本轮" : ""})`,
326
+ "info",
327
+ );
291
328
  });
292
329
 
293
330
  pi.on("session_compact", async (event, ctx) => {
@@ -308,6 +345,8 @@ export default function (pi: ExtensionAPI) {
308
345
  // 压缩自身的 LLM 调用走 completeSimple 直调,不触发 message_end,必须在这里记账
309
346
  accumulate(entry.usage, "compaction");
310
347
  if (entry.id) pi.appendEntry(REASON_LOG, { id: entry.id, reason });
348
+ // 上下文被压缩重置,占用警报解除
349
+ alerted.context = false;
311
350
  updateStatus(ctx);
312
351
  if (ctx.hasUI) {
313
352
  const costText = cost > 0 ? ` · $${cost.toFixed(4)}` : "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xinghe-labs/pi-context-stats",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "pi 扩展:状态栏实时显示压缩次数+缓存读+花费,/ctxstats 展示压缩历史、缓存命中率、缓存浪费(miss 计费)与各项费用分解",
5
5
  "keywords": [
6
6
  "pi-package",