costhawk 1.5.12 → 1.5.13

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.
@@ -1,2 +1,2 @@
1
- export declare const BUILD_COMMIT_SHA = "d2dc17c";
1
+ export declare const BUILD_COMMIT_SHA = "1ed40d7";
2
2
  //# sourceMappingURL=build-info.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Auto-generated during release builds. Values may be empty in dev.
2
- export const BUILD_COMMIT_SHA = "d2dc17c";
2
+ export const BUILD_COMMIT_SHA = "1ed40d7";
3
3
  //# sourceMappingURL=build-info.js.map
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Cursor Local SQLite Parser (PR1 — dry-run only)
2
+ * Cursor Local SQLite Parser
3
3
  *
4
4
  * Parses Cursor IDE chat history from the local SQLite database to extract
5
- * token usage data. Read-only. Does not push to any backend.
5
+ * token usage and timestamps. Read-only. Does not push to any backend.
6
6
  *
7
7
  * Storage:
8
8
  * macOS: ~/Library/Application Support/Cursor/User/globalStorage/state.vscdb
@@ -18,22 +18,40 @@
18
18
  * on bubble rows. Model name at $.modelInfo.modelName. Server-side dedup id
19
19
  * at $.serverBubbleId.
20
20
  *
21
- * NOTE (PR1 scope): Cursor message timestamps are not yet verified across
22
- * versions, so this dry-run parser does NOT return startTime/endTime/dailyUsage.
23
- * PR2 will add timestamp support after verification on real Cursor data.
21
+ * Timestamps (verified in Task #30 against a real state.vscdb):
22
+ * - $.createdAt on bubbles is an ISO 8601 string (~56% coverage, all-or-
23
+ * nothing per composer likely added in a newer Cursor version).
24
+ * - $.createdAt on composerData rows is a Unix milliseconds number (100%
25
+ * coverage). Same field name, different type — parser handles both.
26
+ * - $.lastUpdatedAt on composerData rows is Unix ms (~13% coverage).
27
+ * - $.timingInfo.client* on bubbles is performance.now()-style relative
28
+ * (seconds since Cursor process start), NOT absolute — never use it as
29
+ * a wall-clock timestamp.
24
30
  *
25
- * Workspace metadata fields (workspaceHash/workspaceName) are also unverified
26
- * and return null until PR2 confirms the stable field source.
31
+ * Fallback ladder for per-session timestamps: prefer min/max of bubble
32
+ * createdAt when present, otherwise use composerData.createdAt with optional
33
+ * composerData.lastUpdatedAt as end time. Every session gets non-null
34
+ * timestamps; the `timestampSource` / `timestampQuality` fields surface
35
+ * whether the values are precise or approximate.
36
+ *
37
+ * Workspace metadata fields (workspaceHash/workspaceName) remain unverified
38
+ * and return null. composerData.name is a candidate for workspaceName but
39
+ * has not been confirmed yet.
27
40
  */
28
41
  import type { TokenUsage } from "./transcript-parser.js";
29
42
  /**
30
- * Single Cursor session in dry-run output.
43
+ * Single Cursor session in parser output.
31
44
  *
32
45
  * Shape is intentionally distinct from Claude/Codex SessionUsage because
33
- * Cursor message timestamps are not yet verified startTime/endTime are
34
- * deliberately absent until PR2 verifies the stable timestamp source.
46
+ * Cursor's timestamp coverage is partial provenance fields surface
47
+ * whether startTime/endTime/dailyUsage came from per-bubble timestamps
48
+ * (precise) or a composer-level fallback (approximate).
49
+ *
50
+ * workspaceHash / workspaceName remain null — composerData.name is a
51
+ * candidate for workspaceName but was not verified in Task #30 and is
52
+ * deferred to a later release.
35
53
  */
36
- export interface CursorSessionUsageDryRun {
54
+ export interface CursorSessionUsage {
37
55
  sessionId: string;
38
56
  workspaceHash: string | null;
39
57
  workspaceName: string | null;
@@ -41,13 +59,74 @@ export interface CursorSessionUsageDryRun {
41
59
  tokens: TokenUsage;
42
60
  messageCount: number;
43
61
  filePath: string;
62
+ startTime: string | null;
63
+ endTime: string | null;
64
+ timestampSource: "bubble" | "composer" | "mixed" | "none";
65
+ timestampQuality: "precise" | "approximate" | "none";
66
+ dailyUsage: Record<string, TokenUsage>;
67
+ dailyUsageSource: "bubble" | "composer" | "none";
68
+ }
69
+ /**
70
+ * Backward-compat alias. PR1 shipped `CursorSessionUsageDryRun` in the
71
+ * published `.d.ts`; removing it in a patch release would be a breaking
72
+ * change for anyone who imported the type. The alias will be kept for at
73
+ * least one release after the rename.
74
+ */
75
+ export type CursorSessionUsageDryRun = CursorSessionUsage;
76
+ /**
77
+ * Transparency payload for the `what_we_read` MCP mode. Describes where
78
+ * the parser is reading from and what it sees at the table/prefix level,
79
+ * without leaking conversation content. UUIDs in sample keys are
80
+ * truncated to 8 characters — enough to distinguish keys at a glance,
81
+ * not enough to be a stable correlation handle.
82
+ */
83
+ export interface CursorMeta {
84
+ filePath: string;
85
+ dbFileSize: number;
86
+ tables: string[];
87
+ keyPrefixes: Record<string, number>;
88
+ sampleBubbleKeys: string[];
89
+ sampleComposerKeys: string[];
90
+ }
91
+ /**
92
+ * Output of the `self_test` MCP mode. Runs the full parser pipeline and
93
+ * reports health + coverage + invariant checks. overallStatus semantics:
94
+ * PASS — parser ran, invariants held, coverage looks healthy
95
+ * DEGRADED — parser ran but some bubbles lack timestamps / invariants
96
+ * tripped warnings / coverage is partial. Not an error.
97
+ * FAIL — parser could not run (missing DB, missing sqlite3, query
98
+ * failure, or corrupt top-level output). Surfaces as
99
+ * MCP isError:true at the tool boundary.
100
+ */
101
+ export interface CursorSelfTestResult {
102
+ filePath: string;
103
+ dbExists: boolean;
104
+ sqlite3Path: string;
105
+ canQuery: boolean;
106
+ tokenBubbleCount: number;
107
+ composerCount: number;
108
+ sessionsWithTokens: number;
109
+ timestampCoverage: {
110
+ bubblesWithCreatedAt: number;
111
+ totalBubbles: number;
112
+ composersWithCreatedAt: number;
113
+ totalComposers: number;
114
+ };
115
+ invariantChecks: Array<{
116
+ name: string;
117
+ passed: boolean;
118
+ details?: string;
119
+ }>;
120
+ warnings: string[];
121
+ errors: string[];
122
+ overallStatus: "PASS" | "DEGRADED" | "FAIL";
44
123
  }
45
124
  export interface CursorParserError {
46
125
  code: "CURSOR_DB_NOT_FOUND" | "CURSOR_SQLITE3_NOT_FOUND" | "CURSOR_SQLITE_QUERY_FAILED";
47
126
  message: string;
48
127
  }
49
128
  export interface CursorParserResult {
50
- sessions: CursorSessionUsageDryRun[];
129
+ sessions: CursorSessionUsage[];
51
130
  filePath: string;
52
131
  }
53
132
  /**
@@ -64,12 +143,13 @@ export declare function cursorDbExists(): boolean;
64
143
  */
65
144
  declare function isCursorParserError(value: unknown): value is CursorParserError;
66
145
  /**
67
- * Parse Cursor usage from local SQLite. Read-only dry run — does NOT push
68
- * anything to the costcanary backend.
146
+ * Parse Cursor usage from local SQLite. Read-only — does NOT push anything
147
+ * to the costcanary backend.
69
148
  *
70
- * Returns aggregated session data per composer with per-session token totals
71
- * and message counts. Throws CursorParserError on unrecoverable failures
72
- * (missing DB, missing sqlite3 binary, malformed SQLite output).
149
+ * Returns aggregated session data per composer with per-session token totals,
150
+ * message counts, start/end timestamps, and daily usage buckets. Throws
151
+ * CursorParserError on unrecoverable failures (missing DB, missing sqlite3
152
+ * binary, malformed SQLite output).
73
153
  *
74
154
  * Dedup strategy: per composer, keep one entry per (serverBubbleId ?? bubbleId).
75
155
  * On collision, keep the candidate with the larger token total.
@@ -78,9 +158,37 @@ declare function isCursorParserError(value: unknown): value is CursorParserError
78
158
  * the returned `model` field is "mixed". If no model info is present on any
79
159
  * bubble, the field is "unknown".
80
160
  *
81
- * Sort order: total tokens descending. NOT chronological — message timestamps
82
- * are not yet verified for Cursor.
161
+ * Sort order: total tokens descending.
162
+ */
163
+ export declare function parseCursorUsage(): CursorParserResult;
164
+ /**
165
+ * Backward-compat alias. PR1 consumers called this function name; keep it
166
+ * working for one release after the rename.
167
+ */
168
+ export declare const parseCursorUsageDryRun: typeof parseCursorUsage;
169
+ /**
170
+ * Return transparency metadata about the Cursor SQLite: file size, table
171
+ * list, key-prefix histogram, and a small sample of bubble and composer
172
+ * keys with their UUIDs truncated. Powers the `what_we_read` MCP mode so
173
+ * users can see exactly what data CostHawk is reading.
174
+ *
175
+ * Throws CursorParserError on missing DB, missing sqlite3, or query failure.
176
+ */
177
+ export declare function getCursorMeta(): CursorMeta;
178
+ /**
179
+ * Run a full parser health check against the live DB. Reports coverage
180
+ * numbers, validates invariants, and classifies the result as PASS,
181
+ * DEGRADED, or FAIL.
182
+ *
183
+ * - FAIL is reserved for unrecoverable failures (DB missing, sqlite3
184
+ * missing, query error). The MCP tool surfaces FAIL as isError:true.
185
+ * - DEGRADED means the parser ran but flagged warnings — e.g., invariant
186
+ * tolerance exceeded, partial timestamp coverage, unexpected row shapes.
187
+ * - PASS means the parser ran cleanly with full coverage and no warnings.
188
+ *
189
+ * Never throws — catches errors and reports them as FAIL so callers can
190
+ * present the full structured payload to users.
83
191
  */
84
- export declare function parseCursorUsageDryRun(): CursorParserResult;
192
+ export declare function runCursorSelfTest(): CursorSelfTestResult;
85
193
  export { isCursorParserError };
86
194
  //# sourceMappingURL=cursor-parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cursor-parser.d.ts","sourceRoot":"","sources":["../src/cursor-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAOH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAOzD;;;;;;GAMG;AACH,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EACA,qBAAqB,GACrB,0BAA0B,GAC1B,4BAA4B,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,wBAAwB,EAAE,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAmCxC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAUD;;GAEG;AACH,iBAAS,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAYvE;AA+KD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,IAAI,kBAAkB,CAuH3D;AAID,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
1
+ {"version":3,"file":"cursor-parser.d.ts","sourceRoot":"","sources":["../src/cursor-parser.ts"],"names":[],"mappings":"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"}