@songsid/agend 2.2.0-alpha.1 → 2.2.0-alpha.3

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 (317) hide show
  1. package/README.md +6 -4
  2. package/README.zh-TW.md +6 -4
  3. package/dist/agent-cli.js +6 -1
  4. package/dist/agent-cli.js.map +1 -1
  5. package/dist/agent-endpoint.d.ts +4 -1
  6. package/dist/agent-endpoint.js +82 -14
  7. package/dist/agent-endpoint.js.map +1 -1
  8. package/dist/agent-token-header.d.ts +13 -0
  9. package/dist/agent-token-header.js +16 -0
  10. package/dist/agent-token-header.js.map +1 -0
  11. package/dist/atomic-write.d.ts +21 -0
  12. package/dist/atomic-write.js +65 -0
  13. package/dist/atomic-write.js.map +1 -0
  14. package/dist/auth-api.js +2 -0
  15. package/dist/auth-api.js.map +1 -1
  16. package/dist/backend/antigravity.js +4 -3
  17. package/dist/backend/antigravity.js.map +1 -1
  18. package/dist/backend/codex.d.ts +7 -0
  19. package/dist/backend/codex.js +7 -0
  20. package/dist/backend/codex.js.map +1 -1
  21. package/dist/backend/grok.js +1 -1
  22. package/dist/backend/grok.js.map +1 -1
  23. package/dist/backend/kiro.d.ts +7 -0
  24. package/dist/backend/kiro.js +82 -33
  25. package/dist/backend/kiro.js.map +1 -1
  26. package/dist/backend/muse.d.ts +7 -0
  27. package/dist/backend/muse.js +8 -1
  28. package/dist/backend/muse.js.map +1 -1
  29. package/dist/backend/types.d.ts +12 -0
  30. package/dist/backend/types.js.map +1 -1
  31. package/dist/binary-probe.d.ts +80 -0
  32. package/dist/binary-probe.js +182 -0
  33. package/dist/binary-probe.js.map +1 -0
  34. package/dist/cache-analysis.d.ts +92 -0
  35. package/dist/cache-analysis.js +147 -0
  36. package/dist/cache-analysis.js.map +1 -0
  37. package/dist/cache-ledger.d.ts +108 -0
  38. package/dist/cache-ledger.js +230 -0
  39. package/dist/cache-ledger.js.map +1 -0
  40. package/dist/cache-prices.d.ts +32 -0
  41. package/dist/cache-prices.js +73 -0
  42. package/dist/cache-prices.js.map +1 -0
  43. package/dist/cache-scan.d.ts +39 -0
  44. package/dist/cache-scan.js +115 -0
  45. package/dist/cache-scan.js.map +1 -0
  46. package/dist/cache-service.d.ts +131 -0
  47. package/dist/cache-service.js +452 -0
  48. package/dist/cache-service.js.map +1 -0
  49. package/dist/channel/adapters/discord.d.ts +5 -1
  50. package/dist/channel/adapters/discord.js +56 -11
  51. package/dist/channel/adapters/discord.js.map +1 -1
  52. package/dist/channel/adapters/telegram.d.ts +38 -1
  53. package/dist/channel/adapters/telegram.js +82 -0
  54. package/dist/channel/adapters/telegram.js.map +1 -1
  55. package/dist/channel/mcp-tools.js +1 -1
  56. package/dist/channel/mcp-tools.js.map +1 -1
  57. package/dist/channel/tool-router.d.ts +8 -1
  58. package/dist/channel/tool-router.js +4 -2
  59. package/dist/channel/tool-router.js.map +1 -1
  60. package/dist/channel/types.d.ts +21 -0
  61. package/dist/cli-entry.d.ts +19 -0
  62. package/dist/cli-entry.js +36 -0
  63. package/dist/cli-entry.js.map +1 -0
  64. package/dist/cli.js +376 -54
  65. package/dist/cli.js.map +1 -1
  66. package/dist/connection-secrets.d.ts +5 -0
  67. package/dist/connection-secrets.js.map +1 -1
  68. package/dist/cpu-profile.js +50 -0
  69. package/dist/cpu-profile.js.map +1 -1
  70. package/dist/daemon.d.ts +18 -0
  71. package/dist/daemon.js +256 -63
  72. package/dist/daemon.js.map +1 -1
  73. package/dist/delivery-outbox.d.ts +1 -0
  74. package/dist/delivery-outbox.js +12 -0
  75. package/dist/delivery-outbox.js.map +1 -1
  76. package/dist/discord-permissions.d.ts +33 -0
  77. package/dist/discord-permissions.js +42 -0
  78. package/dist/discord-permissions.js.map +1 -0
  79. package/dist/env-file.d.ts +11 -0
  80. package/dist/env-file.js +27 -0
  81. package/dist/env-file.js.map +1 -0
  82. package/dist/event-log.d.ts +4 -1
  83. package/dist/event-log.js +6 -3
  84. package/dist/event-log.js.map +1 -1
  85. package/dist/fleet-context.d.ts +5 -0
  86. package/dist/fleet-context.js.map +1 -1
  87. package/dist/fleet-control-audit.d.ts +5 -0
  88. package/dist/fleet-control-audit.js +7 -0
  89. package/dist/fleet-control-audit.js.map +1 -1
  90. package/dist/fleet-level-config.d.ts +8 -1
  91. package/dist/fleet-level-config.js +53 -0
  92. package/dist/fleet-level-config.js.map +1 -1
  93. package/dist/fleet-manager.d.ts +122 -7
  94. package/dist/fleet-manager.js +871 -152
  95. package/dist/fleet-manager.js.map +1 -1
  96. package/dist/fleet-stop-budget.d.ts +22 -0
  97. package/dist/fleet-stop-budget.js +95 -0
  98. package/dist/fleet-stop-budget.js.map +1 -0
  99. package/dist/full-restart.js +2 -3
  100. package/dist/full-restart.js.map +1 -1
  101. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +3 -1
  102. package/dist/install-lock.d.ts +49 -0
  103. package/dist/install-lock.js +200 -0
  104. package/dist/install-lock.js.map +1 -0
  105. package/dist/instance-config-impact.js +3 -2
  106. package/dist/instance-config-impact.js.map +1 -1
  107. package/dist/kiro-transcript-lane.d.ts +8 -4
  108. package/dist/kiro-transcript-lane.js +66 -44
  109. package/dist/kiro-transcript-lane.js.map +1 -1
  110. package/dist/locale.js +96 -0
  111. package/dist/locale.js.map +1 -1
  112. package/dist/next-steps.d.ts +5 -0
  113. package/dist/next-steps.js +26 -0
  114. package/dist/next-steps.js.map +1 -0
  115. package/dist/outbound-handlers.js +8 -0
  116. package/dist/outbound-handlers.js.map +1 -1
  117. package/dist/outbound-schemas.d.ts +4 -0
  118. package/dist/outbound-schemas.js +7 -2
  119. package/dist/outbound-schemas.js.map +1 -1
  120. package/dist/outbox-open-error.d.ts +7 -0
  121. package/dist/outbox-open-error.js +15 -0
  122. package/dist/outbox-open-error.js.map +1 -0
  123. package/dist/package-preimage.d.ts +26 -0
  124. package/dist/package-preimage.js +96 -0
  125. package/dist/package-preimage.js.map +1 -0
  126. package/dist/pane-context-cache.d.ts +25 -0
  127. package/dist/pane-context-cache.js +73 -0
  128. package/dist/pane-context-cache.js.map +1 -0
  129. package/dist/paths.d.ts +0 -7
  130. package/dist/paths.js +8 -2
  131. package/dist/paths.js.map +1 -1
  132. package/dist/provider-probe.d.ts +15 -0
  133. package/dist/provider-probe.js +20 -0
  134. package/dist/provider-probe.js.map +1 -1
  135. package/dist/public-link-progress.d.ts +73 -0
  136. package/dist/public-link-progress.js +190 -0
  137. package/dist/public-link-progress.js.map +1 -0
  138. package/dist/public-web-gateway.js +2 -2
  139. package/dist/public-web-gateway.js.map +1 -1
  140. package/dist/public-web-link.d.ts +6 -1
  141. package/dist/public-web-link.js +87 -15
  142. package/dist/public-web-link.js.map +1 -1
  143. package/dist/quickstart-api.d.ts +93 -20
  144. package/dist/quickstart-api.js +194 -62
  145. package/dist/quickstart-api.js.map +1 -1
  146. package/dist/quickstart.d.ts +15 -0
  147. package/dist/quickstart.js +110 -33
  148. package/dist/quickstart.js.map +1 -1
  149. package/dist/reply-buttons-controller.d.ts +98 -0
  150. package/dist/reply-buttons-controller.js +174 -0
  151. package/dist/reply-buttons-controller.js.map +1 -0
  152. package/dist/reply-buttons.d.ts +113 -0
  153. package/dist/reply-buttons.js +208 -0
  154. package/dist/reply-buttons.js.map +1 -0
  155. package/dist/reply-dedup.js +3 -1
  156. package/dist/reply-dedup.js.map +1 -1
  157. package/dist/restart-guard.d.ts +53 -0
  158. package/dist/restart-guard.js +114 -0
  159. package/dist/restart-guard.js.map +1 -0
  160. package/dist/scheduler/db.js +6 -2
  161. package/dist/scheduler/db.js.map +1 -1
  162. package/dist/service-activation.d.ts +62 -7
  163. package/dist/service-activation.js +237 -55
  164. package/dist/service-activation.js.map +1 -1
  165. package/dist/service-installer.d.ts +56 -1
  166. package/dist/service-installer.js +152 -12
  167. package/dist/service-installer.js.map +1 -1
  168. package/dist/service-plan.d.ts +65 -0
  169. package/dist/service-plan.js +108 -0
  170. package/dist/service-plan.js.map +1 -0
  171. package/dist/settings-api.js +92 -10
  172. package/dist/settings-api.js.map +1 -1
  173. package/dist/settings-authority.d.ts +11 -0
  174. package/dist/settings-authority.js +128 -0
  175. package/dist/settings-authority.js.map +1 -0
  176. package/dist/settings-change.d.ts +5 -0
  177. package/dist/settings-change.js +37 -8
  178. package/dist/settings-change.js.map +1 -1
  179. package/dist/settings-confirmation.d.ts +3 -0
  180. package/dist/settings-confirmation.js +7 -1
  181. package/dist/settings-confirmation.js.map +1 -1
  182. package/dist/settings-effect.js +19 -7
  183. package/dist/settings-effect.js.map +1 -1
  184. package/dist/settings-http-confirmation.js +1 -1
  185. package/dist/settings-http-confirmation.js.map +1 -1
  186. package/dist/settings-redaction.d.ts +18 -0
  187. package/dist/settings-redaction.js +58 -0
  188. package/dist/settings-redaction.js.map +1 -0
  189. package/dist/setup-form.d.ts +9 -1
  190. package/dist/setup-form.js +25 -1
  191. package/dist/setup-form.js.map +1 -1
  192. package/dist/setup-host.js +15 -6
  193. package/dist/setup-host.js.map +1 -1
  194. package/dist/setup-wizard.js +27 -19
  195. package/dist/setup-wizard.js.map +1 -1
  196. package/dist/sqlite-open-errors.d.ts +12 -0
  197. package/dist/sqlite-open-errors.js +13 -0
  198. package/dist/sqlite-open-errors.js.map +1 -0
  199. package/dist/systemd-runtime.d.ts +15 -0
  200. package/dist/systemd-runtime.js +41 -0
  201. package/dist/systemd-runtime.js.map +1 -0
  202. package/dist/tmux-control.d.ts +44 -0
  203. package/dist/tmux-control.js +170 -21
  204. package/dist/tmux-control.js.map +1 -1
  205. package/dist/token-env-name.d.ts +37 -0
  206. package/dist/token-env-name.js +93 -0
  207. package/dist/token-env-name.js.map +1 -0
  208. package/dist/tool-permissions.js +5 -3
  209. package/dist/tool-permissions.js.map +1 -1
  210. package/dist/topic-commands.d.ts +7 -16
  211. package/dist/topic-commands.js +56 -85
  212. package/dist/topic-commands.js.map +1 -1
  213. package/dist/transcript-monitor.js +3 -0
  214. package/dist/transcript-monitor.js.map +1 -1
  215. package/dist/transcript-sources.d.ts +68 -0
  216. package/dist/transcript-sources.js +336 -29
  217. package/dist/transcript-sources.js.map +1 -1
  218. package/dist/transcript-turns.d.ts +39 -0
  219. package/dist/transcript-turns.js +58 -0
  220. package/dist/transcript-turns.js.map +1 -0
  221. package/dist/tunnel/cloudflared-install.d.ts +18 -0
  222. package/dist/tunnel/cloudflared-install.js +15 -1
  223. package/dist/tunnel/cloudflared-install.js.map +1 -1
  224. package/dist/turn-reply-guard.d.ts +3 -0
  225. package/dist/turn-reply-guard.js.map +1 -1
  226. package/dist/ui/chat-commands.js +43 -0
  227. package/dist/ui/chat-render.js +34 -8
  228. package/dist/ui/chat-store.js +51 -2
  229. package/dist/ui/chat-strings.js +30 -2
  230. package/dist/ui/chat-thread.js +141 -47
  231. package/dist/ui/first-run.js +64 -0
  232. package/dist/ui/fleet-cache.js +168 -0
  233. package/dist/ui/html-attachment.js +26 -0
  234. package/dist/ui/panel-chat.js +117 -23
  235. package/dist/ui/panel-fleet.js +31 -17
  236. package/dist/ui/panel-settings.js +29 -15
  237. package/dist/ui/preview-panel.js +237 -0
  238. package/dist/ui/preview.js +7 -3
  239. package/dist/ui/settings-apply.js +3 -1
  240. package/dist/ui/settings-dialogs.js +95 -25
  241. package/dist/ui/settings-discord.js +130 -0
  242. package/dist/ui/settings-model.js +21 -0
  243. package/dist/ui/settings-strings.js +50 -8
  244. package/dist/ui/settings-token.js +54 -0
  245. package/dist/ui/settings-wizard.js +63 -29
  246. package/dist/ui/shared/app-route.js +1 -1
  247. package/dist/ui/shared/app-shell.js +25 -37
  248. package/dist/ui/shared/app-store.js +5 -3
  249. package/dist/ui/shared/app-stream.js +2 -1
  250. package/dist/ui/shared/app.css +181 -5
  251. package/dist/ui/shared/app.js +2 -1
  252. package/dist/ui/shared/instance-nav.js +310 -0
  253. package/dist/ui/shared/panel-view.js +12 -190
  254. package/dist/ui/shared/signin.js +13 -1
  255. package/dist/ui/shared/ui-icons.js +2 -0
  256. package/dist/ui/shared/view-roster-store.js +5 -0
  257. package/dist/ui/shared/view-strings.js +12 -0
  258. package/dist/ui/signin.html +2 -1
  259. package/dist/update-dispatch.d.ts +28 -0
  260. package/dist/update-dispatch.js +73 -0
  261. package/dist/update-dispatch.js.map +1 -0
  262. package/dist/update-install.d.ts +78 -8
  263. package/dist/update-install.js +158 -30
  264. package/dist/update-install.js.map +1 -1
  265. package/dist/update-launch.d.ts +22 -0
  266. package/dist/update-launch.js +106 -0
  267. package/dist/update-launch.js.map +1 -0
  268. package/dist/update-marker.js +3 -8
  269. package/dist/update-marker.js.map +1 -1
  270. package/dist/usage/format-rich.js +7 -1
  271. package/dist/usage/format-rich.js.map +1 -1
  272. package/dist/usage/i18n-keys.d.ts +1 -1
  273. package/dist/usage/i18n-keys.js +1 -1
  274. package/dist/usage/i18n-keys.js.map +1 -1
  275. package/dist/usage/providers.d.ts +4 -0
  276. package/dist/usage/providers.js +2 -1
  277. package/dist/usage/providers.js.map +1 -1
  278. package/dist/usage/usage-api.js +48 -2
  279. package/dist/usage/usage-api.js.map +1 -1
  280. package/dist/view-api.d.ts +40 -0
  281. package/dist/view-api.js +28 -10
  282. package/dist/view-api.js.map +1 -1
  283. package/dist/web-api.d.ts +22 -0
  284. package/dist/web-api.js +115 -16
  285. package/dist/web-api.js.map +1 -1
  286. package/dist/web-auth.d.ts +8 -0
  287. package/dist/web-auth.js +34 -8
  288. package/dist/web-auth.js.map +1 -1
  289. package/dist/web-chat-history.d.ts +21 -0
  290. package/dist/web-chat-history.js +44 -0
  291. package/dist/web-chat-history.js.map +1 -1
  292. package/dist/web-commands.d.ts +77 -0
  293. package/dist/web-commands.js +81 -0
  294. package/dist/web-commands.js.map +1 -0
  295. package/dist/web-host-guard.d.ts +18 -2
  296. package/dist/web-host-guard.js +30 -5
  297. package/dist/web-host-guard.js.map +1 -1
  298. package/dist/web-session.d.ts +7 -1
  299. package/dist/web-session.js +23 -2
  300. package/dist/web-session.js.map +1 -1
  301. package/dist/web-shell-routes.d.ts +1 -1
  302. package/dist/web-shell-routes.js +1 -1
  303. package/dist/web-shell-routes.js.map +1 -1
  304. package/launcher/agend +110 -0
  305. package/launcher/agend-agent +110 -0
  306. package/launcher/agend-agent.cjs +4 -0
  307. package/launcher/agend.cjs +4 -0
  308. package/launcher/install-admission.cjs +105 -0
  309. package/launcher/launch.cjs +64 -0
  310. package/launcher/postinstall.cjs +118 -0
  311. package/launcher/run-in-process.cjs +13 -0
  312. package/launcher/runtime-platform.cjs +86 -0
  313. package/launcher/runtime-select.cjs +217 -0
  314. package/package.json +12 -4
  315. package/scripts/preinstall-guard.cjs +40 -21
  316. package/templates/launchd.plist.ejs +2 -2
  317. package/templates/systemd.service.ejs +4 -4
@@ -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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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * #1468: list prices for the prompt-cache analysis — the one table, with the day it was checked against the vendors'
3
+ * own pricing pages. USD per million tokens, standard tier, global routing (no batch, fast, data-residency or
4
+ * regional uplift: AgEnD's CLIs run on subscriptions, so these are list-price equivalents, not a bill).
5
+ *
6
+ * Claude (platform.claude.com/docs/en/about-claude/pricing): a 5-minute cache write is 1.25x base input, a 1-hour
7
+ * write 2x; a cache hit is 0.1x, except 0.05x on Opus/Sonnet 5.5 and 0.025x on Fable/Mythos 5.1. Haiku 5.5 is priced
8
+ * by prompt length (over 100,000 tokens: the higher row), counting cache reads and writes.
9
+ * OpenAI (developers.openai.com/api/docs/pricing, …/guides/prompt-caching): on GPT-5.6 and later a cache write is
10
+ * 1.25x uncached input and the cache lives 30 minutes after its last write or reuse; prompts over 272K input tokens
11
+ * pay the long-context row.
12
+ */
13
+ export declare const PRICES_CHECKED = "2026-10-09";
14
+ export declare const PRICE_SOURCES: readonly ["https://platform.claude.com/docs/en/about-claude/pricing", "https://developers.openai.com/api/docs/pricing", "https://developers.openai.com/api/docs/guides/prompt-caching"];
15
+ /** USD per million tokens. `write` is the one cache-write rate an OpenAI model has; Claude has `write5m`/`write1h`. */
16
+ export interface Rates {
17
+ input: number;
18
+ read: number;
19
+ write5m: number;
20
+ write1h: number;
21
+ write: number;
22
+ output: number;
23
+ }
24
+ /**
25
+ * The table's key for a model id as a transcript records it: "claude-opus-4-5-20251101" → "claude-opus-4-5",
26
+ * "claude-opus-5-5[1m]" → "claude-opus-5-5". Exact keys only — "claude-opus-5" never prices "claude-opus-5-5".
27
+ */
28
+ export declare function priceKey(model: string | null | undefined): string | null;
29
+ /** The rates for one request of `model` with a prompt of `promptTokens`; null when the model is not in the table. */
30
+ export declare function ratesFor(model: string | null | undefined, promptTokens?: number): Rates | null;
31
+ /** Whether a prompt of this size pays the model's long-context (or over-100K) row. */
32
+ export declare function isLongPrompt(model: string | null | undefined, promptTokens: number): boolean;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * #1468: list prices for the prompt-cache analysis — the one table, with the day it was checked against the vendors'
3
+ * own pricing pages. USD per million tokens, standard tier, global routing (no batch, fast, data-residency or
4
+ * regional uplift: AgEnD's CLIs run on subscriptions, so these are list-price equivalents, not a bill).
5
+ *
6
+ * Claude (platform.claude.com/docs/en/about-claude/pricing): a 5-minute cache write is 1.25x base input, a 1-hour
7
+ * write 2x; a cache hit is 0.1x, except 0.05x on Opus/Sonnet 5.5 and 0.025x on Fable/Mythos 5.1. Haiku 5.5 is priced
8
+ * by prompt length (over 100,000 tokens: the higher row), counting cache reads and writes.
9
+ * OpenAI (developers.openai.com/api/docs/pricing, …/guides/prompt-caching): on GPT-5.6 and later a cache write is
10
+ * 1.25x uncached input and the cache lives 30 minutes after its last write or reuse; prompts over 272K input tokens
11
+ * pay the long-context row.
12
+ */
13
+ export const PRICES_CHECKED = "2026-10-09";
14
+ export const PRICE_SOURCES = [
15
+ "https://platform.claude.com/docs/en/about-claude/pricing",
16
+ "https://developers.openai.com/api/docs/pricing",
17
+ "https://developers.openai.com/api/docs/guides/prompt-caching",
18
+ ];
19
+ const claude = (input, read, output) => ({ input, read, write5m: input * 1.25, write1h: input * 2, write: input * 1.25, output });
20
+ const openai = (input, read, write, output) => ({ input, read, write5m: write, write1h: write, write, output });
21
+ const TABLE = {
22
+ "claude-fable-5-1": { rates: [claude(10, 0.25, 50)] },
23
+ "claude-mythos-5-1": { rates: [claude(10, 0.25, 50)] },
24
+ "claude-fable-5": { rates: [claude(10, 1, 50)] },
25
+ "claude-mythos-5": { rates: [claude(10, 1, 50)] },
26
+ "claude-opus-5-5": { rates: [claude(4, 0.20, 20)] },
27
+ "claude-opus-5": { rates: [claude(5, 0.50, 25)] },
28
+ "claude-opus-4-8": { rates: [claude(5, 0.50, 25)] },
29
+ "claude-opus-4-7": { rates: [claude(5, 0.50, 25)] },
30
+ "claude-opus-4-6": { rates: [claude(5, 0.50, 25)] },
31
+ "claude-opus-4-5": { rates: [claude(5, 0.50, 25)] },
32
+ "claude-opus-4-1": { rates: [claude(15, 1.50, 75)] },
33
+ "claude-opus-4": { rates: [claude(15, 1.50, 75)] },
34
+ "claude-sonnet-5-5": { rates: [claude(2, 0.10, 10)] },
35
+ "claude-sonnet-5": { rates: [claude(2, 0.20, 10)] },
36
+ "claude-sonnet-4-6": { rates: [claude(3, 0.30, 15)] },
37
+ "claude-sonnet-4-5": { rates: [claude(3, 0.30, 15)] },
38
+ "claude-sonnet-4": { rates: [claude(3, 0.30, 15)] },
39
+ "claude-haiku-5-5": { rates: [claude(0.10, 0.01, 0.50), claude(0.50, 0.05, 2.50)], longOver: 100_000 },
40
+ "claude-haiku-4-5": { rates: [claude(1, 0.10, 5)] },
41
+ "gpt-6.1-sol": { rates: [openai(2, 0.10, 2.50, 10), openai(4, 0.20, 5, 15)], longOver: 272_000 },
42
+ "gpt-6-sol": { rates: [openai(2, 0.20, 2.50, 10), openai(4, 0.40, 5, 15)], longOver: 272_000 },
43
+ "gpt-6-astra": { rates: [openai(10, 1, 12.50, 50), openai(20, 2, 25, 75)], longOver: 272_000 },
44
+ "gpt-6-luna": { rates: [openai(0.10, 0.01, 0.125, 0.50), openai(0.20, 0.02, 0.25, 0.75)], longOver: 272_000 },
45
+ "gpt-5.6-sol": { rates: [openai(4, 0.40, 5, 20), openai(8, 0.80, 10, 30)], longOver: 272_000 },
46
+ "gpt-5.6-terra": { rates: [openai(2, 0.20, 2.50, 12), openai(4, 0.40, 5, 18)], longOver: 272_000 },
47
+ "gpt-5.6-luna": { rates: [openai(0.20, 0.02, 0.25, 1.20), openai(0.40, 0.04, 0.50, 1.80)], longOver: 272_000 },
48
+ };
49
+ /**
50
+ * The table's key for a model id as a transcript records it: "claude-opus-4-5-20251101" → "claude-opus-4-5",
51
+ * "claude-opus-5-5[1m]" → "claude-opus-5-5". Exact keys only — "claude-opus-5" never prices "claude-opus-5-5".
52
+ */
53
+ export function priceKey(model) {
54
+ if (typeof model !== "string")
55
+ return null;
56
+ const id = model.trim().toLowerCase().replace(/\[[^\]]*\]$/, "").replace(/-\d{8}$/, "");
57
+ return Object.prototype.hasOwnProperty.call(TABLE, id) ? id : null;
58
+ }
59
+ /** The rates for one request of `model` with a prompt of `promptTokens`; null when the model is not in the table. */
60
+ export function ratesFor(model, promptTokens = 0) {
61
+ const key = priceKey(model);
62
+ if (!key)
63
+ return null;
64
+ const row = TABLE[key];
65
+ return row.longOver !== undefined && promptTokens > row.longOver && row.rates[1] ? row.rates[1] : row.rates[0];
66
+ }
67
+ /** Whether a prompt of this size pays the model's long-context (or over-100K) row. */
68
+ export function isLongPrompt(model, promptTokens) {
69
+ const key = priceKey(model);
70
+ const row = key ? TABLE[key] : undefined;
71
+ return !!row && row.longOver !== undefined && promptTokens > row.longOver;
72
+ }
73
+ //# sourceMappingURL=cache-prices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-prices.js","sourceRoot":"","sources":["../src/cache-prices.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ export interface ScanOptions {
2
+ maxBytes: number;
3
+ /** The inode the caller's cursor belongs to: if the descriptor opened is another file, nothing is read. */
4
+ ino?: number;
5
+ chunkBytes?: number;
6
+ wholeUpTo?: number;
7
+ headBytes?: number;
8
+ tailBytes?: number;
9
+ }
10
+ /** A whole line (`whole`), or a long one's two ends (`head`/`tail`). `length` is the line's size in bytes. */
11
+ export type ScanLine = {
12
+ whole: Buffer;
13
+ length: number;
14
+ } | {
15
+ head: Buffer;
16
+ tail: Buffer;
17
+ length: number;
18
+ };
19
+ export interface ScanResult {
20
+ /** Where the next pass starts: just after the last complete line read. */
21
+ offset: number;
22
+ /** The file's size when this pass started (of the descriptor actually opened). */
23
+ size: number;
24
+ /** Nothing complete is left before `size`. */
25
+ done: boolean;
26
+ /** Bytes committed (whole lines). */
27
+ bytes: number;
28
+ /** Bytes physically read — what a pass's budget is charged, unfinished line included. */
29
+ read: number;
30
+ /** The inode of the descriptor opened. */
31
+ ino: number;
32
+ /** `opts.ino` was given and the path now names another file: nothing was read. */
33
+ replaced?: true;
34
+ }
35
+ export declare const SCAN_CHUNK_BYTES: number;
36
+ export declare const SCAN_WHOLE_UP_TO: number;
37
+ export declare function scanJsonl(path: string, from: number, opts: ScanOptions, onLine: (line: ScanLine) => void): Promise<ScanResult>;
38
+ /** The JSON object that starts at `start` (an opening brace) in `text`, or null if it does not close there. */
39
+ export declare function objectAt(text: string, start: number): string | null;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * #1468: read a JSONL file from a byte offset, line by line, without ever holding a large line or blocking the
3
+ * fleet loop. Transcripts reach hundreds of MB, and a single line (a tool result) can be tens of MB:
4
+ * - every chunk read is awaited, so the event loop runs between chunks;
5
+ * - a line up to `wholeUpTo` bytes is handed over whole; a longer one only as its first `headBytes` and last
6
+ * `tailBytes` (a Claude assistant line has its model/id at the start and its usage/timestamp at the end);
7
+ * - a pass stops at the first line end once `maxBytes` have been read (physically: an unfinished line costs what it
8
+ * took to read) and returns the offset to resume from — always a line start. An unfinished last line (a file
9
+ * being appended to) is left for the next pass;
10
+ * - given the cursor's inode, a descriptor that turns out to be another file is not read at all.
11
+ */
12
+ import { open } from "node:fs/promises";
13
+ export const SCAN_CHUNK_BYTES = 256 * 1024;
14
+ export const SCAN_WHOLE_UP_TO = 1024 * 1024;
15
+ const HEAD_BYTES = 8 * 1024;
16
+ const TAIL_BYTES = 64 * 1024;
17
+ export async function scanJsonl(path, from, opts, onLine) {
18
+ const chunkBytes = opts.chunkBytes ?? SCAN_CHUNK_BYTES;
19
+ const wholeUpTo = opts.wholeUpTo ?? SCAN_WHOLE_UP_TO;
20
+ const headBytes = opts.headBytes ?? HEAD_BYTES;
21
+ const tailBytes = opts.tailBytes ?? TAIL_BYTES;
22
+ const fh = await open(path, "r");
23
+ try {
24
+ const st = await fh.stat();
25
+ const size = st.size, ino = st.ino;
26
+ // The file as opened, not as listed: a rename between the two must not feed another file's lines to this cursor.
27
+ if (opts.ino !== undefined && ino !== opts.ino)
28
+ return { offset: from, size, done: true, bytes: 0, read: 0, ino, replaced: true };
29
+ let pos = from, committed = from;
30
+ let parts = [], partLen = 0;
31
+ let big = false, head = Buffer.alloc(0), tail = Buffer.alloc(0), total = 0;
32
+ const append = (seg) => {
33
+ if (!big) {
34
+ if (partLen + seg.length <= wholeUpTo) {
35
+ parts.push(seg);
36
+ partLen += seg.length;
37
+ return;
38
+ }
39
+ const all = Buffer.concat([...parts, seg]);
40
+ head = Buffer.from(all.subarray(0, headBytes));
41
+ tail = Buffer.from(all.subarray(Math.max(0, all.length - tailBytes)));
42
+ total = all.length;
43
+ big = true;
44
+ parts = [];
45
+ partLen = 0;
46
+ return;
47
+ }
48
+ total += seg.length;
49
+ const joined = seg.length >= tailBytes ? seg : Buffer.concat([tail, seg]);
50
+ tail = Buffer.from(joined.subarray(Math.max(0, joined.length - tailBytes)));
51
+ };
52
+ const emit = () => {
53
+ if (big)
54
+ onLine({ head, tail, length: total });
55
+ else if (partLen > 0)
56
+ onLine({ whole: parts.length === 1 ? parts[0] : Buffer.concat(parts, partLen), length: partLen });
57
+ parts = [];
58
+ partLen = 0;
59
+ big = false;
60
+ head = Buffer.alloc(0);
61
+ tail = Buffer.alloc(0);
62
+ total = 0;
63
+ };
64
+ while (pos < size) {
65
+ const want = Math.min(chunkBytes, size - pos);
66
+ const chunk = Buffer.allocUnsafe(want);
67
+ const { bytesRead } = await fh.read(chunk, 0, want, pos);
68
+ if (bytesRead <= 0)
69
+ break;
70
+ let i = 0;
71
+ while (i < bytesRead) {
72
+ const nl = chunk.indexOf(10, i);
73
+ if (nl === -1 || nl >= bytesRead) {
74
+ append(chunk.subarray(i, bytesRead));
75
+ i = bytesRead;
76
+ break;
77
+ }
78
+ append(chunk.subarray(i, nl));
79
+ emit();
80
+ committed = pos + nl + 1;
81
+ i = nl + 1;
82
+ if (pos + bytesRead - from >= opts.maxBytes)
83
+ return { offset: committed, size, done: committed >= size, bytes: committed - from, read: pos + bytesRead - from, ino };
84
+ }
85
+ pos += bytesRead;
86
+ }
87
+ return { offset: committed, size, done: true, bytes: committed - from, read: pos - from, ino };
88
+ }
89
+ finally {
90
+ await fh.close();
91
+ }
92
+ }
93
+ /** The JSON object that starts at `start` (an opening brace) in `text`, or null if it does not close there. */
94
+ export function objectAt(text, start) {
95
+ if (text[start] !== "{")
96
+ return null;
97
+ let depth = 0, inString = false;
98
+ for (let i = start; i < text.length; i++) {
99
+ const c = text[i];
100
+ if (inString) {
101
+ if (c === "\\")
102
+ i++;
103
+ else if (c === "\"")
104
+ inString = false;
105
+ }
106
+ else if (c === "\"")
107
+ inString = true;
108
+ else if (c === "{")
109
+ depth++;
110
+ else if (c === "}" && --depth === 0)
111
+ return text.slice(start, i + 1);
112
+ }
113
+ return null;
114
+ }
115
+ //# sourceMappingURL=cache-scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-scan.js","sourceRoot":"","sources":["../src/cache-scan.ts"],"names":[],"mappings":"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"}