claude-token-saver 3.4.0 → 3.4.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-token-saver",
3
- "version": "3.4.0",
3
+ "version": "3.4.2",
4
4
  "description": "Save tokens on Claude Code — spike diagnosis, 1M-context detection, TTL countdown, statusline. (formerly claude-cache-monitor)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -35,6 +35,10 @@ import { homedir } from 'node:os';
35
35
  // Post-promotion delegated-category error rate above this flags the rule
36
36
  // for review (rule-health). Calibrated against local T0 avg error incidence.
37
37
  export const HEALTH_ERR_RATE = 0.2;
38
+ // Below this many shape-eligible episodes in the window the error rate is
39
+ // noise (1 error in 4 episodes = 25% — instant flag), so the review flag is
40
+ // withheld until the sample is large enough to mean something.
41
+ export const HEALTH_MIN_SAMPLE = 10;
38
42
 
39
43
  function stateDir() {
40
44
  if (process.platform === 'win32') {
@@ -214,7 +218,7 @@ export function refreshModelRules(episodeStats, { now } = {}) {
214
218
  r.count = s.count;
215
219
  r.errRate = s.epCount > 0 ? s.errCount / s.epCount : 0;
216
220
  r.lastSeen = now || r.lastSeen;
217
- r.status = r.errRate > HEALTH_ERR_RATE ? 'review' : 'active';
221
+ r.status = r.errRate > HEALTH_ERR_RATE && s.epCount >= HEALTH_MIN_SAMPLE ? 'review' : 'active';
218
222
  changed = true;
219
223
  }
220
224
  if (changed) {
@@ -32,6 +32,24 @@ function contentText(content) {
32
32
  const MUTATING_TOOLS = new Set(['Edit', 'Write', 'NotebookEdit', 'Bash']);
33
33
  const DELEGATION_TOOLS = new Set(['Task', 'Agent']);
34
34
 
35
+ // Permission rejections and harness policy denials arrive as is_error
36
+ // tool_results, but they encode the user's choice / the permission system's
37
+ // policy, not task difficulty — counting them poisons the rule-health error
38
+ // rate. Measured on a 14-day window: 6 user rejections + ~29 auto-mode
39
+ // classifier denials out of 142 is_error results (~25% of the numerator).
40
+ const REJECTION_RE = /doesn't want to proceed|tool use was rejected|doesn't want to take this action|denied by the claude code auto mode classifier|permission for this action was denied|requires approval/i;
41
+
42
+ function toolResultText(content) {
43
+ if (typeof content === 'string') return content;
44
+ if (!Array.isArray(content)) return '';
45
+ return content.map((b) => (b && typeof b.text === 'string' ? b.text : '')).join(' ');
46
+ }
47
+
48
+ function isRealToolError(block) {
49
+ return block && block.type === 'tool_result' && block.is_error &&
50
+ !REJECTION_RE.test(toolResultText(block.content));
51
+ }
52
+
35
53
  export async function collectSessionRecords(filePath, { includeContent = true } = {}) {
36
54
  const records = new Map();
37
55
  let depth = 0;
@@ -62,7 +80,7 @@ export async function collectSessionRecords(filePath, { includeContent = true }
62
80
  // follows the assistant call — attribute them to that call's record.
63
81
  if (lastRecord && Array.isArray(msg.content)) {
64
82
  for (const b of msg.content) {
65
- if (b && b.type === 'tool_result' && b.is_error) lastRecord.toolErrors += 1;
83
+ if (isRealToolError(b)) lastRecord.toolErrors += 1;
66
84
  }
67
85
  }
68
86
  continue;