@songsid/agend 2.2.0-alpha.1 → 2.2.0-alpha.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.
Files changed (150) hide show
  1. package/README.md +2 -0
  2. package/README.zh-TW.md +2 -0
  3. package/dist/agent-cli.js +6 -1
  4. package/dist/agent-cli.js.map +1 -1
  5. package/dist/agent-endpoint.js +68 -12
  6. package/dist/agent-endpoint.js.map +1 -1
  7. package/dist/agent-token-header.d.ts +13 -0
  8. package/dist/agent-token-header.js +16 -0
  9. package/dist/agent-token-header.js.map +1 -0
  10. package/dist/backend/antigravity.js +4 -3
  11. package/dist/backend/antigravity.js.map +1 -1
  12. package/dist/backend/grok.js +1 -1
  13. package/dist/backend/grok.js.map +1 -1
  14. package/dist/backend/kiro.d.ts +7 -0
  15. package/dist/backend/kiro.js +82 -33
  16. package/dist/backend/kiro.js.map +1 -1
  17. package/dist/backend/muse.js +1 -1
  18. package/dist/backend/muse.js.map +1 -1
  19. package/dist/backend/types.d.ts +6 -0
  20. package/dist/backend/types.js.map +1 -1
  21. package/dist/cache-analysis.d.ts +92 -0
  22. package/dist/cache-analysis.js +147 -0
  23. package/dist/cache-analysis.js.map +1 -0
  24. package/dist/cache-ledger.d.ts +108 -0
  25. package/dist/cache-ledger.js +230 -0
  26. package/dist/cache-ledger.js.map +1 -0
  27. package/dist/cache-prices.d.ts +32 -0
  28. package/dist/cache-prices.js +73 -0
  29. package/dist/cache-prices.js.map +1 -0
  30. package/dist/cache-scan.d.ts +39 -0
  31. package/dist/cache-scan.js +115 -0
  32. package/dist/cache-scan.js.map +1 -0
  33. package/dist/cache-service.d.ts +115 -0
  34. package/dist/cache-service.js +417 -0
  35. package/dist/cache-service.js.map +1 -0
  36. package/dist/cli-entry.d.ts +19 -0
  37. package/dist/cli-entry.js +36 -0
  38. package/dist/cli-entry.js.map +1 -0
  39. package/dist/cli.js +251 -24
  40. package/dist/cli.js.map +1 -1
  41. package/dist/daemon.d.ts +6 -0
  42. package/dist/daemon.js +90 -37
  43. package/dist/daemon.js.map +1 -1
  44. package/dist/event-log.d.ts +4 -1
  45. package/dist/event-log.js +6 -3
  46. package/dist/event-log.js.map +1 -1
  47. package/dist/fleet-control-audit.d.ts +5 -0
  48. package/dist/fleet-control-audit.js +7 -0
  49. package/dist/fleet-control-audit.js.map +1 -1
  50. package/dist/fleet-level-config.js +22 -0
  51. package/dist/fleet-level-config.js.map +1 -1
  52. package/dist/fleet-manager.d.ts +46 -0
  53. package/dist/fleet-manager.js +287 -40
  54. package/dist/fleet-manager.js.map +1 -1
  55. package/dist/full-restart.js +2 -3
  56. package/dist/full-restart.js.map +1 -1
  57. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +3 -1
  58. package/dist/install-lock.d.ts +49 -0
  59. package/dist/install-lock.js +200 -0
  60. package/dist/install-lock.js.map +1 -0
  61. package/dist/instance-config-impact.js +3 -2
  62. package/dist/instance-config-impact.js.map +1 -1
  63. package/dist/locale.js +14 -0
  64. package/dist/locale.js.map +1 -1
  65. package/dist/outbound-schemas.js +1 -1
  66. package/dist/outbound-schemas.js.map +1 -1
  67. package/dist/package-preimage.d.ts +26 -0
  68. package/dist/package-preimage.js +96 -0
  69. package/dist/package-preimage.js.map +1 -0
  70. package/dist/public-web-gateway.js +2 -2
  71. package/dist/public-web-gateway.js.map +1 -1
  72. package/dist/quickstart.js +10 -4
  73. package/dist/quickstart.js.map +1 -1
  74. package/dist/restart-guard.d.ts +52 -0
  75. package/dist/restart-guard.js +109 -0
  76. package/dist/restart-guard.js.map +1 -0
  77. package/dist/scheduler/db.js +6 -2
  78. package/dist/scheduler/db.js.map +1 -1
  79. package/dist/service-activation.d.ts +47 -5
  80. package/dist/service-activation.js +156 -51
  81. package/dist/service-activation.js.map +1 -1
  82. package/dist/service-installer.d.ts +56 -1
  83. package/dist/service-installer.js +134 -12
  84. package/dist/service-installer.js.map +1 -1
  85. package/dist/service-plan.d.ts +65 -0
  86. package/dist/service-plan.js +108 -0
  87. package/dist/service-plan.js.map +1 -0
  88. package/dist/settings-api.js +46 -5
  89. package/dist/settings-api.js.map +1 -1
  90. package/dist/settings-change.js +34 -6
  91. package/dist/settings-change.js.map +1 -1
  92. package/dist/setup-host.js +2 -3
  93. package/dist/setup-host.js.map +1 -1
  94. package/dist/setup-wizard.js +2 -1
  95. package/dist/setup-wizard.js.map +1 -1
  96. package/dist/sqlite-open-errors.d.ts +12 -0
  97. package/dist/sqlite-open-errors.js +13 -0
  98. package/dist/sqlite-open-errors.js.map +1 -0
  99. package/dist/tmux-control.d.ts +22 -0
  100. package/dist/tmux-control.js +89 -5
  101. package/dist/tmux-control.js.map +1 -1
  102. package/dist/topic-commands.js +14 -5
  103. package/dist/topic-commands.js.map +1 -1
  104. package/dist/ui/chat-commands.js +43 -0
  105. package/dist/ui/chat-render.js +2 -2
  106. package/dist/ui/chat-store.js +30 -1
  107. package/dist/ui/chat-strings.js +22 -2
  108. package/dist/ui/chat-thread.js +47 -10
  109. package/dist/ui/fleet-cache.js +168 -0
  110. package/dist/ui/panel-chat.js +104 -18
  111. package/dist/ui/panel-fleet.js +26 -12
  112. package/dist/ui/panel-settings.js +12 -8
  113. package/dist/ui/preview-panel.js +202 -0
  114. package/dist/ui/preview.js +7 -3
  115. package/dist/ui/settings-strings.js +4 -2
  116. package/dist/ui/shared/app-route.js +1 -1
  117. package/dist/ui/shared/app-shell.js +6 -4
  118. package/dist/ui/shared/app.css +147 -3
  119. package/dist/ui/shared/signin.js +13 -1
  120. package/dist/ui/shared/ui-icons.js +2 -0
  121. package/dist/ui/signin.html +2 -1
  122. package/dist/update-dispatch.d.ts +28 -0
  123. package/dist/update-dispatch.js +73 -0
  124. package/dist/update-dispatch.js.map +1 -0
  125. package/dist/update-install.d.ts +65 -7
  126. package/dist/update-install.js +126 -26
  127. package/dist/update-install.js.map +1 -1
  128. package/dist/web-api.d.ts +14 -0
  129. package/dist/web-api.js +65 -0
  130. package/dist/web-api.js.map +1 -1
  131. package/dist/web-commands.d.ts +77 -0
  132. package/dist/web-commands.js +81 -0
  133. package/dist/web-commands.js.map +1 -0
  134. package/dist/web-shell-routes.d.ts +1 -1
  135. package/dist/web-shell-routes.js +1 -1
  136. package/dist/web-shell-routes.js.map +1 -1
  137. package/launcher/agend +110 -0
  138. package/launcher/agend-agent +110 -0
  139. package/launcher/agend-agent.cjs +4 -0
  140. package/launcher/agend.cjs +4 -0
  141. package/launcher/install-admission.cjs +105 -0
  142. package/launcher/launch.cjs +64 -0
  143. package/launcher/postinstall.cjs +114 -0
  144. package/launcher/run-in-process.cjs +13 -0
  145. package/launcher/runtime-platform.cjs +86 -0
  146. package/launcher/runtime-select.cjs +217 -0
  147. package/package.json +12 -4
  148. package/scripts/preinstall-guard.cjs +40 -21
  149. package/templates/launchd.plist.ejs +2 -2
  150. package/templates/systemd.service.ejs +1 -1
@@ -0,0 +1,92 @@
1
+ /**
2
+ * #1468: the prompt-cache expiry analysis — pure, over a ledger (cache-ledger.ts). For a window [from, to]:
3
+ *
4
+ * - Each gap carries the TTL of the cache it sat on, as that session last wrote it: Claude Code's own write split
5
+ * (1 hour, or 5 minutes), Codex's 30 minutes (GPT-5.6 and later). Sessions of one instance can differ.
6
+ * - An expired request: one that came after a gap longer than its TTL and had to write at least half of the context
7
+ * the previous request left cached (the cache can outlive its TTL; then nothing was lost). What it rewrote, up to
8
+ * that context, is the expiry's tokens; its cost is those tokens at the write rate minus what reading them would
9
+ * have cost, at the returning request's own price tier.
10
+ * - Keep-warm, simulated: a ping at every I = TTL − TTL/10 after a request (t + k·I, k ≥ 1, up to and including
11
+ * the moment the gap ends — floor(gap / I) of them), each reading the cached context at that context's own tier.
12
+ * Only pings whose instant falls inside the window are counted. A gap that ended in an expired request inside the
13
+ * window saves its expiry cost; any other gap only pays for its pings. After a session's last request the pings
14
+ * are pure loss, up to the moment the next session of the instance starts, or the window ends.
15
+ * - Recommendation: on when the simulated pings cost less than the expiries they prevent (by a margin), else off.
16
+ *
17
+ * Money is list price (cache-prices.ts). If any model the window touches — requests, pinged contexts, idle tails —
18
+ * is missing from the table, the whole instance is weighed in input-price units instead (write 1.25x, 1-hour write
19
+ * 2x, read 0.1x, output 5x) and shows no dollars.
20
+ */
21
+ import { type Rates } from "./cache-prices.js";
22
+ import { type GapEvent, type Ledger } from "./cache-ledger.js";
23
+ export type Backend = "claude" | "codex";
24
+ export interface SimOptions {
25
+ rates: (model: string, prompt: number) => Rates;
26
+ from: number;
27
+ to: number;
28
+ /** Codex: use each event's measured write (the instance records real writes) rather than the estimate. */
29
+ measured: boolean;
30
+ }
31
+ export interface SimTail {
32
+ t: number;
33
+ ctx: number;
34
+ model: string;
35
+ ttl: number;
36
+ until: number;
37
+ }
38
+ export interface SimResult {
39
+ pastTtl: number;
40
+ expired: number;
41
+ rewriteTokens: number;
42
+ /** Expiry cost — and, below, every amount — in USD, or in input-price units when the instance is not priced. */
43
+ expiryCost: number;
44
+ pings: number;
45
+ pingCost: number;
46
+ tailPings: number;
47
+ tailCost: number;
48
+ /** What keep-warm would have saved, net of every ping. */
49
+ net: number;
50
+ /** A gap judged in this window used an estimated write (Codex's uncached input), not a recorded one. */
51
+ estimated: boolean;
52
+ }
53
+ export declare const UNITS: Rates;
54
+ export declare const pingIntervalMs: (ttlSec: number) => number;
55
+ /** Pings at t + k·I (k ≥ 1) no later than `end`, counted only where they fall inside [from, to]. */
56
+ export declare function pingsBetween(t: number, end: number, intervalMs: number, from: number, to: number): number;
57
+ /** The keep-warm simulation over a window's long gaps and its sessions' ends. */
58
+ export declare function simulate(gaps: readonly GapEvent[], tails: readonly SimTail[], o: SimOptions): SimResult;
59
+ /**
60
+ * Each session's end: its last request, until the next session of the instance starts (the earliest other session
61
+ * that starts at or after it — one that starts at the same instant leaves no idle time), or `to`.
62
+ */
63
+ export declare function sessionTails(ledger: Ledger, to: number): SimTail[];
64
+ /** Codex GPT-5.6 and later bill cache writes and keep a prefix 30 minutes. */
65
+ export declare const isOpenAiTtlModel: (model: string) => boolean;
66
+ export interface InstanceAnalysis {
67
+ requests: number;
68
+ /** The TTL the instance's latest session uses (each gap is judged by its own session's TTL). */
69
+ ttlSec: number;
70
+ /** Writes (and so expiries) are estimated: Codex records none. Also true for a Codex model whose TTL is assumed. */
71
+ estimate: boolean;
72
+ priced: boolean;
73
+ models: string[];
74
+ gapBuckets: number[];
75
+ sim: SimResult;
76
+ totalCost: number;
77
+ share: number | null;
78
+ recommendation: {
79
+ on: boolean;
80
+ reason: "quiet" | "never_expired" | "saves" | "costs_more";
81
+ pingsPerExpiry?: number;
82
+ };
83
+ }
84
+ export interface AnalyzeOptions {
85
+ backend: Backend;
86
+ from: number;
87
+ to: number;
88
+ rates?: (model: string, prompt: number) => Rates | null;
89
+ }
90
+ /** Requests in a window below this are too few to say anything. */
91
+ export declare const MIN_REQUESTS = 3;
92
+ export declare function analyzeLedger(ledger: Ledger, o: AnalyzeOptions): InstanceAnalysis;
@@ -0,0 +1,147 @@
1
+ /**
2
+ * #1468: the prompt-cache expiry analysis — pure, over a ledger (cache-ledger.ts). For a window [from, to]:
3
+ *
4
+ * - Each gap carries the TTL of the cache it sat on, as that session last wrote it: Claude Code's own write split
5
+ * (1 hour, or 5 minutes), Codex's 30 minutes (GPT-5.6 and later). Sessions of one instance can differ.
6
+ * - An expired request: one that came after a gap longer than its TTL and had to write at least half of the context
7
+ * the previous request left cached (the cache can outlive its TTL; then nothing was lost). What it rewrote, up to
8
+ * that context, is the expiry's tokens; its cost is those tokens at the write rate minus what reading them would
9
+ * have cost, at the returning request's own price tier.
10
+ * - Keep-warm, simulated: a ping at every I = TTL − TTL/10 after a request (t + k·I, k ≥ 1, up to and including
11
+ * the moment the gap ends — floor(gap / I) of them), each reading the cached context at that context's own tier.
12
+ * Only pings whose instant falls inside the window are counted. A gap that ended in an expired request inside the
13
+ * window saves its expiry cost; any other gap only pays for its pings. After a session's last request the pings
14
+ * are pure loss, up to the moment the next session of the instance starts, or the window ends.
15
+ * - Recommendation: on when the simulated pings cost less than the expiries they prevent (by a margin), else off.
16
+ *
17
+ * Money is list price (cache-prices.ts). If any model the window touches — requests, pinged contexts, idle tails —
18
+ * is missing from the table, the whole instance is weighed in input-price units instead (write 1.25x, 1-hour write
19
+ * 2x, read 0.1x, output 5x) and shows no dollars.
20
+ */
21
+ import { ratesFor } from "./cache-prices.js";
22
+ import { GAP_BUCKETS } from "./cache-ledger.js";
23
+ export const UNITS = { input: 1, read: 0.1, write5m: 1.25, write1h: 2, write: 1.25, output: 5 };
24
+ const M = 1_000_000;
25
+ export const pingIntervalMs = (ttlSec) => Math.round(ttlSec * 900);
26
+ const writeRate = (r, ttlSec) => (ttlSec >= 3600 ? r.write1h : ttlSec <= 300 ? r.write5m : r.write);
27
+ /** Pings at t + k·I (k ≥ 1) no later than `end`, counted only where they fall inside [from, to]. */
28
+ export function pingsBetween(t, end, intervalMs, from, to) {
29
+ if (!(intervalMs > 0))
30
+ return 0;
31
+ const kMin = Math.max(1, Math.ceil((from - t) / intervalMs));
32
+ const kMax = Math.floor((Math.min(end, to) - t) / intervalMs);
33
+ return Math.max(0, kMax - kMin + 1);
34
+ }
35
+ /** The keep-warm simulation over a window's long gaps and its sessions' ends. */
36
+ export function simulate(gaps, tails, o) {
37
+ const res = { pastTtl: 0, expired: 0, rewriteTokens: 0, expiryCost: 0, pings: 0, pingCost: 0, tailPings: 0, tailCost: 0, net: 0, estimated: false };
38
+ let saved = 0;
39
+ for (const [tEnd, gap, ctx, ctxModel, ttl, model, prompt, measured, estimated] of gaps) {
40
+ if (tEnd < o.from)
41
+ continue;
42
+ const start = tEnd - Math.round(gap * 1000);
43
+ const pings = pingsBetween(start, tEnd, pingIntervalMs(ttl), o.from, o.to);
44
+ if (pings) {
45
+ res.pings += pings;
46
+ res.pingCost += pings * ctx * o.rates(ctxModel, ctx).read / M;
47
+ }
48
+ if (tEnd > o.to || !(gap > ttl))
49
+ continue;
50
+ res.pastTtl++;
51
+ const useMeasured = o.measured && measured !== null;
52
+ if (!useMeasured)
53
+ res.estimated = true;
54
+ const rewrite = useMeasured ? measured : estimated;
55
+ const lost = Math.min(rewrite, ctx);
56
+ if (ctx > 0 && lost >= ctx / 2) {
57
+ const r = o.rates(model, prompt);
58
+ const cost = lost * (writeRate(r, ttl) - r.read) / M;
59
+ res.expired++;
60
+ res.rewriteTokens += lost;
61
+ res.expiryCost += cost;
62
+ saved += cost;
63
+ }
64
+ }
65
+ for (const tail of tails) {
66
+ const n = pingsBetween(tail.t, tail.until, pingIntervalMs(tail.ttl), o.from, o.to);
67
+ if (!n)
68
+ continue;
69
+ res.tailPings += n;
70
+ res.tailCost += n * tail.ctx * o.rates(tail.model, tail.ctx).read / M;
71
+ }
72
+ res.net = saved - res.pingCost - res.tailCost;
73
+ return res;
74
+ }
75
+ /**
76
+ * Each session's end: its last request, until the next session of the instance starts (the earliest other session
77
+ * that starts at or after it — one that starts at the same instant leaves no idle time), or `to`.
78
+ */
79
+ export function sessionTails(ledger, to) {
80
+ const sessions = Object.values(ledger.files).filter((f) => f.last && f.first !== undefined).map((f) => ({ first: f.first, last: f.last }));
81
+ return sessions.map((s) => {
82
+ let until = to;
83
+ for (const o of sessions)
84
+ if (o !== s && o.first >= s.last.t && o.first < until)
85
+ until = o.first;
86
+ return { t: s.last.t, ctx: s.last.ctx, model: s.last.model, ttl: s.last.ttl, until };
87
+ });
88
+ }
89
+ /** Codex GPT-5.6 and later bill cache writes and keep a prefix 30 minutes. */
90
+ export const isOpenAiTtlModel = (model) => /^gpt-(5\.(6|[7-9])|[6-9])/.test(model);
91
+ /** Requests in a window below this are too few to say anything. */
92
+ export const MIN_REQUESTS = 3;
93
+ export function analyzeLedger(ledger, o) {
94
+ const listed = o.rates ?? ratesFor;
95
+ const inWindow = (h) => h + 3_600_000 > o.from && h <= o.to;
96
+ const tails = sessionTails(ledger, o.to);
97
+ const estimatedTotals = o.backend === "codex" && !ledger.writesSeen;
98
+ // One unit for the whole instance: dollars when every model that contributes to this window is priced — the
99
+ // window's requests, and whatever the simulation itself prices (a pinged context, an expired request, an idle tail).
100
+ // A probe run records exactly the models the simulation asks a rate for.
101
+ const requestModels = new Set();
102
+ for (const [hk, hour] of Object.entries(ledger.hours)) {
103
+ if (!inWindow(Number(hk)))
104
+ continue;
105
+ for (const mk of Object.keys(hour.m))
106
+ requestModels.add(mk.slice(0, mk.lastIndexOf("|")));
107
+ }
108
+ const touched = new Set(requestModels);
109
+ simulate(ledger.gaps, tails, { rates: (model) => { touched.add(model); return UNITS; }, from: o.from, to: o.to, measured: !estimatedTotals });
110
+ const priced = [...touched].every((m) => listed(m, 0) !== null);
111
+ const rates = (model, prompt) => (priced ? listed(model, prompt) : null) ?? UNITS;
112
+ const gapBuckets = new Array(GAP_BUCKETS).fill(0);
113
+ let requests = 0, totalCost = 0;
114
+ for (const [hk, hour] of Object.entries(ledger.hours)) {
115
+ if (!inWindow(Number(hk)))
116
+ continue;
117
+ hour.g.forEach((n, i) => { gapBuckets[i] += n; });
118
+ for (const [mk, [n, uncached, read, write5m, write1h, write, output]] of Object.entries(hour.m)) {
119
+ const bar = mk.lastIndexOf("|");
120
+ const model = mk.slice(0, bar), long = mk.slice(bar + 1) === "1";
121
+ requests += n;
122
+ const r = rates(model, long ? Number.MAX_SAFE_INTEGER : 0);
123
+ // Codex on GPT-5.6+ with no recorded writes: its uncached input is what got written (the estimate).
124
+ const uncachedRate = estimatedTotals && isOpenAiTtlModel(model) ? r.write : r.input;
125
+ totalCost += (uncached * uncachedRate + read * r.read + write5m * r.write5m + write1h * r.write1h + write * r.write + output * r.output) / M;
126
+ }
127
+ }
128
+ const sim = simulate(ledger.gaps, tails, { rates, from: o.from, to: o.to, measured: !estimatedTotals });
129
+ const latest = tails.reduce((a, t) => (!a || t.t > a.t ? t : a), null);
130
+ const ttlSec = latest ? latest.ttl : o.backend === "codex" ? 1800 : 3600;
131
+ const assumedTtl = o.backend === "codex" && [...requestModels].some((m) => !isOpenAiTtlModel(m));
132
+ let recommendation;
133
+ if (requests < MIN_REQUESTS)
134
+ recommendation = { on: false, reason: "quiet" };
135
+ else if (!sim.expired)
136
+ recommendation = { on: false, reason: "never_expired" };
137
+ else if (sim.net > 0 && sim.net >= sim.expiryCost * 0.1)
138
+ recommendation = { on: true, reason: "saves" };
139
+ else
140
+ recommendation = { on: false, reason: "costs_more", pingsPerExpiry: Math.round((sim.pings + sim.tailPings) / sim.expired) };
141
+ return {
142
+ // An estimate when anything shown used one: the totals (Codex with no recorded write), a gap's rewrite, a TTL.
143
+ requests, ttlSec, estimate: (estimatedTotals && requests > 0) || sim.estimated || assumedTtl, priced, models: [...requestModels].sort(), gapBuckets, sim,
144
+ totalCost, share: totalCost > 0 ? sim.expiryCost / totalCost : null, recommendation,
145
+ };
146
+ }
147
+ //# sourceMappingURL=cache-analysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-analysis.js","sourceRoot":"","sources":["../src/cache-analysis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * #1468: what the prompt-cache analysis keeps per instance — never the transcripts themselves. Each model request a
3
+ * CLI made (a Claude assistant message, a Codex `token_count`) becomes a "turn"; the ledger folds turns into:
4
+ * - hourly totals per model and price tier (requests and tokens: uncached input, cache reads, cache writes, output)
5
+ * with the hour's histogram of gaps between consecutive requests of the same session;
6
+ * - one event per long gap (≥ GAP_EVENT_SEC): when it ended, how long it was, the context it left cached, what the
7
+ * request after it wrote to the cache — all the keep-warm simulation needs;
8
+ * - per transcript file: where reading stopped (a line start), and the session's first and last request.
9
+ * That keeps a month of a busy fleet in kilobytes per instance, and a file is read once, incrementally.
10
+ *
11
+ * Usage fields (real shapes, checked 2026-10-09):
12
+ * - Claude Code: `message.usage` { input_tokens, cache_read_input_tokens, cache_creation_input_tokens,
13
+ * cache_creation: { ephemeral_5m_input_tokens, ephemeral_1h_input_tokens }, output_tokens }. A message spans several
14
+ * lines (one per content block) carrying the same id and usage: counted once.
15
+ * - Codex (0.160): `event_msg` `token_count` with `info.last_token_usage` { input_tokens (cached included),
16
+ * cached_input_tokens, cache_write_input_tokens, output_tokens }. The write field exists but was 0 in every one of
17
+ * 38,551 real events on GPT-5.6+ models — so a write is estimated as the request's uncached input unless a real
18
+ * write count shows up. The model comes from the preceding `turn_context`.
19
+ */
20
+ import { type ScanLine } from "./cache-scan.js";
21
+ export type TranscriptKind = "claude" | "codex";
22
+ /** One model request, as the transcript records it. */
23
+ export interface Turn {
24
+ t: number;
25
+ model: string;
26
+ /** Everything the request sent: uncached input + cache reads + cache writes. */
27
+ prompt: number;
28
+ uncached: number;
29
+ read: number;
30
+ write5m: number;
31
+ write1h: number;
32
+ /** An OpenAI cache write as recorded (null: not recorded, 0 included when the field is absent). */
33
+ write: number | null;
34
+ output: number;
35
+ }
36
+ /** Gap histogram edges, seconds: ≤5 min, ≤30 min, ≤1 h, ≤2 h, ≤6 h, ≤24 h, longer. */
37
+ export declare const GAP_EDGES: readonly [300, 1800, 3600, 7200, 21600, 86400];
38
+ export declare const GAP_BUCKETS: number;
39
+ /** Gaps at least this long are kept one by one (the shortest keep-warm interval is a 5-minute TTL's 270 s). */
40
+ export declare const GAP_EVENT_SEC = 240;
41
+ export declare const KEEP_DAYS = 31;
42
+ /** [requests, uncached, read, write5m, write1h, write, output] */
43
+ export type HourTotals = [number, number, number, number, number, number, number];
44
+ export interface HourAgg {
45
+ m: Record<string, HourTotals>;
46
+ g: number[];
47
+ }
48
+ /**
49
+ * One long gap: [tEnd, gapSec, contextCached, contextModel, ttlSec, model, promptAfter, measuredWrite, estimatedWrite].
50
+ * - `contextCached`/`contextModel`: what the previous request left cached, and its model — what a ping would read.
51
+ * - `ttlSec`: the TTL of that cache as the session last wrote it (Claude's own write split; Codex's 30 minutes).
52
+ * - `measuredWrite`: what the request after the gap recorded writing (Claude always; Codex its write field, 0
53
+ * included, or null when the field is absent). `estimatedWrite`: Codex's uncached input — used only while the
54
+ * instance has never recorded a real write (the field exists but stays 0 on the CLIs seen so far).
55
+ */
56
+ export type GapEvent = [number, number, number, string, number, string, number, number | null, number];
57
+ export interface SessionPoint {
58
+ t: number;
59
+ ctx: number;
60
+ model: string;
61
+ ttl: number;
62
+ }
63
+ export interface FileCursor {
64
+ kind: TranscriptKind;
65
+ ino: number;
66
+ offset: number;
67
+ first?: number;
68
+ last?: SessionPoint;
69
+ /** Claude: the TTL the session's latest cache write used (1 h or 5 min). */
70
+ ttl?: number;
71
+ /** The file size at which only an unfinished line was left: not read again until the file grows. */
72
+ stalled?: number;
73
+ /** Codex: the model of the latest turn_context; the last cumulative total (token_count repeats itself). */
74
+ model?: string;
75
+ total?: number;
76
+ /** Claude: ids of the last messages counted (one message spans several lines). */
77
+ ids?: string[];
78
+ }
79
+ export interface Ledger {
80
+ v: 2;
81
+ files: Record<string, FileCursor>;
82
+ hours: Record<string, HourAgg>;
83
+ gaps: GapEvent[];
84
+ /** A real OpenAI cache-write count was seen (Codex): writes are then measured, not estimated. */
85
+ writesSeen?: boolean;
86
+ }
87
+ export declare function emptyLedger(): Ledger;
88
+ /** A ledger read back from disk; anything that is not one is a fresh ledger. */
89
+ export declare function reviveLedger(raw: unknown): Ledger;
90
+ export declare const hourOf: (t: number) => number;
91
+ export declare const gapBucket: (sec: number) => number;
92
+ /** The key hourly totals are kept under: the model, and whether the request paid its long-prompt row. */
93
+ export declare const modelKey: (model: string, prompt: number) => string;
94
+ /** Codex keeps a prefix 30 minutes (GPT-5.6 and later; assumed for any other Codex model, which is then an estimate). */
95
+ export declare const CODEX_TTL_SEC = 1800;
96
+ /** The TTL a Claude request's own cache writes used: its write split decides; no write leaves the session's as it was. */
97
+ export declare function claudeTtlAfter(turn: Turn, current: number | undefined): number;
98
+ /**
99
+ * Fold one turn into the ledger, in file order. Turns older than `cutoff` only move the session's last point (the
100
+ * next gap needs it); everything else is counted. Pure apart from mutating `ledger`.
101
+ */
102
+ export declare function addTurn(ledger: Ledger, file: FileCursor, turn: Turn, cutoff: number): void;
103
+ /** Drop what is older than `cutoff` (hours and gap events). */
104
+ export declare function pruneLedger(ledger: Ledger, cutoff: number): void;
105
+ /** The turn a Claude Code transcript line records, or null; a message already counted (`file.ids`) is null too. */
106
+ export declare function claudeLine(line: ScanLine, file: FileCursor): Turn | null;
107
+ /** The turn a Codex rollout line records, or null. A `turn_context` line updates the file's model. */
108
+ export declare function codexLine(line: ScanLine, file: FileCursor): Turn | null;
@@ -0,0 +1,230 @@
1
+ /**
2
+ * #1468: what the prompt-cache analysis keeps per instance — never the transcripts themselves. Each model request a
3
+ * CLI made (a Claude assistant message, a Codex `token_count`) becomes a "turn"; the ledger folds turns into:
4
+ * - hourly totals per model and price tier (requests and tokens: uncached input, cache reads, cache writes, output)
5
+ * with the hour's histogram of gaps between consecutive requests of the same session;
6
+ * - one event per long gap (≥ GAP_EVENT_SEC): when it ended, how long it was, the context it left cached, what the
7
+ * request after it wrote to the cache — all the keep-warm simulation needs;
8
+ * - per transcript file: where reading stopped (a line start), and the session's first and last request.
9
+ * That keeps a month of a busy fleet in kilobytes per instance, and a file is read once, incrementally.
10
+ *
11
+ * Usage fields (real shapes, checked 2026-10-09):
12
+ * - Claude Code: `message.usage` { input_tokens, cache_read_input_tokens, cache_creation_input_tokens,
13
+ * cache_creation: { ephemeral_5m_input_tokens, ephemeral_1h_input_tokens }, output_tokens }. A message spans several
14
+ * lines (one per content block) carrying the same id and usage: counted once.
15
+ * - Codex (0.160): `event_msg` `token_count` with `info.last_token_usage` { input_tokens (cached included),
16
+ * cached_input_tokens, cache_write_input_tokens, output_tokens }. The write field exists but was 0 in every one of
17
+ * 38,551 real events on GPT-5.6+ models — so a write is estimated as the request's uncached input unless a real
18
+ * write count shows up. The model comes from the preceding `turn_context`.
19
+ */
20
+ import { objectAt } from "./cache-scan.js";
21
+ import { isLongPrompt } from "./cache-prices.js";
22
+ /** Gap histogram edges, seconds: ≤5 min, ≤30 min, ≤1 h, ≤2 h, ≤6 h, ≤24 h, longer. */
23
+ export const GAP_EDGES = [300, 1800, 3600, 7200, 21600, 86400];
24
+ export const GAP_BUCKETS = GAP_EDGES.length + 1;
25
+ /** Gaps at least this long are kept one by one (the shortest keep-warm interval is a 5-minute TTL's 270 s). */
26
+ export const GAP_EVENT_SEC = 240;
27
+ export const KEEP_DAYS = 31;
28
+ /** A dictionary keyed by names from outside (paths, model ids): every key an own property (#1467 lesson). */
29
+ const dict = () => Object.create(null);
30
+ export function emptyLedger() { return { v: 2, files: dict(), hours: dict(), gaps: [] }; }
31
+ /** A ledger read back from disk; anything that is not one is a fresh ledger. */
32
+ export function reviveLedger(raw) {
33
+ const l = emptyLedger();
34
+ if (!raw || typeof raw !== "object" || raw.v !== 2)
35
+ return l;
36
+ const r = raw;
37
+ for (const [k, v] of Object.entries(r.files ?? {}))
38
+ if (v && typeof v.offset === "number")
39
+ l.files[k] = v;
40
+ for (const [k, v] of Object.entries(r.hours ?? {})) {
41
+ if (!v || !Array.isArray(v.g))
42
+ continue;
43
+ const m = dict();
44
+ for (const [mk, tot] of Object.entries(v.m ?? {}))
45
+ if (Array.isArray(tot) && tot.length === 7)
46
+ m[mk] = tot;
47
+ l.hours[k] = { m, g: v.g };
48
+ }
49
+ if (Array.isArray(r.gaps))
50
+ l.gaps = r.gaps.filter((g) => Array.isArray(g) && g.length === 9);
51
+ if (r.writesSeen)
52
+ l.writesSeen = true;
53
+ return l;
54
+ }
55
+ const HOUR = 3_600_000;
56
+ export const hourOf = (t) => Math.floor(t / HOUR) * HOUR;
57
+ export const gapBucket = (sec) => { let i = 0; while (i < GAP_EDGES.length && sec > GAP_EDGES[i])
58
+ i++; return i; };
59
+ /** The key hourly totals are kept under: the model, and whether the request paid its long-prompt row. */
60
+ export const modelKey = (model, prompt) => `${model}|${isLongPrompt(model, prompt) ? 1 : 0}`;
61
+ /** Codex keeps a prefix 30 minutes (GPT-5.6 and later; assumed for any other Codex model, which is then an estimate). */
62
+ export const CODEX_TTL_SEC = 1800;
63
+ const CLAUDE_DEFAULT_TTL_SEC = 3600;
64
+ /** The TTL a Claude request's own cache writes used: its write split decides; no write leaves the session's as it was. */
65
+ export function claudeTtlAfter(turn, current) {
66
+ if (turn.write1h > 0 || turn.write5m > 0)
67
+ return turn.write5m > turn.write1h ? 300 : 3600;
68
+ return current ?? CLAUDE_DEFAULT_TTL_SEC;
69
+ }
70
+ /**
71
+ * Fold one turn into the ledger, in file order. Turns older than `cutoff` only move the session's last point (the
72
+ * next gap needs it); everything else is counted. Pure apart from mutating `ledger`.
73
+ */
74
+ export function addTurn(ledger, file, turn, cutoff) {
75
+ const prev = file.last;
76
+ const ttlBefore = file.kind === "claude" ? (file.ttl ?? CLAUDE_DEFAULT_TTL_SEC) : CODEX_TTL_SEC;
77
+ if (file.kind === "claude")
78
+ file.ttl = claudeTtlAfter(turn, file.ttl);
79
+ file.first ??= turn.t;
80
+ file.last = { t: turn.t, ctx: turn.prompt + turn.output, model: turn.model, ttl: file.kind === "claude" ? file.ttl : CODEX_TTL_SEC };
81
+ if (turn.write !== null && turn.write > 0)
82
+ ledger.writesSeen = true;
83
+ if (turn.t < cutoff)
84
+ return;
85
+ const hk = String(hourOf(turn.t));
86
+ const hour = ledger.hours[hk] ??= { m: dict(), g: new Array(GAP_BUCKETS).fill(0) };
87
+ const mk = modelKey(turn.model, turn.prompt);
88
+ const tot = hour.m[mk] ??= [0, 0, 0, 0, 0, 0, 0];
89
+ tot[0] += 1;
90
+ tot[1] += turn.uncached;
91
+ tot[2] += turn.read;
92
+ tot[3] += turn.write5m;
93
+ tot[4] += turn.write1h;
94
+ tot[5] += turn.write ?? 0;
95
+ tot[6] += turn.output;
96
+ if (!prev)
97
+ return;
98
+ const gap = (turn.t - prev.t) / 1000;
99
+ if (!(gap >= 0))
100
+ return;
101
+ hour.g[gapBucket(gap)] += 1;
102
+ if (gap < GAP_EVENT_SEC)
103
+ return;
104
+ const measured = file.kind === "claude" ? turn.write5m + turn.write1h : turn.write;
105
+ const estimated = file.kind === "claude" ? turn.write5m + turn.write1h : turn.uncached + (turn.write ?? 0);
106
+ ledger.gaps.push([turn.t, gap, prev.ctx, prev.model, ttlBefore, turn.model, turn.prompt, measured, estimated]);
107
+ }
108
+ /** Drop what is older than `cutoff` (hours and gap events). */
109
+ export function pruneLedger(ledger, cutoff) {
110
+ for (const k of Object.keys(ledger.hours))
111
+ if (Number(k) + HOUR <= cutoff)
112
+ delete ledger.hours[k];
113
+ if (ledger.gaps.length && ledger.gaps[0][0] < cutoff)
114
+ ledger.gaps = ledger.gaps.filter((g) => g[0] >= cutoff);
115
+ }
116
+ // ── Reading turns out of transcript lines ──
117
+ const num = (v) => (typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0);
118
+ const CLAUDE_ID = /"id":"(msg_[A-Za-z0-9_-]{1,120})"/;
119
+ const CLAUDE_ID_G = /"id":"(msg_[A-Za-z0-9_-]{1,120})"/g;
120
+ const CLAUDE_MODEL = /"model":"([^"]{1,120})"/;
121
+ const CLAUDE_MODEL_G = /"model":"([^"]{1,120})"/g;
122
+ const TIMESTAMP = /"timestamp":"([^"]{10,40})"/g;
123
+ const lastMatch = (text, re) => { const all = [...text.matchAll(re)]; return all.length ? all[all.length - 1][1] : undefined; };
124
+ function lastUsage(text) {
125
+ const at = text.lastIndexOf("\"usage\":{");
126
+ const obj = at === -1 ? null : objectAt(text, at + "\"usage\":".length);
127
+ try {
128
+ return obj ? JSON.parse(obj) : undefined;
129
+ }
130
+ catch {
131
+ return undefined;
132
+ }
133
+ }
134
+ function claudeTurn(u, model, ts) {
135
+ if (!u || typeof model !== "string" || typeof ts !== "string")
136
+ return null;
137
+ const t = Date.parse(ts);
138
+ if (!Number.isFinite(t))
139
+ return null;
140
+ const input = num(u.input_tokens), read = num(u.cache_read_input_tokens), creation = num(u.cache_creation_input_tokens);
141
+ const split = u.cache_creation;
142
+ let write1h = num(split?.ephemeral_1h_input_tokens), write5m = num(split?.ephemeral_5m_input_tokens);
143
+ // No split recorded (older CLIs): the API's default, a 5-minute write.
144
+ if (!split && creation)
145
+ write5m = creation;
146
+ if (split && write1h + write5m !== creation)
147
+ write5m = Math.max(0, creation - write1h);
148
+ return { t, model, prompt: input + read + creation, uncached: input, read, write5m, write1h, write: null, output: num(u.output_tokens) };
149
+ }
150
+ /** The turn a Claude Code transcript line records, or null; a message already counted (`file.ids`) is null too. */
151
+ export function claudeLine(line, file) {
152
+ let id, turn = null;
153
+ if ("whole" in line) {
154
+ if (line.whole.indexOf("\"usage\"") === -1 || line.whole.indexOf("\"assistant\"") === -1)
155
+ return null;
156
+ let d;
157
+ try {
158
+ d = JSON.parse(line.whole.toString("utf8"));
159
+ }
160
+ catch {
161
+ return null;
162
+ }
163
+ if (d.type !== "assistant" || d.isSidechain === true || !d.message)
164
+ return null;
165
+ id = typeof d.message.id === "string" ? d.message.id : undefined;
166
+ if (id && file.ids?.includes(id))
167
+ return null;
168
+ turn = claudeTurn(d.message.usage, d.message.model, d.timestamp);
169
+ }
170
+ else {
171
+ // A long line, kept as its two ends. Claude writes the model and id near the start and the usage and timestamp
172
+ // near the end, but where `type` and `isSidechain` sit depends on the writer: both ends are searched alike.
173
+ const head = line.head.toString("utf8"), tail = line.tail.toString("utf8");
174
+ const either = (needle) => head.includes(needle) || tail.includes(needle);
175
+ if (!either("\"type\":\"assistant\"") || either("\"isSidechain\":true"))
176
+ return null;
177
+ id = CLAUDE_ID.exec(head)?.[1] ?? lastMatch(tail, CLAUDE_ID_G);
178
+ if (id && file.ids?.includes(id))
179
+ return null;
180
+ const usage = lastUsage(tail) ?? lastUsage(head);
181
+ const ts = lastMatch(tail, TIMESTAMP) ?? lastMatch(head, TIMESTAMP);
182
+ turn = claudeTurn(usage, CLAUDE_MODEL.exec(head)?.[1] ?? lastMatch(tail, CLAUDE_MODEL_G), ts);
183
+ }
184
+ if (turn && id)
185
+ file.ids = [...(file.ids ?? []).slice(-15), id];
186
+ return turn;
187
+ }
188
+ /** The turn a Codex rollout line records, or null. A `turn_context` line updates the file's model. */
189
+ export function codexLine(line, file) {
190
+ if (!("whole" in line))
191
+ return null; // token_count and turn_context lines are small
192
+ const w = line.whole;
193
+ const isCount = w.indexOf("\"token_count\"") !== -1;
194
+ if (!isCount && w.indexOf("\"turn_context\"") === -1)
195
+ return null;
196
+ let d;
197
+ try {
198
+ d = JSON.parse(w.toString("utf8"));
199
+ }
200
+ catch {
201
+ return null;
202
+ }
203
+ const p = d.payload;
204
+ if (!p)
205
+ return null;
206
+ if (d.type === "turn_context") {
207
+ if (typeof p.model === "string")
208
+ file.model = p.model;
209
+ return null;
210
+ }
211
+ if (d.type !== "event_msg" || p.type !== "token_count")
212
+ return null;
213
+ const info = p.info;
214
+ const u = info?.last_token_usage;
215
+ if (!u || typeof d.timestamp !== "string")
216
+ return null;
217
+ // The same usage is re-announced (a rate-limit update): the running total has not moved.
218
+ const total = num(info?.total_token_usage?.total_tokens);
219
+ if (total && file.total === total)
220
+ return null;
221
+ if (total)
222
+ file.total = total;
223
+ const t = Date.parse(d.timestamp);
224
+ if (!Number.isFinite(t))
225
+ return null;
226
+ const input = num(u.input_tokens), read = Math.min(num(u.cached_input_tokens), input);
227
+ const write = typeof u.cache_write_input_tokens === "number" ? Math.min(num(u.cache_write_input_tokens), input - read) : null;
228
+ return { t, model: file.model ?? "unknown", prompt: input, uncached: input - read - (write ?? 0), read, write5m: 0, write1h: 0, write, output: num(u.output_tokens) };
229
+ }
230
+ //# sourceMappingURL=cache-ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-ledger.js","sourceRoot":"","sources":["../src/cache-ledger.ts"],"names":[],"mappings":"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"}