claude-code-infinite 1.0.9 → 1.0.14

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.
Files changed (136) hide show
  1. package/README.md +230 -37
  2. package/dist/cc-request.d.ts +94 -0
  3. package/dist/cc-request.d.ts.map +1 -0
  4. package/dist/cc-request.js +194 -0
  5. package/dist/cc-request.js.map +1 -0
  6. package/dist/claude-env.d.ts +30 -0
  7. package/dist/claude-env.d.ts.map +1 -0
  8. package/dist/claude-env.js +55 -0
  9. package/dist/claude-env.js.map +1 -0
  10. package/dist/cli-args.d.ts +32 -0
  11. package/dist/cli-args.d.ts.map +1 -0
  12. package/dist/cli-args.js +72 -0
  13. package/dist/cli-args.js.map +1 -0
  14. package/dist/cli-lifecycle.d.ts +17 -0
  15. package/dist/cli-lifecycle.d.ts.map +1 -0
  16. package/dist/cli-lifecycle.js +30 -0
  17. package/dist/cli-lifecycle.js.map +1 -0
  18. package/dist/cli-main.d.ts +15 -0
  19. package/dist/cli-main.d.ts.map +1 -0
  20. package/dist/cli-main.js +464 -0
  21. package/dist/cli-main.js.map +1 -0
  22. package/dist/cli.js +13 -177
  23. package/dist/cli.js.map +1 -1
  24. package/dist/config.d.ts +3 -0
  25. package/dist/config.d.ts.map +1 -1
  26. package/dist/config.js +29 -3
  27. package/dist/config.js.map +1 -1
  28. package/dist/count-tokens.d.ts +78 -0
  29. package/dist/count-tokens.d.ts.map +1 -0
  30. package/dist/count-tokens.js +277 -0
  31. package/dist/count-tokens.js.map +1 -0
  32. package/dist/final-index.d.ts +10 -0
  33. package/dist/final-index.d.ts.map +1 -0
  34. package/dist/final-index.js +37 -0
  35. package/dist/final-index.js.map +1 -0
  36. package/dist/hooks.d.ts +239 -0
  37. package/dist/hooks.d.ts.map +1 -0
  38. package/dist/hooks.js +618 -0
  39. package/dist/hooks.js.map +1 -0
  40. package/dist/index.d.ts +9 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +8 -0
  43. package/dist/index.js.map +1 -1
  44. package/dist/memtree-fetch.d.ts +24 -0
  45. package/dist/memtree-fetch.d.ts.map +1 -0
  46. package/dist/memtree-fetch.js +121 -0
  47. package/dist/memtree-fetch.js.map +1 -0
  48. package/dist/memtree-finder.d.ts +99 -0
  49. package/dist/memtree-finder.d.ts.map +1 -0
  50. package/dist/memtree-finder.js +235 -0
  51. package/dist/memtree-finder.js.map +1 -0
  52. package/dist/memtree-links.d.ts +16 -0
  53. package/dist/memtree-links.d.ts.map +1 -0
  54. package/dist/memtree-links.js +66 -0
  55. package/dist/memtree-links.js.map +1 -0
  56. package/dist/memtree-mcp-config.d.ts +28 -0
  57. package/dist/memtree-mcp-config.d.ts.map +1 -0
  58. package/dist/memtree-mcp-config.js +121 -0
  59. package/dist/memtree-mcp-config.js.map +1 -0
  60. package/dist/memtree-mcp.d.ts +265 -0
  61. package/dist/memtree-mcp.d.ts.map +1 -0
  62. package/dist/memtree-mcp.js +667 -0
  63. package/dist/memtree-mcp.js.map +1 -0
  64. package/dist/memtree-newest.d.ts +74 -0
  65. package/dist/memtree-newest.d.ts.map +1 -0
  66. package/dist/memtree-newest.js +217 -0
  67. package/dist/memtree-newest.js.map +1 -0
  68. package/dist/memtree-tools.d.ts +105 -0
  69. package/dist/memtree-tools.d.ts.map +1 -0
  70. package/dist/memtree-tools.js +408 -0
  71. package/dist/memtree-tools.js.map +1 -0
  72. package/dist/memtree.d.ts +398 -0
  73. package/dist/memtree.d.ts.map +1 -0
  74. package/dist/memtree.js +1231 -0
  75. package/dist/memtree.js.map +1 -0
  76. package/dist/notices.d.ts +177 -0
  77. package/dist/notices.d.ts.map +1 -0
  78. package/dist/notices.js +469 -0
  79. package/dist/notices.js.map +1 -0
  80. package/dist/payment-gate.d.ts +53 -0
  81. package/dist/payment-gate.d.ts.map +1 -0
  82. package/dist/payment-gate.js +97 -0
  83. package/dist/payment-gate.js.map +1 -0
  84. package/dist/project-meta.d.ts +15 -0
  85. package/dist/project-meta.d.ts.map +1 -0
  86. package/dist/project-meta.js +80 -0
  87. package/dist/project-meta.js.map +1 -0
  88. package/dist/prompt-accounting.d.ts +12 -0
  89. package/dist/prompt-accounting.d.ts.map +1 -0
  90. package/dist/prompt-accounting.js +70 -0
  91. package/dist/prompt-accounting.js.map +1 -0
  92. package/dist/proxy.d.ts +197 -0
  93. package/dist/proxy.d.ts.map +1 -0
  94. package/dist/proxy.js +4247 -0
  95. package/dist/proxy.js.map +1 -0
  96. package/dist/reqlog.d.ts +419 -0
  97. package/dist/reqlog.d.ts.map +1 -0
  98. package/dist/reqlog.js +146 -0
  99. package/dist/reqlog.js.map +1 -0
  100. package/dist/request-sizing.d.ts +52 -0
  101. package/dist/request-sizing.d.ts.map +1 -0
  102. package/dist/request-sizing.js +92 -0
  103. package/dist/request-sizing.js.map +1 -0
  104. package/dist/resume-repair-file.d.ts +17 -0
  105. package/dist/resume-repair-file.d.ts.map +1 -0
  106. package/dist/resume-repair-file.js +100 -0
  107. package/dist/resume-repair-file.js.map +1 -0
  108. package/dist/resume-repair.d.ts +27 -0
  109. package/dist/resume-repair.d.ts.map +1 -0
  110. package/dist/resume-repair.js +198 -0
  111. package/dist/resume-repair.js.map +1 -0
  112. package/dist/route-cache.d.ts +5 -0
  113. package/dist/route-cache.d.ts.map +1 -0
  114. package/dist/route-cache.js +63 -0
  115. package/dist/route-cache.js.map +1 -0
  116. package/dist/route-fallback.d.ts +29 -0
  117. package/dist/route-fallback.d.ts.map +1 -0
  118. package/dist/route-fallback.js +49 -0
  119. package/dist/route-fallback.js.map +1 -0
  120. package/dist/route-identity.d.ts +6 -0
  121. package/dist/route-identity.d.ts.map +1 -0
  122. package/dist/route-identity.js +100 -0
  123. package/dist/route-identity.js.map +1 -0
  124. package/dist/transcript-usage.d.ts +57 -0
  125. package/dist/transcript-usage.d.ts.map +1 -0
  126. package/dist/transcript-usage.js +570 -0
  127. package/dist/transcript-usage.js.map +1 -0
  128. package/dist/turns.d.ts +73 -0
  129. package/dist/turns.d.ts.map +1 -0
  130. package/dist/turns.js +298 -0
  131. package/dist/turns.js.map +1 -0
  132. package/dist/update-check.d.ts +38 -0
  133. package/dist/update-check.d.ts.map +1 -0
  134. package/dist/update-check.js +67 -0
  135. package/dist/update-check.js.map +1 -0
  136. package/package.json +6 -2
@@ -0,0 +1,419 @@
1
+ /**
2
+ * Always-on request/timing log (append-only JSONL) for post-hoc incident
3
+ * reconstruction — when a turn stalls for minutes we want a client-side
4
+ * record without asking the user to relaunch with --debug.
5
+ *
6
+ * Hard constraints:
7
+ * - NEVER throw and NEVER block the proxy path: every filesystem touch is
8
+ * wrapped in try/catch and writes are fire-and-forget (async appendFile
9
+ * with an ignored error callback). A broken log path degrades to silence.
10
+ * - Append-only single file at ~/.claude-code-infinite/logs/requests.jsonl,
11
+ * with one rotation slot: if the file exceeds ~20MB at proxy startup it is
12
+ * renamed to requests.jsonl.1 (overwriting any previous .1).
13
+ *
14
+ * One JSON object per line. `ts` (ISO 8601) and `pid` (the ccc process, which
15
+ * tells apart concurrent ccc sessions sharing this file) are stamped here so
16
+ * callers only supply event fields. Token counts under `approxInputTokens` are a rough
17
+ * bytes/4 chars→tokens proxy, NOT real tokenizer output — exact usage, when
18
+ * the response format lets us extract it cheaply, lands under `usage`.
19
+ */
20
+ import type { ClaudeCodeRequestInfo, TranscriptShape } from "./cc-request.js";
21
+ /** Rough bytes→tokens estimate (bytes/4). Label the result approximate. */
22
+ export declare function approxTokensFromBytes(bytes: number): number;
23
+ export declare function defaultLogPath(): string;
24
+ export type TurnType = "first-user" | "tool"
25
+ /** A tool turn sent on its lane's memory route (compressed prefix + suffix). */
26
+ | "tool-memory"
27
+ /**
28
+ * A main-thread tool turn sent on the session's stable prefix directly (no
29
+ * lane route to ride, e.g. after a tool-turn compaction made while a typed
30
+ * prompt was pending): the prefix bytes, then every later message verbatim.
31
+ */
32
+ | "tool-prefix"
33
+ /**
34
+ * A tool turn that compressed: its estimated size reached the budget (see
35
+ * `compaction`, reason "budget"), or its lane's route no longer fitted the
36
+ * context window. Validated compressed bytes were sent to Anthropic; on the
37
+ * main thread the result is also the session's new stable prefix.
38
+ */
39
+ | "tool-recompressed" | "followup-compressed"
40
+ /**
41
+ * A main-thread human turn sent on the session's stable compressed prefix:
42
+ * the prefix bytes stored at the last compaction, then every message after
43
+ * the part it covers, verbatim. No compress call (the history still goes
44
+ * to MemTree as a background index_only call), and the prefix is a prompt
45
+ * cache read. See `compaction` for the size check that allowed it.
46
+ */
47
+ | "followup-prefix" | "followup-noop"
48
+ /** Indexed response that carried no prior conversation; history forwarded. */
49
+ | "followup-empty-memory"
50
+ /**
51
+ * Compressed result carried no server `flattened_messages` (pre-flatten
52
+ * server or malformed field); history forwarded.
53
+ */
54
+ | "followup-no-flatten" | "followup-degraded"
55
+ /**
56
+ * A fork of the main conversation (the away recap) rode the main thread's
57
+ * last compressed prefix: same bytes the main thread sent, so Anthropic's
58
+ * prompt cache hits, and no MemTree call was made.
59
+ */
60
+ | "fork-memory"
61
+ /**
62
+ * Claude Code's own side request (the security monitor): forwarded
63
+ * untouched, no MemTree call, main-thread route state left alone.
64
+ */
65
+ | "side-request"
66
+ /**
67
+ * Not from Claude Code (no X-Claude-Code-Session-Id): another program that
68
+ * inherited ccc's ANTHROPIC_BASE_URL. Forwarded untouched, no MemTree.
69
+ */
70
+ | "foreign" | "followup-client-closed" | "unparseable";
71
+ /** Real token usage parsed from Anthropic's response, when available. */
72
+ export interface UsageRecord {
73
+ input_tokens?: number;
74
+ cache_read_input_tokens?: number;
75
+ cache_creation_input_tokens?: number;
76
+ output_tokens?: number;
77
+ }
78
+ /**
79
+ * One /v1/messages request through the proxy. Built up mutably as the request
80
+ * flows through the forward path (the forwarders fill in upstream timing) and
81
+ * written once when the response finishes. Fields that a given path can't
82
+ * derive cheaply are simply omitted.
83
+ */
84
+ /** One actual Count Tokens HTTP attempt; never includes body, credentials or token counts. */
85
+ export interface CountTokensRecord {
86
+ statusClass: string;
87
+ ms: number;
88
+ }
89
+ export interface MessagesRecord {
90
+ kind: "messages";
91
+ turnType: TurnType;
92
+ /** Body bytes as received from Claude Code. */
93
+ requestBytes: number;
94
+ model?: string;
95
+ stream?: boolean;
96
+ /** For a foreign request: the opening of its User-Agent, to name the program. */
97
+ userAgent?: string;
98
+ /**
99
+ * What Claude Code's billing header says about this request (see
100
+ * cc-request.ts). Logged to measure which request kinds lack
101
+ * `cc_turn_origin` before any of them are kept away from MemTree.
102
+ */
103
+ client?: ClaudeCodeRequestInfo;
104
+ /**
105
+ * For a request shaped like Claude Code's security monitor (billing header,
106
+ * no turn origin, no tools): whether its `<transcript>` block has the known
107
+ * format. `ok: false` means it was handled as an ordinary request, and is
108
+ * the signal that Claude Code changed the format.
109
+ */
110
+ transcript?: TranscriptShape;
111
+ /** Body bytes actually sent to Anthropic (after notice strip / compression). */
112
+ forwardedBytes?: number;
113
+ /** forwardedBytes/4 — rough chars→tokens proxy, not a tokenizer count. */
114
+ approxInputTokens?: number;
115
+ /** Present when a blocking MemTree compress was attempted for this turn. */
116
+ compress?: {
117
+ /** Overall wall time of the compress step (all concurrent legs). */
118
+ ms: number;
119
+ /**
120
+ * The compress call returned a result. NOT a
121
+ * "forwarded compressed history" flag: the proxy can still discard the
122
+ * result afterwards (no-op response, or indexed-but-empty memory) and
123
+ * forward the full history — `turnType` (followup-noop /
124
+ * followup-empty-memory vs followup-compressed) and `history.usable`
125
+ * record what actually happened.
126
+ */
127
+ ok: boolean;
128
+ /**
129
+ * The compress call consumed (roughly) the whole compress budget and
130
+ * returned nothing.
131
+ */
132
+ timedOut: boolean;
133
+ };
134
+ /**
135
+ * How much conversation the compressed response actually carried. Recorded
136
+ * separately from `compress.ok` because a fully indexed response can still
137
+ * return an empty conversation; this is the field that shows it.
138
+ */
139
+ history?: {
140
+ retainedChars: number;
141
+ priorHistoryChars: number;
142
+ usable: boolean;
143
+ };
144
+ /**
145
+ * Which memory-route lane this request keys to: the away-summary side
146
+ * channel, a request carrying agent attribution, or the main thread. This
147
+ * is what splits verbatim `tool` rows into main-miss vs subagent traffic
148
+ * when evaluating recovery behaviour after the fact.
149
+ */
150
+ routeLane?: "main" | "away" | "agent";
151
+ /**
152
+ * Tool turns that missed their lane's route: "missing" (empty lane),
153
+ * "rejected" (a route was present in this lane but unusable for this
154
+ * request), or "replay" (the body was an exact replay of the request that
155
+ * installed the route — a client retry after a pre-flush socket death —
156
+ * forwarded verbatim with the route retained and no recovery attempt).
157
+ * "rejected" covers every OTHER reason memoryRoutedToolBody refuses:
158
+ * a changed system/prefix hash, a conversation that shrank below the
159
+ * stored prefix, an unexpected tool suffix shape, or an assembled route
160
+ * estimated to exceed the destination context window. (An epoch-stale
161
+ * route never reaches rejection: getMemoryRoute drops it on lookup, so
162
+ * that case logs "missing".) "superseded": a main-thread route built on
163
+ * an older stable prefix than the session's current one (a later tool-turn
164
+ * compaction replaced the prefix without owning the route); dropped, and
165
+ * the turn rides the current prefix instead.
166
+ * Absent means there was no applicable miss — hits and away-summary turns
167
+ * never emit it. "none" is deliberately not a value.
168
+ */
169
+ routeMiss?: "missing" | "rejected" | "replay" | "superseded";
170
+ /**
171
+ * The request's last conversation message was neither a user prompt nor a
172
+ * tool result: e.g. a background task notification, which Claude Code
173
+ * sends as a trailing role=system block after the assistant's last reply.
174
+ * Handled like a tool turn (stable-prefix ride, budget compaction) without
175
+ * consulting the lane's route. Absent on every other request.
176
+ */
177
+ continuation?: true;
178
+ /**
179
+ * Why a fork of the main conversation (away recap) did not ride the main
180
+ * thread's last prefix and fell back to its own compression; absent when it
181
+ * rode (turnType "fork-memory") or was not a fork.
182
+ */
183
+ forkMiss?: "no-route" | "session" | "system" | "prefix" | "too-large";
184
+ /**
185
+ * Stable-prefix (edge) compaction: the budget the turn was measured
186
+ * against, the size estimate, and, when it compacted, why. Recorded on
187
+ * main-thread human turns and on every lane's tool turns (a tool turn under
188
+ * the budget forwards with no compress call; at the budget it compresses
189
+ * once, reason "budget"). Absent on other requests, and on tool turns under
190
+ * the CCC_TOOL_ROUTE_RECOVERY=0 kill switch.
191
+ */
192
+ compaction?: CompactionRecord;
193
+ /** Every actual counting attempt, in order; cached results add no entry. */
194
+ countTokens?: CountTokensRecord[];
195
+ /** One bounded compression after an assembled ride was counted above the soft budget. */
196
+ lateCountRecovery?: {
197
+ outcome: "compressed" | "forwarded" | "refused";
198
+ };
199
+ /** Searchable flag explaining a forward above the advisory compression budget. */
200
+ overBudgetForward?: {
201
+ reason: "exact-count-fits-window" | "estimate-fits-window" | "bytes-fallback-fits-window";
202
+ };
203
+ /**
204
+ * Outcome of a tool-turn compaction attempt (a tool turn whose estimated
205
+ * size reached the budget), or why none was made although one was due.
206
+ * Absent on tool turns under the budget: they forward with no attempt.
207
+ */
208
+ routeRecovery?: {
209
+ /**
210
+ * Serialized non-system conversation bytes, measured once per attempt.
211
+ * Absent when no attempt was made (kill switch, cooldown, in-flight,
212
+ * backoff, awaiting-index).
213
+ */
214
+ conversationBytes?: number;
215
+ outcome: "compressed" | "failed"
216
+ /** The server returned the conversation uncompressed (index warming). */
217
+ | "noop" | "unusable"
218
+ /**
219
+ * Compressed result carried no server `flattened_messages`
220
+ * (pre-flatten server or malformed field). Forwarded the original.
221
+ */
222
+ | "no-flatten" | "no-gain" | "client-closed"
223
+ /**
224
+ * CCC_TOOL_ROUTE_RECOVERY=0: a route miss that got no size check and
225
+ * no attempt.
226
+ */
227
+ | "disabled"
228
+ /** A recent attempt returned null; the blocking wait was skipped. */
229
+ | "cooldown"
230
+ /** Another attempt for this lane is still in flight. */
231
+ | "in-flight"
232
+ /**
233
+ * This lane's last attempt this human turn produced no prefix to ride
234
+ * (no-op, no gain, failure, a forward that installed nothing, or a
235
+ * result still over the window); the next one waits until the
236
+ * conversation has grown by a twentieth of the budget, so a lane that
237
+ * cannot compress does not pay a blocking call on every tool turn.
238
+ */
239
+ | "backoff"
240
+ /**
241
+ * This lane's last attempt found no finished tree for the conversation
242
+ * (no-op, nothing indexed), so a compress call could only pass it
243
+ * through again. None is made until that attempt's MemTree page
244
+ * reports built (checked in the background, one fetch at a time).
245
+ */
246
+ | "awaiting-index"
247
+ /**
248
+ * The compressed result could not be serialized (e.g. V8 string-length
249
+ * limit on a multi-megabyte body). Forwarded the original.
250
+ */
251
+ | "build-failed";
252
+ /**
253
+ * Main-thread compactions with a session: whether the result became the
254
+ * session's stable prefix ("not-installed": the forward never completed,
255
+ * or a newer prefix replaced the one this compaction started from).
256
+ */
257
+ prefix?: "installed" | "not-installed";
258
+ /** A no-op that found no tree: the lane now waits for one ("awaiting-index"). */
259
+ awaitingIndex?: boolean;
260
+ /** Route candidate fate; only "compressed" outcomes carry it. */
261
+ install?: "installed" | "stale" | "prompt-pending" | "no-session"
262
+ /**
263
+ * The recovered forward never reached protocol-complete and the
264
+ * downstream client had NOT aborted. Typically an upstream 5xx/529
265
+ * or a truncated upstream stream, but any non-2xx of the compressed
266
+ * forward lands here — including a plain 4xx, which is deliberately
267
+ * non-arming for the fuse — as does proxy-shutdown teardown of an
268
+ * in-flight recovery. Lifts the lane's backoff so the identical-body
269
+ * retry compresses again.
270
+ */
271
+ | "upstream-failed"
272
+ /**
273
+ * The upstream served the turn to protocol-complete but route
274
+ * bookkeeping threw at an activation attempt (the protocol-complete
275
+ * attempt, the delivered-settle retry, or both), so no route exists.
276
+ * Releases the lane's reservation but keeps its backoff. In the common
277
+ * sub-case the client got its complete answer — a fast-tool abort
278
+ * after message_stop lands here, not in "client-aborted" — so no
279
+ * identical-body retry is coming, and the throw is not
280
+ * upstream-health evidence. Rarer closes land here too: a socket
281
+ * that dies before the accepted message_stop bytes flush (which
282
+ * DOES retry the identical body) and an upstream-owned error
283
+ * arriving after the data chunk that carried message_stop. A retry
284
+ * finds its lane backed off and forwards uncompressed until the
285
+ * history grows past the retry size or the next human turn — a
286
+ * bounded degradation accepted because the closes are
287
+ * indistinguishable at settle time and lifting the backoff would
288
+ * fund one blocking recompress per tool turn under a deterministic
289
+ * activation throw.
290
+ */
291
+ | "activation-error"
292
+ /**
293
+ * The downstream client aborted mid-stream before protocol-complete.
294
+ * Lifts the backoff exactly like upstream-failed (no route exists
295
+ * and the client's identical-body retry is imminent) but is split
296
+ * out so attempt-rate tripwires can tell client behavior from
297
+ * upstream health.
298
+ */
299
+ | "client-aborted";
300
+ };
301
+ upstreamStatus?: number;
302
+ /** The downstream client aborted before the response finished. */
303
+ clientAborted?: true;
304
+ /** Forward start → first response byte from Anthropic. */
305
+ ttfbMs?: number;
306
+ /** Forward start → first upstream content_block_delta (SSE only). */
307
+ firstContentMs?: number;
308
+ /** Request received → response fully sent to Claude Code. */
309
+ totalMs?: number;
310
+ /** Legacy schema field; live response fabrication is disabled. */
311
+ preludeFired?: boolean;
312
+ usage?: UsageRecord;
313
+ }
314
+ /** Why a turn compressed instead of riding its prefix (or passing through). */
315
+ export type RecompressReason =
316
+ /** The prefix plus the turns after it reached the budget. */
317
+ "budget"
318
+ /** The messages the prefix covers changed: rewind, edit, fork, /clear. */
319
+ | "prefix-mismatch"
320
+ /** `/memtree-compact [N]` asked for a compaction now. */
321
+ | "manual"
322
+ /** The session's target or context window changed since the prefix was built. */
323
+ | "target-change";
324
+ export interface CompactionRecord {
325
+ /** "off" after `/memtree-compact off`; "explicit" for an N or CCC_COMPACT_TARGET. */
326
+ mode: "auto" | "explicit" | "off";
327
+ /** The whole-request budget the turn was measured against. */
328
+ budgetTokens: number;
329
+ /**
330
+ * Where the budget came from: CCC_BUDGET_TOKENS ("override"), the
331
+ * server's `model_budget_tokens` ("server"), or the model's context window
332
+ * times FALLBACK_BUDGET_WINDOW_RATIO until the server reports one.
333
+ */
334
+ budgetSource: "override" | "server" | "window-ratio";
335
+ /** What a compaction on this turn aims at (budget/2, or the explicit N). */
336
+ targetTokens?: number;
337
+ /** Sent as compression_threshold_tokens (servers that report a budget). */
338
+ thresholdTokens?: number;
339
+ /** Size estimate of what would be sent (prefix ride or full history). */
340
+ estimatedTokens?: number;
341
+ /** Bytes of the request underlying estimatedTokens, including a reused prefix. */
342
+ estimatedBytes?: number;
343
+ /**
344
+ * "reported": Anthropic's input+cache_read+cache_creation for the previous
345
+ * request of the same shape, plus bytes/4 for what was added since.
346
+ * "bytes": bytes/4 of the whole body (no earlier usage to start from).
347
+ */
348
+ sizeSource?: "reported" | "bytes";
349
+ /** Set when the turn compressed (or tried to) for one of these reasons. */
350
+ reason?: RecompressReason;
351
+ /** For "prefix-mismatch": which check failed. */
352
+ prefixMiss?: "session" | "system" | "prefix";
353
+ /**
354
+ * A compaction was attempted but did not produce a new prefix (MemTree
355
+ * down, no-op, unusable) and the turn rode the old prefix instead.
356
+ */
357
+ keptPrefix?: true;
358
+ }
359
+ /** One MemTree API call (blocking compress or background index). */
360
+ export interface MemtreeRecord {
361
+ kind: "memtree";
362
+ indexOnly: boolean;
363
+ ms: number;
364
+ ok: boolean;
365
+ /** HTTP status; absent when the call died before a response (network/timeout). */
366
+ status?: number;
367
+ requestBytes: number;
368
+ /** Model sent for server-side budget resolution (compression calls only). */
369
+ model?: string;
370
+ /**
371
+ * Response diagnostics, present on successful calls that reported usage.
372
+ * `indexedTokens` (cached_tokens) is the prompt coverage of the index and
373
+ * drives the success notice: flat coverage across turns means MemTree
374
+ * indexed nothing new. `memoryChars` moves independently of it because the
375
+ * index is unfolded per question.
376
+ */
377
+ indexedTokens?: number;
378
+ rawPromptTokens?: number;
379
+ memoryChars?: number;
380
+ /** The per-request MemTree page the server stamped on the response, if any. */
381
+ memtreeUrl?: string;
382
+ /** The completed index the turn was compressed against, if the server said. */
383
+ memtreeIndex?: string;
384
+ /** The server's model budget (`model_budget_tokens`), when it reported one. */
385
+ modelBudgetTokens?: number;
386
+ }
387
+ /** A display-only notice was atomically claimed by one Claude Code hook. */
388
+ export interface NoticeRecord {
389
+ kind: "notice";
390
+ event: "claimed";
391
+ via: "MessageDisplay" | "Stop";
392
+ }
393
+ export type RequestRecord = MessagesRecord | MemtreeRecord | NoticeRecord;
394
+ /** Structural logging seam retained for embedders and focused tests. */
395
+ export interface RequestLogSink {
396
+ log(record: RequestRecord): void;
397
+ }
398
+ export declare class RequestLogger implements RequestLogSink {
399
+ readonly path: string;
400
+ private readonly pendingWrites;
401
+ constructor(filePath?: string);
402
+ /** Fire-and-forget append of one JSONL line. Never throws, never blocks. */
403
+ log(record: RequestRecord): void;
404
+ /**
405
+ * Shutdown-only durability seam. Wait for already-scheduled appends without
406
+ * making normal proxy logging synchronous; false means the bounded wait
407
+ * expired. Callers should stop request producers before invoking this.
408
+ */
409
+ flush(timeoutMs?: number): Promise<boolean>;
410
+ }
411
+ /**
412
+ * Merge token usage out of a parsed SSE event into a record. Anthropic sends
413
+ * input/cache counts on message_start and the final output count (plus
414
+ * occasionally refreshed input counts) on message_delta.
415
+ */
416
+ export declare function mergeUsageFromSseEvent(data: any, rec: MessagesRecord): void;
417
+ /** Merge usage from a non-streaming /v1/messages JSON response body. */
418
+ export declare function mergeUsageFromJsonBody(body: Buffer, rec: MessagesRecord): void;
419
+ //# sourceMappingURL=reqlog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reqlog.d.ts","sourceRoot":"","sources":["../src/reqlog.ts"],"names":[],"mappings":"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"}
package/dist/reqlog.js ADDED
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Always-on request/timing log (append-only JSONL) for post-hoc incident
3
+ * reconstruction — when a turn stalls for minutes we want a client-side
4
+ * record without asking the user to relaunch with --debug.
5
+ *
6
+ * Hard constraints:
7
+ * - NEVER throw and NEVER block the proxy path: every filesystem touch is
8
+ * wrapped in try/catch and writes are fire-and-forget (async appendFile
9
+ * with an ignored error callback). A broken log path degrades to silence.
10
+ * - Append-only single file at ~/.claude-code-infinite/logs/requests.jsonl,
11
+ * with one rotation slot: if the file exceeds ~20MB at proxy startup it is
12
+ * renamed to requests.jsonl.1 (overwriting any previous .1).
13
+ *
14
+ * One JSON object per line. `ts` (ISO 8601) and `pid` (the ccc process, which
15
+ * tells apart concurrent ccc sessions sharing this file) are stamped here so
16
+ * callers only supply event fields. Token counts under `approxInputTokens` are a rough
17
+ * bytes/4 chars→tokens proxy, NOT real tokenizer output — exact usage, when
18
+ * the response format lets us extract it cheaply, lands under `usage`.
19
+ */
20
+ import { appendFile, mkdirSync, renameSync, statSync } from "node:fs";
21
+ import { dirname, join } from "node:path";
22
+ import { getConfigDir } from "./config.js";
23
+ const MAX_LOG_BYTES = 20 * 1024 * 1024; // rotate above ~20MB at startup
24
+ const APPROX_CHARS_PER_TOKEN = 4;
25
+ /** Rough bytes→tokens estimate (bytes/4). Label the result approximate. */
26
+ export function approxTokensFromBytes(bytes) {
27
+ return Math.round(bytes / APPROX_CHARS_PER_TOKEN);
28
+ }
29
+ export function defaultLogPath() {
30
+ return join(getConfigDir(), "logs", "requests.jsonl");
31
+ }
32
+ export class RequestLogger {
33
+ path;
34
+ pendingWrites = new Set();
35
+ constructor(filePath) {
36
+ this.path = filePath ?? defaultLogPath();
37
+ try {
38
+ mkdirSync(dirname(this.path), { recursive: true });
39
+ rotateIfLarge(this.path);
40
+ }
41
+ catch {
42
+ // Unwritable/odd log location: run silent, never break startup.
43
+ }
44
+ }
45
+ /** Fire-and-forget append of one JSONL line. Never throws, never blocks. */
46
+ log(record) {
47
+ try {
48
+ const line = JSON.stringify({ ts: new Date().toISOString(), pid: process.pid, ...record }) + "\n";
49
+ let finish;
50
+ const pending = new Promise((resolve) => {
51
+ finish = resolve;
52
+ });
53
+ const settle = () => {
54
+ this.pendingWrites.delete(pending);
55
+ finish();
56
+ };
57
+ this.pendingWrites.add(pending);
58
+ try {
59
+ appendFile(this.path, line, settle);
60
+ }
61
+ catch {
62
+ settle();
63
+ }
64
+ }
65
+ catch {
66
+ // Serialization/scheduling failure — same policy.
67
+ }
68
+ }
69
+ /**
70
+ * Shutdown-only durability seam. Wait for already-scheduled appends without
71
+ * making normal proxy logging synchronous; false means the bounded wait
72
+ * expired. Callers should stop request producers before invoking this.
73
+ */
74
+ async flush(timeoutMs = 2_000) {
75
+ const boundedMs = Number.isFinite(timeoutMs) && timeoutMs >= 0
76
+ ? Math.floor(timeoutMs)
77
+ : 2_000;
78
+ const deadline = Date.now() + boundedMs;
79
+ while (this.pendingWrites.size > 0) {
80
+ const remaining = deadline - Date.now();
81
+ if (remaining <= 0)
82
+ return false;
83
+ let timer;
84
+ const completed = await Promise.race([
85
+ Promise.allSettled([...this.pendingWrites]).then(() => true),
86
+ new Promise((resolve) => {
87
+ timer = setTimeout(() => resolve(false), remaining);
88
+ }),
89
+ ]);
90
+ if (timer)
91
+ clearTimeout(timer);
92
+ if (!completed)
93
+ return false;
94
+ }
95
+ return true;
96
+ }
97
+ }
98
+ /**
99
+ * Merge token usage out of a parsed SSE event into a record. Anthropic sends
100
+ * input/cache counts on message_start and the final output count (plus
101
+ * occasionally refreshed input counts) on message_delta.
102
+ */
103
+ export function mergeUsageFromSseEvent(data, rec) {
104
+ const type = data?.type;
105
+ if (type === "message_start")
106
+ mergeUsage(data?.message?.usage, rec);
107
+ else if (type === "message_delta")
108
+ mergeUsage(data?.usage, rec);
109
+ }
110
+ /** Merge usage from a non-streaming /v1/messages JSON response body. */
111
+ export function mergeUsageFromJsonBody(body, rec) {
112
+ try {
113
+ mergeUsage(JSON.parse(body.toString("utf-8"))?.usage, rec);
114
+ }
115
+ catch {
116
+ // Not the shape we expected — usage just stays absent.
117
+ }
118
+ }
119
+ const USAGE_FIELDS = [
120
+ "input_tokens",
121
+ "cache_read_input_tokens",
122
+ "cache_creation_input_tokens",
123
+ "output_tokens",
124
+ ];
125
+ function mergeUsage(usage, rec) {
126
+ if (!usage || typeof usage !== "object")
127
+ return;
128
+ for (const field of USAGE_FIELDS) {
129
+ const value = usage[field];
130
+ if (typeof value !== "number")
131
+ continue;
132
+ rec.usage = rec.usage ?? {};
133
+ rec.usage[field] = value;
134
+ }
135
+ }
136
+ /** Startup size protection: one rotation slot, old .1 is overwritten. */
137
+ function rotateIfLarge(path) {
138
+ try {
139
+ if (statSync(path).size > MAX_LOG_BYTES)
140
+ renameSync(path, `${path}.1`);
141
+ }
142
+ catch {
143
+ // Missing file (fresh install) or unreadable — nothing to rotate.
144
+ }
145
+ }
146
+ //# sourceMappingURL=reqlog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reqlog.js","sourceRoot":"","sources":["../src/reqlog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import { type CountTokensSession, type CountTokensResult, type SizeSample } from "./count-tokens.js";
2
+ export type CandidateKind = "original" | "ride" | "replacement";
3
+ export interface SizingLimits {
4
+ budget: number;
5
+ context: number;
6
+ output: number;
7
+ }
8
+ export interface PlannedSize {
9
+ tokens: number;
10
+ source: "reported" | "bytes";
11
+ }
12
+ export interface CandidateMeasurement {
13
+ tokens: number;
14
+ source: "exact" | "reported" | "bytes";
15
+ /** Advisory estimate retained for compression and stale-estimate diagnostics. */
16
+ estimatedTokens: number;
17
+ count?: CountTokensResult;
18
+ }
19
+ export interface RequestCandidate {
20
+ readonly key: string;
21
+ /** Owned copy: callers must not mutate these bytes. */
22
+ readonly body: Buffer;
23
+ readonly kind: CandidateKind;
24
+ readonly sample?: Readonly<SizeSample>;
25
+ readonly estimate: Readonly<PlannedSize>;
26
+ }
27
+ export declare class RequestSizing {
28
+ private readonly counts;
29
+ private readonly enabled;
30
+ private readonly candidates;
31
+ private readonly excludedFallbacks;
32
+ constructor(counts: CountTokensSession, enabled: boolean);
33
+ /** The first registration fixes both bytes and learned sample for this request. */
34
+ register(body: Buffer, kind: CandidateKind, sample?: SizeSample): RequestCandidate;
35
+ /** Eligibility belongs to route planning, not to a later byte-size guess. */
36
+ excludeFallback(candidate: RequestCandidate): void;
37
+ /** Planning and metadata consult the same exact count, else the snapshotted estimate. */
38
+ plan(candidate: RequestCandidate): PlannedSize;
39
+ clientInputTokens(candidate: RequestCandidate): number | undefined;
40
+ /** Count only when a size decision needs it; all views reuse the same result. */
41
+ measure(candidate: RequestCandidate, limits: SizingLimits, phase?: "plan" | "delivery"): Promise<CandidateMeasurement>;
42
+ /** The preferred body gets first chance; only validated registered candidates
43
+ * are considered afterward. Exact overflow never degrades to a byte guess. */
44
+ select(preferred: RequestCandidate, limits: SizingLimits): Promise<{
45
+ candidate: RequestCandidate;
46
+ measurement: CandidateMeasurement;
47
+ } | undefined>;
48
+ }
49
+ export declare function fitsNative(input: number, limits: SizingLimits): boolean;
50
+ /** One shared estimator for planner telemetry and request-local snapshots. */
51
+ export declare function estimateRequestTokens(sample: SizeSample | undefined, bytes: number): PlannedSize;
52
+ //# sourceMappingURL=request-sizing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-sizing.d.ts","sourceRoot":"","sources":["../src/request-sizing.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG9F,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,MAAM,GAAG,aAAa,CAAC;AAChE,MAAM,WAAW,YAAY;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACjF,MAAM,WAAW,WAAW;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAA;CAAE;AAC7E,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;IACvC,iFAAiF;IACjF,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,iBAAiB,CAAC;CAC3B;AACD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;CAC1C;AAED,qBAAa,aAAa;IAIZ,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAsB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHjF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuC;IAClE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA+B;gBAEpC,MAAM,EAAE,kBAAkB,EAAmB,OAAO,EAAE,OAAO;IAE1F,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,gBAAgB;IAWlF,6EAA6E;IAC7E,eAAe,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI;IAIlD,yFAAyF;IACzF,IAAI,CAAC,SAAS,EAAE,gBAAgB,GAAG,WAAW;IAK9C,iBAAiB,CAAC,SAAS,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS;IAKlE,iFAAiF;IAC3E,OAAO,CAAC,SAAS,EAAE,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAC7D,KAAK,GAAE,MAAM,GAAG,UAAuB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAkBzE;kFAC8E;IACxE,MAAM,CAAC,SAAS,EAAE,gBAAgB,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC;QACvE,SAAS,EAAE,gBAAgB,CAAC;QAAC,WAAW,EAAE,oBAAoB,CAAC;KAChE,GAAG,SAAS,CAAC;CAef;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAGvE;AAED,8EAA8E;AAC9E,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAOhG"}