@omnicross/daemon 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -53,7 +53,7 @@ __export(src_exports, {
53
53
  module.exports = __toCommonJS(src_exports);
54
54
 
55
55
  // src/bootstrap.ts
56
- var import_node_fs23 = require("fs");
56
+ var import_node_fs26 = require("fs");
57
57
  var import_audit_types = require("@omnicross/contracts/audit-types");
58
58
  var import_billing_types = require("@omnicross/contracts/billing-types");
59
59
  var import_GeminiCodeAssistProjectResolver = require("@omnicross/core/auth/GeminiCodeAssistProjectResolver");
@@ -69,7 +69,7 @@ var import_gemini_code_assist_resolver = require("@omnicross/core/ports/gemini-c
69
69
  var import_provider_proxy4 = require("@omnicross/core/provider-proxy");
70
70
  var import_cli_launcher2 = require("@omnicross/cli-launcher");
71
71
  var import_outbound_api5 = require("@omnicross/core/outbound-api");
72
- var import_usage = require("@omnicross/core/usage");
72
+ var import_usage2 = require("@omnicross/core/usage");
73
73
  var import_subscriptions4 = require("@omnicross/subscriptions");
74
74
 
75
75
  // src/admin/accountsCodexOAuth.ts
@@ -650,6 +650,37 @@ function handleAuditQuery(req, res, reader) {
650
650
  res.writeHead(200, { "Content-Type": "application/json" });
651
651
  res.end(JSON.stringify({ records }));
652
652
  }
653
+ function handleAuditBodyQuery(req, res, reader) {
654
+ const url = new URL(req.url ?? "/", "http://localhost");
655
+ const id = url.searchParams.get("id")?.trim();
656
+ const sessionKey = url.searchParams.get("session")?.trim();
657
+ if (!id || !sessionKey) {
658
+ res.writeHead(400, { "Content-Type": "application/json" });
659
+ res.end(JSON.stringify({ error: "id and session are required" }));
660
+ return;
661
+ }
662
+ const query2 = { id, sessionKey };
663
+ const ts = intParam(url.searchParams.get("ts"));
664
+ if (ts !== void 0) query2.ts = ts;
665
+ const body = reader ? reader(query2) : {};
666
+ res.writeHead(200, { "Content-Type": "application/json" });
667
+ res.end(JSON.stringify(body));
668
+ }
669
+ function handleAuditCompact(res, compact) {
670
+ if (!compact) {
671
+ res.writeHead(200, { "Content-Type": "application/json" });
672
+ res.end(JSON.stringify({ days: 0, shards: 0, savedBytes: 0 }));
673
+ return;
674
+ }
675
+ try {
676
+ const result = compact();
677
+ res.writeHead(200, { "Content-Type": "application/json" });
678
+ res.end(JSON.stringify(result));
679
+ } catch (error) {
680
+ res.writeHead(500, { "Content-Type": "application/json" });
681
+ res.end(JSON.stringify({ error: error instanceof Error ? error.message : "compaction failed" }));
682
+ }
683
+ }
653
684
  async function handleAuditStatsQuery(req, res, reader) {
654
685
  const url = new URL(req.url ?? "/", "http://localhost");
655
686
  const query2 = {};
@@ -1264,6 +1295,35 @@ function validateApiKeys(raw) {
1264
1295
  }
1265
1296
  return out.length > 0 ? out : void 0;
1266
1297
  }
1298
+ var THINK_LEVELS = /* @__PURE__ */ new Set([
1299
+ "none",
1300
+ "minimal",
1301
+ "low",
1302
+ "medium",
1303
+ "high",
1304
+ "xhigh",
1305
+ "max"
1306
+ ]);
1307
+ function validateThinkingLevels(raw) {
1308
+ if (!Array.isArray(raw)) return void 0;
1309
+ if (!raw.every((level) => typeof level === "string" && THINK_LEVELS.has(level))) {
1310
+ return void 0;
1311
+ }
1312
+ return [...raw];
1313
+ }
1314
+ function validateThinkingTokenLimit(raw) {
1315
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
1316
+ const bounds = raw;
1317
+ const min = bounds["min"];
1318
+ const max = bounds["max"];
1319
+ if (typeof min !== "number" || !Number.isFinite(min) || !Number.isInteger(min) || min < 0) {
1320
+ return void 0;
1321
+ }
1322
+ if (typeof max !== "number" || !Number.isFinite(max) || !Number.isInteger(max) || max < min) {
1323
+ return void 0;
1324
+ }
1325
+ return { min, max };
1326
+ }
1267
1327
  function validateModelConfigs(raw) {
1268
1328
  if (!Array.isArray(raw)) return void 0;
1269
1329
  const out = [];
@@ -1278,6 +1338,10 @@ function validateModelConfigs(raw) {
1278
1338
  if (typeof m["enabled"] === "boolean") entry.enabled = m["enabled"];
1279
1339
  if (typeof m["vision"] === "boolean") entry.vision = m["vision"];
1280
1340
  if (typeof m["reasoning"] === "boolean") entry.reasoning = m["reasoning"];
1341
+ const thinkingLevels = validateThinkingLevels(m["thinkingLevels"]);
1342
+ if (thinkingLevels) entry.thinkingLevels = thinkingLevels;
1343
+ const thinkingTokenLimit = validateThinkingTokenLimit(m["thinkingTokenLimit"]);
1344
+ if (thinkingTokenLimit) entry.thinkingTokenLimit = thinkingTokenLimit;
1281
1345
  out.push(entry);
1282
1346
  }
1283
1347
  return out.length > 0 ? out : void 0;
@@ -3585,6 +3649,7 @@ function applyAuditConfig(config) {
3585
3649
  } else {
3586
3650
  (0, import_auditSink.setAuditCaptureConfig)(null);
3587
3651
  (0, import_auditSink.setAuditSink)(null);
3652
+ writer?.reset();
3588
3653
  if (sweeper) {
3589
3654
  if (config) sweeper.configure(config);
3590
3655
  sweeper.dispose();
@@ -3595,6 +3660,7 @@ function resetAuditRuntimeForTests() {
3595
3660
  (0, import_auditSink.setAuditCaptureConfig)(null);
3596
3661
  (0, import_auditSink.setAuditSink)(null);
3597
3662
  (0, import_upstreamTrace.setUpstreamTracePath)(null);
3663
+ writer?.reset();
3598
3664
  if (sweeper) sweeper.dispose();
3599
3665
  writer = null;
3600
3666
  sweeper = null;
@@ -4186,6 +4252,7 @@ async function handleImport(body, deps) {
4186
4252
  }
4187
4253
 
4188
4254
  // src/admin/usagePricing.ts
4255
+ var import_usage = require("@omnicross/core/usage");
4189
4256
  var err4 = (status, message) => ({
4190
4257
  status,
4191
4258
  body: { error: { type: "admin_api_error", message } }
@@ -4211,6 +4278,9 @@ var BUCKET_SPAN_MS = {
4211
4278
  };
4212
4279
  var MAX_TIMESERIES_BUCKETS = 2e3;
4213
4280
  async function handleUsageGet(view, query2, deps) {
4281
+ if (view === "throughput") {
4282
+ return { status: 200, body: (0, import_usage.getSharedUsageThroughputTracker)().snapshot() };
4283
+ }
4214
4284
  const range = parseRange(query2);
4215
4285
  if (!isRange(range)) return range;
4216
4286
  switch (view) {
@@ -5013,6 +5083,12 @@ function parseModelConfigsInput(raw, existing) {
5013
5083
  else if (typeof prior?.vision === "boolean") entry.vision = prior.vision;
5014
5084
  if (typeof m["reasoning"] === "boolean") entry.reasoning = m["reasoning"];
5015
5085
  else if (typeof prior?.reasoning === "boolean") entry.reasoning = prior.reasoning;
5086
+ const thinkingLevels = validateThinkingLevels(m["thinkingLevels"]);
5087
+ if (thinkingLevels) entry.thinkingLevels = thinkingLevels;
5088
+ else if (prior?.thinkingLevels) entry.thinkingLevels = prior.thinkingLevels;
5089
+ const thinkingTokenLimit = validateThinkingTokenLimit(m["thinkingTokenLimit"]);
5090
+ if (thinkingTokenLimit) entry.thinkingTokenLimit = thinkingTokenLimit;
5091
+ else if (prior?.thinkingTokenLimit) entry.thinkingTokenLimit = prior.thinkingTokenLimit;
5016
5092
  out.push(entry);
5017
5093
  }
5018
5094
  return out.length > 0 ? out : void 0;
@@ -5934,7 +6010,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
5934
6010
  }
5935
6011
 
5936
6012
  // src/admin/version.ts
5937
- var DAEMON_VERSION = true ? "0.1.8" : "0.0.0-dev";
6013
+ var DAEMON_VERSION = true ? "0.1.10" : "0.0.0-dev";
5938
6014
 
5939
6015
  // src/admin/AdminServer.ts
5940
6016
  var LOOPBACK_ADDR = "127.0.0.1";
@@ -6046,6 +6122,14 @@ var AdminServer = class {
6046
6122
  await handleAuditStatsQuery(req, res, this.deps.auditStatsReader);
6047
6123
  return;
6048
6124
  }
6125
+ if (path2 === "/admin/api/audit/body" && (req.method === "GET" || req.method === "HEAD")) {
6126
+ handleAuditBodyQuery(req, res, this.deps.auditBodyReader);
6127
+ return;
6128
+ }
6129
+ if (path2 === "/admin/api/audit/compact" && req.method === "POST") {
6130
+ handleAuditCompact(res, this.deps.auditCompactor);
6131
+ return;
6132
+ }
6049
6133
  if (path2 === "/admin/api/billing-status" && (req.method === "GET" || req.method === "HEAD")) {
6050
6134
  handleBillingStatus(res, this.deps.billingStatusReader);
6051
6135
  return;
@@ -6502,6 +6586,15 @@ function toLLMProvider(row) {
6502
6586
  api_base_url: row.baseUrl,
6503
6587
  api_key: resolvePreferredApiKey(row),
6504
6588
  models,
6589
+ modelConfigs: row.modelConfigs?.map((config) => ({
6590
+ id: config.id,
6591
+ name: config.name ?? config.id,
6592
+ enabled: config.enabled ?? true,
6593
+ vision: config.vision,
6594
+ reasoning: config.reasoning,
6595
+ thinkingLevels: config.thinkingLevels,
6596
+ thinkingTokenLimit: config.thinkingTokenLimit
6597
+ })),
6505
6598
  enabled: true,
6506
6599
  transformer,
6507
6600
  // app-parity-2 child 3: POPULATE the coding-plan endpoint onto the core
@@ -8778,18 +8871,214 @@ var AccountHealthSweeper = class {
8778
8871
  };
8779
8872
 
8780
8873
  // src/audit/AuditPruneSweeper.ts
8781
- var import_node_fs18 = require("fs");
8782
- var import_node_path11 = require("path");
8874
+ var import_node_fs19 = require("fs");
8875
+ var import_node_path12 = require("path");
8876
+ var import_promises2 = require("stream/promises");
8877
+ var import_node_zlib = require("zlib");
8878
+
8879
+ // src/audit/auditDictionary.ts
8880
+ var import_node_fs17 = require("fs");
8881
+ var import_node_path10 = require("path");
8882
+
8883
+ // src/audit/auditBodyStore.ts
8884
+ var ANCHOR_EVERY = 64;
8885
+ var ANCHOR_DELTA_RATIO = 0.75;
8886
+ var DIVERGED_PREFIX_RATIO = 0.25;
8887
+ var MAX_BASE_CHARS = 4e6;
8888
+ var CACHE_BUDGET_CHARS = 16e6;
8889
+ var CACHE_MAX_SESSIONS = 32;
8890
+ var MAX_BASES_PER_SESSION = 4;
8891
+ var isHighSurrogate = (code) => code >= 55296 && code <= 56319;
8892
+ var isLowSurrogate = (code) => code >= 56320 && code <= 57343;
8893
+ function computeBodyDelta(prev, next) {
8894
+ const shortest = Math.min(prev.length, next.length);
8895
+ let pre = 0;
8896
+ while (pre < shortest && prev.charCodeAt(pre) === next.charCodeAt(pre)) pre += 1;
8897
+ if (pre > 0 && isHighSurrogate(prev.charCodeAt(pre - 1))) pre -= 1;
8898
+ const maxSuf = shortest - pre;
8899
+ let suf = 0;
8900
+ while (suf < maxSuf && prev.charCodeAt(prev.length - 1 - suf) === next.charCodeAt(next.length - 1 - suf)) {
8901
+ suf += 1;
8902
+ }
8903
+ if (suf > 0 && isLowSurrogate(prev.charCodeAt(prev.length - suf))) suf -= 1;
8904
+ return { pre, suf, ins: next.slice(pre, next.length - suf) };
8905
+ }
8906
+ function applyBodyDelta(prev, delta) {
8907
+ const head = delta.pre > 0 ? prev.slice(0, delta.pre) : "";
8908
+ const tail = delta.suf > 0 ? prev.slice(prev.length - delta.suf) : "";
8909
+ return head + delta.ins + tail;
8910
+ }
8911
+ var SessionBaseCache = class {
8912
+ constructor(maxSessions = CACHE_MAX_SESSIONS, budgetChars = CACHE_BUDGET_CHARS, maxBaseChars = MAX_BASE_CHARS, maxHeads = MAX_BASES_PER_SESSION) {
8913
+ this.maxSessions = maxSessions;
8914
+ this.budgetChars = budgetChars;
8915
+ this.maxBaseChars = maxBaseChars;
8916
+ this.maxHeads = maxHeads;
8917
+ }
8918
+ maxSessions;
8919
+ budgetChars;
8920
+ maxBaseChars;
8921
+ maxHeads;
8922
+ /** Session key to its retained heads, most-recent first. */
8923
+ entries = /* @__PURE__ */ new Map();
8924
+ chars = 0;
8925
+ /** Retained sessions (tests + diagnostics). */
8926
+ get size() {
8927
+ return this.entries.size;
8928
+ }
8929
+ /** A session's retained heads, most-recent first. Refreshes LRU recency. */
8930
+ get(sessionKey) {
8931
+ const found = this.entries.get(sessionKey);
8932
+ if (!found) return [];
8933
+ this.entries.delete(sessionKey);
8934
+ this.entries.set(sessionKey, found);
8935
+ return found;
8936
+ }
8937
+ /**
8938
+ * Retain `base` as a head of `sessionKey`.
8939
+ *
8940
+ * `replacesId` is the head this turn CONTINUES (its body was preserved whole
8941
+ * inside the new one), which is swapped out so a linear conversation keeps
8942
+ * exactly one head. Omit it when the turn started a distinct stream %s that
8943
+ * head is added alongside, which is what keeps a fork's branches apart.
8944
+ *
8945
+ * A body larger than `maxBaseChars` is not retained: the next turn anchors
8946
+ * rather than letting one oversized session monopolize the budget.
8947
+ */
8948
+ remember(sessionKey, base, replacesId) {
8949
+ const heads = this.entries.get(sessionKey) ?? [];
8950
+ if (replacesId !== void 0) {
8951
+ const at = heads.findIndex((head) => head.lastId === replacesId);
8952
+ if (at >= 0) {
8953
+ this.chars -= heads[at].text.length;
8954
+ heads.splice(at, 1);
8955
+ }
8956
+ }
8957
+ if (base.text.length <= this.maxBaseChars) {
8958
+ heads.unshift(base);
8959
+ this.chars += base.text.length;
8960
+ }
8961
+ while (heads.length > this.maxHeads) {
8962
+ const dropped = heads.pop();
8963
+ if (dropped) this.chars -= dropped.text.length;
8964
+ }
8965
+ this.entries.delete(sessionKey);
8966
+ if (heads.length > 0) this.entries.set(sessionKey, heads);
8967
+ this.evict();
8968
+ }
8969
+ /** Drop a session's heads (eviction, or a write failure invalidating them). */
8970
+ forget(sessionKey) {
8971
+ const heads = this.entries.get(sessionKey);
8972
+ if (!heads) return;
8973
+ for (const head of heads) this.chars -= head.text.length;
8974
+ this.entries.delete(sessionKey);
8975
+ }
8976
+ /** Drop everything (writer disposal / test teardown). */
8977
+ clear() {
8978
+ this.entries.clear();
8979
+ this.chars = 0;
8980
+ }
8981
+ /** Evict least-recently-used sessions until both bounds hold. */
8982
+ evict() {
8983
+ while (this.entries.size > this.maxSessions || this.chars > this.budgetChars && this.entries.size > 1) {
8984
+ const oldest = this.entries.keys().next();
8985
+ if (oldest.done) break;
8986
+ this.forget(oldest.value);
8987
+ }
8988
+ }
8989
+ };
8990
+ function anchorReason(base, dayDir, delta, nextLength) {
8991
+ if (!base || !delta) return "new";
8992
+ if (base.dayDir !== dayDir) return "day";
8993
+ if (base.chainLen >= ANCHOR_EVERY) return "chain";
8994
+ if (delta.pre < base.text.length * DIVERGED_PREFIX_RATIO) return "diverged";
8995
+ if (delta.ins.length > nextLength * ANCHOR_DELTA_RATIO) return "costly";
8996
+ return null;
8997
+ }
8998
+ function pickBase(heads, next) {
8999
+ let best = null;
9000
+ for (const base of heads) {
9001
+ const delta = computeBodyDelta(base.text, next);
9002
+ if (best !== null && delta.ins.length >= best.delta.ins.length) continue;
9003
+ best = { base, delta, continues: delta.pre + delta.suf >= base.text.length };
9004
+ }
9005
+ return best;
9006
+ }
9007
+ function encodeBodyEntry(record, sessionKey, dayDir, cache) {
9008
+ const requestBody = record.requestBody;
9009
+ const responseBody = record.responseBody;
9010
+ if (requestBody === void 0 && responseBody === void 0) return null;
9011
+ const entry = { id: record.id, ts: record.ts };
9012
+ if (requestBody !== void 0) {
9013
+ const heads = cache.get(sessionKey);
9014
+ const sameDay = heads.filter((head) => head.dayDir === dayDir);
9015
+ const chosen = pickBase(sameDay, requestBody);
9016
+ const reason = heads.length > 0 && sameDay.length === 0 ? "day" : anchorReason(chosen?.base, dayDir, chosen?.delta ?? null, requestBody.length);
9017
+ if (reason !== null) {
9018
+ entry.req = { base: null, anchor: reason, pre: 0, suf: 0, ins: requestBody };
9019
+ cache.remember(
9020
+ sessionKey,
9021
+ { dayDir, lastId: record.id, text: requestBody, chainLen: 0 },
9022
+ chosen?.continues === true ? chosen.base.lastId : void 0
9023
+ );
9024
+ } else {
9025
+ const picked = chosen;
9026
+ entry.req = {
9027
+ base: picked.base.lastId,
9028
+ ...picked.continues ? { cont: true } : {},
9029
+ pre: picked.delta.pre,
9030
+ suf: picked.delta.suf,
9031
+ ins: picked.delta.ins
9032
+ };
9033
+ cache.remember(
9034
+ sessionKey,
9035
+ { dayDir, lastId: record.id, text: requestBody, chainLen: picked.base.chainLen + 1 },
9036
+ picked.continues ? picked.base.lastId : void 0
9037
+ );
9038
+ }
9039
+ }
9040
+ if (responseBody !== void 0) entry.res = responseBody;
9041
+ return JSON.stringify(entry);
9042
+ }
9043
+ function isAuditBodyEntry(value) {
9044
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
9045
+ const entry = value;
9046
+ if (typeof entry["id"] !== "string" || typeof entry["ts"] !== "number") return false;
9047
+ if (entry["res"] !== void 0 && typeof entry["res"] !== "string") return false;
9048
+ const req = entry["req"];
9049
+ if (req === void 0) return true;
9050
+ if (!req || typeof req !== "object" || Array.isArray(req)) return false;
9051
+ const delta = req;
9052
+ if (delta["anchor"] !== void 0 && typeof delta["anchor"] !== "string") return false;
9053
+ if (delta["cont"] !== void 0 && typeof delta["cont"] !== "boolean") return false;
9054
+ return (delta["base"] === null || typeof delta["base"] === "string") && Number.isSafeInteger(delta["pre"]) && delta["pre"] >= 0 && Number.isSafeInteger(delta["suf"]) && delta["suf"] >= 0 && typeof delta["ins"] === "string";
9055
+ }
8783
9056
 
8784
9057
  // src/audit/auditFiles.ts
8785
9058
  var AUDIT_FILE_RE = /^audit-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
9059
+ var AUDIT_DAY_DIR_RE = /^audit-(\d{4})-(\d{2})-(\d{2})$/;
9060
+ var AUDIT_META_FILE = "meta.jsonl";
9061
+ var AUDIT_BODIES_DIR = "bodies";
9062
+ var AUDIT_SESSION_KEY_RE = /^[0-9a-f]{8,64}$/;
8786
9063
  var pad22 = (n) => String(n).padStart(2, "0");
8787
- function auditFileName(ts) {
9064
+ var localDateStamp = (ts) => {
8788
9065
  const d = new Date(ts);
8789
- return `audit-${d.getFullYear()}-${pad22(d.getMonth() + 1)}-${pad22(d.getDate())}.jsonl`;
9066
+ return `${d.getFullYear()}-${pad22(d.getMonth() + 1)}-${pad22(d.getDate())}`;
9067
+ };
9068
+ function auditDayDirName(ts) {
9069
+ return `audit-${localDateStamp(ts)}`;
9070
+ }
9071
+ function isAuditDayDir(name) {
9072
+ return AUDIT_DAY_DIR_RE.test(name);
9073
+ }
9074
+ function isSafeSessionKey(key) {
9075
+ return typeof key === "string" && AUDIT_SESSION_KEY_RE.test(key);
9076
+ }
9077
+ function auditBodyFileName(sessionKey) {
9078
+ return `${sessionKey}.jsonl`;
8790
9079
  }
8791
- function auditFileDateMs(fileName) {
8792
- const m = AUDIT_FILE_RE.exec(fileName);
9080
+ function auditFileDateMs(name) {
9081
+ const m = AUDIT_FILE_RE.exec(name) ?? AUDIT_DAY_DIR_RE.exec(name);
8793
9082
  if (!m) return null;
8794
9083
  const year = Number(m[1]);
8795
9084
  const month = Number(m[2]);
@@ -8801,9 +9090,146 @@ function auditFileDateMs(fileName) {
8801
9090
  return d.getTime();
8802
9091
  }
8803
9092
 
9093
+ // src/audit/auditDictionary.ts
9094
+ var AUDIT_DICT_FILE = "_dict.jsonl";
9095
+ var DICT_BASE_PREFIX = "dict:";
9096
+ var DICT_CANDIDATES = 3;
9097
+ var MIN_SAVING_RATIO = 0.2;
9098
+ function parseEntries(raw) {
9099
+ const entries = [];
9100
+ for (const line of raw.split("\n")) {
9101
+ const trimmed = line.trim();
9102
+ if (!trimmed) continue;
9103
+ try {
9104
+ const parsed = JSON.parse(trimmed);
9105
+ if (isAuditBodyEntry(parsed)) entries.push(parsed);
9106
+ } catch {
9107
+ }
9108
+ }
9109
+ return entries;
9110
+ }
9111
+ function plainShards(bodiesPath) {
9112
+ try {
9113
+ return (0, import_node_fs17.readdirSync)(bodiesPath).filter(
9114
+ (file) => file.endsWith(".jsonl") && isSafeSessionKey(file.slice(0, -".jsonl".length))
9115
+ );
9116
+ } catch {
9117
+ return [];
9118
+ }
9119
+ }
9120
+ function chooseDictionary(anchors) {
9121
+ if (anchors.length < 2) return null;
9122
+ const total = anchors.reduce((sum, body) => sum + body.length, 0);
9123
+ const candidates = [...anchors].sort((a, b) => b.length - a.length).slice(0, DICT_CANDIDATES);
9124
+ let best = null;
9125
+ for (const candidate of candidates) {
9126
+ let cost = candidate.length;
9127
+ for (const body of anchors) {
9128
+ cost += body === candidate ? 0 : computeBodyDelta(candidate, body).ins.length;
9129
+ }
9130
+ if (best === null || cost < best.cost) best = { body: candidate, cost };
9131
+ }
9132
+ if (best === null) return null;
9133
+ return total - best.cost >= total * MIN_SAVING_RATIO ? best.body : null;
9134
+ }
9135
+ var EMPTY = { shards: 0, anchors: 0, savedBytes: 0 };
9136
+ function compactAuditDay(dayPath) {
9137
+ const bodiesPath = (0, import_node_path10.join)(dayPath, AUDIT_BODIES_DIR);
9138
+ if (!(0, import_node_fs17.existsSync)(bodiesPath)) return EMPTY;
9139
+ const dictPath = (0, import_node_path10.join)(bodiesPath, AUDIT_DICT_FILE);
9140
+ if ((0, import_node_fs17.existsSync)(dictPath) || (0, import_node_fs17.existsSync)(`${dictPath}.gz`)) return EMPTY;
9141
+ const shardFiles = plainShards(bodiesPath);
9142
+ if (shardFiles.length < 2) return EMPTY;
9143
+ const loaded = /* @__PURE__ */ new Map();
9144
+ const anchors = [];
9145
+ for (const file of shardFiles) {
9146
+ let entries;
9147
+ try {
9148
+ entries = parseEntries((0, import_node_fs17.readFileSync)((0, import_node_path10.join)(bodiesPath, file), "utf8"));
9149
+ } catch {
9150
+ continue;
9151
+ }
9152
+ loaded.set(file, entries);
9153
+ for (const entry of entries) {
9154
+ if (entry.req && entry.req.base === null) anchors.push(entry.req.ins);
9155
+ }
9156
+ }
9157
+ if (anchors.length < 2) return EMPTY;
9158
+ const dictionary = chooseDictionary(anchors);
9159
+ if (dictionary === null) return EMPTY;
9160
+ const dictEntry = {
9161
+ id: `${DICT_BASE_PREFIX}0`,
9162
+ ts: 0,
9163
+ req: { base: null, anchor: "dict", pre: 0, suf: 0, ins: dictionary }
9164
+ };
9165
+ (0, import_node_fs17.writeFileSync)(dictPath, JSON.stringify(dictEntry) + "\n", "utf8");
9166
+ const result = { shards: 0, anchors: 0, savedBytes: 0 };
9167
+ for (const [file, entries] of loaded) {
9168
+ let changed = false;
9169
+ let saved = 0;
9170
+ const rewritten = entries.map((entry) => {
9171
+ if (!entry.req || entry.req.base !== null || entry.req.ins === dictionary) return entry;
9172
+ const delta = computeBodyDelta(dictionary, entry.req.ins);
9173
+ if (delta.ins.length >= entry.req.ins.length) return entry;
9174
+ changed = true;
9175
+ saved += entry.req.ins.length - delta.ins.length;
9176
+ return {
9177
+ ...entry,
9178
+ req: { base: dictEntry.id, pre: delta.pre, suf: delta.suf, ins: delta.ins }
9179
+ };
9180
+ });
9181
+ if (!changed) continue;
9182
+ const target = (0, import_node_path10.join)(bodiesPath, file);
9183
+ const temp = `${target}.compacting`;
9184
+ try {
9185
+ (0, import_node_fs17.writeFileSync)(temp, rewritten.map((e) => JSON.stringify(e)).join("\n") + "\n", "utf8");
9186
+ (0, import_node_fs17.renameSync)(temp, target);
9187
+ } catch {
9188
+ try {
9189
+ if ((0, import_node_fs17.existsSync)(temp)) (0, import_node_fs17.unlinkSync)(temp);
9190
+ } catch {
9191
+ }
9192
+ continue;
9193
+ }
9194
+ result.shards += 1;
9195
+ result.anchors += rewritten.filter((e) => e.req?.base === dictEntry.id).length;
9196
+ result.savedBytes += saved;
9197
+ }
9198
+ if (result.shards === 0) {
9199
+ try {
9200
+ (0, import_node_fs17.unlinkSync)(dictPath);
9201
+ } catch {
9202
+ }
9203
+ }
9204
+ return result;
9205
+ }
9206
+ function compactAllClosedAuditDays(auditDir, now = Date.now) {
9207
+ const run = { days: 0, shards: 0, savedBytes: 0 };
9208
+ if (!(0, import_node_fs17.existsSync)(auditDir)) return run;
9209
+ const today = auditDayDirName(now());
9210
+ let names;
9211
+ try {
9212
+ names = (0, import_node_fs17.readdirSync)(auditDir).filter(isAuditDayDir).sort();
9213
+ } catch {
9214
+ return run;
9215
+ }
9216
+ for (const name of names) {
9217
+ if (name === today) continue;
9218
+ try {
9219
+ const result = compactAuditDay((0, import_node_path10.join)(auditDir, name));
9220
+ if (result.shards === 0) continue;
9221
+ run.days += 1;
9222
+ run.shards += result.shards;
9223
+ run.savedBytes += result.savedBytes;
9224
+ } catch {
9225
+ }
9226
+ }
9227
+ return run;
9228
+ }
9229
+
8804
9230
  // src/audit/auditStats.ts
8805
- var import_node_fs17 = require("fs");
8806
- var import_node_path10 = require("path");
9231
+ var import_node_fs18 = require("fs");
9232
+ var import_node_path11 = require("path");
8807
9233
  var SIDECAR_VERSION = 1;
8808
9234
  var META_PREFIX_BYTES = 64 * 1024;
8809
9235
  var READ_CHUNK_BYTES = 4 * 1024 * 1024;
@@ -8811,9 +9237,9 @@ function auditStatsFileName(auditFile) {
8811
9237
  return auditFile.replace(/\.jsonl$/, ".stats.json");
8812
9238
  }
8813
9239
  function readPersisted(path2) {
8814
- if (!(0, import_node_fs17.existsSync)(path2)) return null;
9240
+ if (!(0, import_node_fs18.existsSync)(path2)) return null;
8815
9241
  try {
8816
- const value = JSON.parse((0, import_node_fs17.readFileSync)(path2, "utf8"));
9242
+ const value = JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8"));
8817
9243
  if (value.version !== SIDECAR_VERSION || !Number.isSafeInteger(value.auditBytes) || (value.auditBytes ?? -1) < 0 || !Number.isSafeInteger(value.requestCount) || (value.requestCount ?? -1) < 0 || !Number.isSafeInteger(value.errorCount) || (value.errorCount ?? -1) < 0 || (value.errorCount ?? 0) > (value.requestCount ?? -1) || typeof value.complete !== "boolean" || value.minTs !== null && !Number.isFinite(value.minTs) || value.maxTs !== null && !Number.isFinite(value.maxTs)) {
8818
9244
  return null;
8819
9245
  }
@@ -8823,7 +9249,7 @@ function readPersisted(path2) {
8823
9249
  }
8824
9250
  }
8825
9251
  function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfter, record) {
8826
- const statsPath = (0, import_node_path10.join)((0, import_node_path10.dirname)(auditPath), auditStatsFileName((0, import_node_path10.basename)(auditPath)));
9252
+ const statsPath = (0, import_node_path11.join)((0, import_node_path11.dirname)(auditPath), auditStatsFileName((0, import_node_path11.basename)(auditPath)));
8827
9253
  const previous = auditBytesBefore === 0 ? {
8828
9254
  version: SIDECAR_VERSION,
8829
9255
  auditBytes: 0,
@@ -8843,13 +9269,13 @@ function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfte
8843
9269
  minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
8844
9270
  maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
8845
9271
  };
8846
- (0, import_node_fs17.writeFileSync)(statsPath, JSON.stringify(next), "utf8");
9272
+ (0, import_node_fs18.writeFileSync)(statsPath, JSON.stringify(next), "utf8");
8847
9273
  }
8848
9274
  function queryCovers(stats, from, to) {
8849
9275
  return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
8850
9276
  }
8851
- function fileOverlaps(file, from, to) {
8852
- const start = auditFileDateMs(file);
9277
+ function fileOverlaps(name, from, to) {
9278
+ const start = auditFileDateMs(name);
8853
9279
  if (start === null) return false;
8854
9280
  const date = new Date(start);
8855
9281
  const end = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1).getTime();
@@ -8901,7 +9327,7 @@ async function scanAuditFile(auditPath, startByte, auditBytes, from, to) {
8901
9327
  prefixTruncated = false;
8902
9328
  };
8903
9329
  if (auditBytes > startByte) {
8904
- const stream = (0, import_node_fs17.createReadStream)(auditPath, {
9330
+ const stream = (0, import_node_fs18.createReadStream)(auditPath, {
8905
9331
  start: startByte,
8906
9332
  end: auditBytes - 1,
8907
9333
  highWaterMark: READ_CHUNK_BYTES
@@ -8954,21 +9380,27 @@ function mergePersistedStats(previous, appended) {
8954
9380
  };
8955
9381
  }
8956
9382
  async function readAuditStats(auditDir, query2 = {}) {
8957
- if (!(0, import_node_fs17.existsSync)(auditDir)) return { requestCount: 0, errorCount: 0, complete: true };
9383
+ if (!(0, import_node_fs18.existsSync)(auditDir)) return { requestCount: 0, errorCount: 0, complete: true };
8958
9384
  const from = typeof query2.from === "number" ? query2.from : -Infinity;
8959
9385
  const to = typeof query2.to === "number" ? query2.to : Infinity;
8960
- let files;
9386
+ let sources;
8961
9387
  try {
8962
- files = (0, import_node_fs17.readdirSync)(auditDir).filter((file) => AUDIT_FILE_RE.test(file) && fileOverlaps(file, from, to)).sort();
9388
+ sources = (0, import_node_fs18.readdirSync)(auditDir).filter((name) => fileOverlaps(name, from, to)).sort().map(
9389
+ (name) => AUDIT_DAY_DIR_RE.test(name) ? {
9390
+ auditPath: (0, import_node_path11.join)(auditDir, name, AUDIT_META_FILE),
9391
+ statsPath: (0, import_node_path11.join)(auditDir, name, auditStatsFileName(AUDIT_META_FILE))
9392
+ } : {
9393
+ auditPath: (0, import_node_path11.join)(auditDir, name),
9394
+ statsPath: (0, import_node_path11.join)(auditDir, auditStatsFileName(name))
9395
+ }
9396
+ ).filter((source) => (0, import_node_fs18.existsSync)(source.auditPath));
8963
9397
  } catch {
8964
9398
  return { requestCount: 0, errorCount: 0, complete: false };
8965
9399
  }
8966
9400
  const total = { requestCount: 0, errorCount: 0, complete: true };
8967
- for (const file of files) {
8968
- const auditPath = (0, import_node_path10.join)(auditDir, file);
9401
+ for (const { auditPath, statsPath } of sources) {
8969
9402
  try {
8970
- const auditBytes = (0, import_node_fs17.statSync)(auditPath).size;
8971
- const statsPath = (0, import_node_path10.join)(auditDir, auditStatsFileName(file));
9403
+ const auditBytes = (0, import_node_fs18.statSync)(auditPath).size;
8972
9404
  const persisted = readPersisted(statsPath);
8973
9405
  if (persisted && persisted.complete && persisted.auditBytes === auditBytes && queryCovers(persisted, from, to)) {
8974
9406
  total.requestCount += persisted.requestCount;
@@ -8987,7 +9419,7 @@ async function readAuditStats(auditDir, query2 = {}) {
8987
9419
  total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
8988
9420
  total.complete = total.complete && scanned.filtered.complete;
8989
9421
  const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
8990
- if (current.complete) (0, import_node_fs17.writeFileSync)(statsPath, JSON.stringify(current), "utf8");
9422
+ if (current.complete) (0, import_node_fs18.writeFileSync)(statsPath, JSON.stringify(current), "utf8");
8991
9423
  } catch {
8992
9424
  total.complete = false;
8993
9425
  }
@@ -8998,6 +9430,7 @@ async function readAuditStats(auditDir, query2 = {}) {
8998
9430
  // src/audit/AuditPruneSweeper.ts
8999
9431
  var DAY_MS = 24 * 60 * 6e4;
9000
9432
  var SWEEP_INTERVAL_MS2 = 60 * 6e4;
9433
+ var ARCHIVE_BATCH = 64;
9001
9434
  var AuditPruneSweeper = class {
9002
9435
  constructor(auditDir, logger, config, intervalMs = SWEEP_INTERVAL_MS2, now = Date.now) {
9003
9436
  this.auditDir = auditDir;
@@ -9013,6 +9446,7 @@ var AuditPruneSweeper = class {
9013
9446
  now;
9014
9447
  timer = null;
9015
9448
  sweeping = false;
9449
+ archiving = false;
9016
9450
  /** Whether pruning is active (audit enabled). */
9017
9451
  get enabled() {
9018
9452
  return this.config.enabled;
@@ -9022,13 +9456,13 @@ var AuditPruneSweeper = class {
9022
9456
  this.config = config;
9023
9457
  }
9024
9458
  /**
9025
- * Arm the prune interval AND run one prune immediately (boot cleanup). No-op
9026
- * when audit is disabled (zero regression). Idempotent.
9459
+ * Arm the interval AND run one pass immediately (boot cleanup). No-op when
9460
+ * audit is disabled (zero regression). Idempotent.
9027
9461
  */
9028
9462
  start() {
9029
9463
  if (this.timer || !this.config.enabled) return;
9030
- void this.sweep();
9031
- this.timer = setInterval(() => void this.sweep(), this.intervalMs);
9464
+ void this.runOnce();
9465
+ this.timer = setInterval(() => void this.runOnce(), this.intervalMs);
9032
9466
  this.timer.unref?.();
9033
9467
  }
9034
9468
  /** Clear the interval (daemon shutdown / test teardown). Idempotent. */
@@ -9038,31 +9472,43 @@ var AuditPruneSweeper = class {
9038
9472
  this.timer = null;
9039
9473
  }
9040
9474
  }
9475
+ /** Prune first, then archive — never spend CPU compressing a day about to go. */
9476
+ async runOnce() {
9477
+ await this.sweep();
9478
+ await this.archive();
9479
+ }
9480
+ /** The LOCAL-midnight epoch ms of the current day. */
9481
+ todayMidnight() {
9482
+ const today = new Date(this.now());
9483
+ return new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
9484
+ }
9041
9485
  /**
9042
- * One prune: unlink every audit date file strictly OLDER than the retention
9043
- * cutoff (`now - retentionDays` days, at local-midnight granularity). Exposed
9044
- * for tests; never throws. Returns the number of files removed.
9486
+ * One prune: remove every audit day strictly OLDER than the retention cutoff
9487
+ * (`now - retentionDays` days, at local-midnight granularity). Exposed for
9488
+ * tests; never throws. Returns the number of days removed.
9045
9489
  */
9046
9490
  async sweep() {
9047
9491
  if (!this.config.enabled || this.sweeping) return 0;
9048
9492
  this.sweeping = true;
9049
9493
  try {
9050
- if (!(0, import_node_fs18.existsSync)(this.auditDir)) return 0;
9051
- const today = new Date(this.now());
9052
- const todayMidnight = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
9053
- const cutoff = todayMidnight - (this.config.retentionDays - 1) * DAY_MS;
9494
+ if (!(0, import_node_fs19.existsSync)(this.auditDir)) return 0;
9495
+ const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS;
9054
9496
  let removed = 0;
9055
- for (const file of (0, import_node_fs18.readdirSync)(this.auditDir)) {
9056
- const dateMs = auditFileDateMs(file);
9497
+ for (const name of (0, import_node_fs19.readdirSync)(this.auditDir)) {
9498
+ const dateMs = auditFileDateMs(name);
9057
9499
  if (dateMs === null || dateMs >= cutoff) continue;
9058
9500
  try {
9059
- (0, import_node_fs18.unlinkSync)((0, import_node_path11.join)(this.auditDir, file));
9501
+ if (isAuditDayDir(name)) {
9502
+ (0, import_node_fs19.rmSync)((0, import_node_path12.join)(this.auditDir, name), { recursive: true, force: true });
9503
+ } else {
9504
+ (0, import_node_fs19.unlinkSync)((0, import_node_path12.join)(this.auditDir, name));
9505
+ const statsPath = (0, import_node_path12.join)(this.auditDir, auditStatsFileName(name));
9506
+ if ((0, import_node_fs19.existsSync)(statsPath)) (0, import_node_fs19.unlinkSync)(statsPath);
9507
+ }
9060
9508
  removed += 1;
9061
- const statsPath = (0, import_node_path11.join)(this.auditDir, auditStatsFileName(file));
9062
- if ((0, import_node_fs18.existsSync)(statsPath)) (0, import_node_fs18.unlinkSync)(statsPath);
9063
9509
  } catch (error) {
9064
- this.logger.warn("[AuditPruneSweeper] failed to unlink expired audit file", {
9065
- file,
9510
+ this.logger.warn("[AuditPruneSweeper] failed to remove expired audit day", {
9511
+ name,
9066
9512
  error: error instanceof Error ? error.message : String(error)
9067
9513
  });
9068
9514
  }
@@ -9078,59 +9524,349 @@ var AuditPruneSweeper = class {
9078
9524
  this.sweeping = false;
9079
9525
  }
9080
9526
  }
9527
+ /**
9528
+ * Gzip the body shards of every CLOSED day (anything before today). Today is
9529
+ * deliberately left as plain text so it stays greppable while it is the day you
9530
+ * are debugging. Exposed for tests; never throws. Returns shards compressed.
9531
+ */
9532
+ async archive() {
9533
+ if (!this.config.enabled || this.archiving) return 0;
9534
+ this.archiving = true;
9535
+ try {
9536
+ if (!(0, import_node_fs19.existsSync)(this.auditDir)) return 0;
9537
+ const today = this.todayMidnight();
9538
+ let compressed = 0;
9539
+ for (const name of (0, import_node_fs19.readdirSync)(this.auditDir)) {
9540
+ if (compressed >= ARCHIVE_BATCH) break;
9541
+ const dateMs = auditFileDateMs(name);
9542
+ if (dateMs === null || dateMs >= today || !isAuditDayDir(name)) continue;
9543
+ const dayPath = (0, import_node_path12.join)(this.auditDir, name);
9544
+ try {
9545
+ const compaction = compactAuditDay(dayPath);
9546
+ if (compaction.shards > 0) {
9547
+ this.logger.debug("audit cross-session compaction complete", {
9548
+ day: name,
9549
+ shards: compaction.shards,
9550
+ anchors: compaction.anchors,
9551
+ savedBytes: compaction.savedBytes
9552
+ });
9553
+ }
9554
+ } catch (error) {
9555
+ this.logger.warn("[AuditPruneSweeper] cross-session compaction failed", {
9556
+ day: name,
9557
+ error: error instanceof Error ? error.message : String(error)
9558
+ });
9559
+ }
9560
+ compressed += await this.archiveDay(
9561
+ (0, import_node_path12.join)(dayPath, AUDIT_BODIES_DIR),
9562
+ ARCHIVE_BATCH - compressed
9563
+ );
9564
+ }
9565
+ if (compressed > 0) this.logger.debug("audit archive complete", { compressed });
9566
+ return compressed;
9567
+ } catch (error) {
9568
+ this.logger.warn("audit archive pass failed", {
9569
+ error: error instanceof Error ? error.message : String(error)
9570
+ });
9571
+ return 0;
9572
+ } finally {
9573
+ this.archiving = false;
9574
+ }
9575
+ }
9576
+ /** Gzip up to `budget` plain shards in one day's `bodies/` directory. */
9577
+ async archiveDay(bodiesPath, budget) {
9578
+ let shards;
9579
+ try {
9580
+ shards = (0, import_node_fs19.readdirSync)(bodiesPath).filter((file) => file.endsWith(".jsonl"));
9581
+ } catch {
9582
+ return 0;
9583
+ }
9584
+ let compressed = 0;
9585
+ for (const shard of shards) {
9586
+ if (compressed >= budget) break;
9587
+ const source = (0, import_node_path12.join)(bodiesPath, shard);
9588
+ const target = `${source}.gz`;
9589
+ try {
9590
+ if ((0, import_node_fs19.existsSync)(target)) {
9591
+ (0, import_node_fs19.unlinkSync)(source);
9592
+ continue;
9593
+ }
9594
+ await (0, import_promises2.pipeline)((0, import_node_fs19.createReadStream)(source), (0, import_node_zlib.createGzip)(), (0, import_node_fs19.createWriteStream)(target));
9595
+ (0, import_node_fs19.unlinkSync)(source);
9596
+ compressed += 1;
9597
+ } catch (error) {
9598
+ try {
9599
+ if ((0, import_node_fs19.existsSync)(target)) (0, import_node_fs19.unlinkSync)(target);
9600
+ } catch {
9601
+ }
9602
+ this.logger.warn("[AuditPruneSweeper] failed to archive audit body shard", {
9603
+ shard,
9604
+ error: error instanceof Error ? error.message : String(error)
9605
+ });
9606
+ }
9607
+ }
9608
+ return compressed;
9609
+ }
9081
9610
  };
9082
9611
 
9083
- // src/audit/auditReader.ts
9084
- var import_node_fs19 = require("fs");
9085
- var import_node_path12 = require("path");
9086
- var DEFAULT_LIMIT = 200;
9087
- var MAX_LIMIT = 2e3;
9088
- function readAuditRecords(auditDir, query2 = {}) {
9089
- if (!(0, import_node_fs19.existsSync)(auditDir)) return [];
9090
- let files;
9612
+ // src/audit/auditBodyReader.ts
9613
+ var import_node_fs21 = require("fs");
9614
+ var import_node_path13 = require("path");
9615
+ var import_node_zlib2 = require("zlib");
9616
+
9617
+ // src/audit/auditJsonl.ts
9618
+ var import_node_fs20 = require("fs");
9619
+ var WINDOW_BYTES = 1 << 20;
9620
+ var MAX_LINE_BYTES = 32 * 1024 * 1024;
9621
+ var NEWLINE = 10;
9622
+ function forEachLineFromTail(path2, onLine) {
9623
+ let fd;
9624
+ let end;
9091
9625
  try {
9092
- files = (0, import_node_fs19.readdirSync)(auditDir).filter((f) => AUDIT_FILE_RE.test(f));
9626
+ end = (0, import_node_fs20.statSync)(path2).size;
9627
+ if (end === 0) return;
9628
+ fd = (0, import_node_fs20.openSync)(path2, "r");
9629
+ } catch {
9630
+ return;
9631
+ }
9632
+ try {
9633
+ let carry = Buffer.alloc(0);
9634
+ while (end > 0) {
9635
+ const start = Math.max(0, end - WINDOW_BYTES);
9636
+ const window = Buffer.allocUnsafe(end - start);
9637
+ let read;
9638
+ try {
9639
+ read = (0, import_node_fs20.readSync)(fd, window, 0, end - start, start);
9640
+ } catch {
9641
+ return;
9642
+ }
9643
+ const chunk = carry.length > 0 ? Buffer.concat([window.subarray(0, read), carry]) : window.subarray(0, read);
9644
+ let lineEnd = chunk.length;
9645
+ let nl = lineEnd > 0 ? chunk.lastIndexOf(NEWLINE, lineEnd - 1) : -1;
9646
+ while (nl >= 0) {
9647
+ if (nl + 1 < lineEnd) {
9648
+ const line = chunk.subarray(nl + 1, lineEnd).toString("utf8").trim();
9649
+ if (line && onLine(line)) return;
9650
+ }
9651
+ lineEnd = nl;
9652
+ nl = lineEnd > 0 ? chunk.lastIndexOf(NEWLINE, lineEnd - 1) : -1;
9653
+ }
9654
+ if (start === 0) {
9655
+ if (lineEnd > 0) {
9656
+ const line = chunk.subarray(0, lineEnd).toString("utf8").trim();
9657
+ if (line) onLine(line);
9658
+ }
9659
+ return;
9660
+ }
9661
+ if (lineEnd > MAX_LINE_BYTES) return;
9662
+ carry = Buffer.from(chunk.subarray(0, lineEnd));
9663
+ end = start;
9664
+ }
9665
+ } finally {
9666
+ try {
9667
+ (0, import_node_fs20.closeSync)(fd);
9668
+ } catch {
9669
+ }
9670
+ }
9671
+ }
9672
+
9673
+ // src/audit/auditBodyReader.ts
9674
+ function candidateDays(auditDir, ts) {
9675
+ if (typeof ts === "number" && Number.isFinite(ts)) {
9676
+ const named = auditDayDirName(ts);
9677
+ if ((0, import_node_fs21.existsSync)((0, import_node_path13.join)(auditDir, named))) return [named];
9678
+ }
9679
+ try {
9680
+ return (0, import_node_fs21.readdirSync)(auditDir).filter(isAuditDayDir).sort().reverse();
9093
9681
  } catch {
9094
9682
  return [];
9095
9683
  }
9096
- const from = typeof query2.from === "number" ? query2.from : -Infinity;
9097
- const to = typeof query2.to === "number" ? query2.to : Infinity;
9098
- const limit = Math.min(MAX_LIMIT, Math.max(1, Math.trunc(query2.limit ?? DEFAULT_LIMIT)));
9099
- const matched = [];
9100
- for (const file of files.sort().reverse()) {
9101
- let raw;
9684
+ }
9685
+ function readShard(auditDir, day, sessionKey) {
9686
+ const base = (0, import_node_path13.join)(auditDir, day, AUDIT_BODIES_DIR, auditBodyFileName(sessionKey));
9687
+ try {
9688
+ if ((0, import_node_fs21.existsSync)(base)) return (0, import_node_fs21.readFileSync)(base, "utf8");
9689
+ const gz = `${base}.gz`;
9690
+ if ((0, import_node_fs21.existsSync)(gz)) return (0, import_node_zlib2.gunzipSync)((0, import_node_fs21.readFileSync)(gz)).toString("utf8");
9691
+ } catch {
9692
+ return null;
9693
+ }
9694
+ return null;
9695
+ }
9696
+ function parseShard(raw) {
9697
+ const entries = /* @__PURE__ */ new Map();
9698
+ for (const line of raw.split("\n")) {
9699
+ const trimmed = line.trim();
9700
+ if (!trimmed) continue;
9701
+ let parsed;
9102
9702
  try {
9103
- raw = (0, import_node_fs19.readFileSync)((0, import_node_path12.join)(auditDir, file), "utf8");
9703
+ parsed = JSON.parse(trimmed);
9104
9704
  } catch {
9105
9705
  continue;
9106
9706
  }
9107
- for (const line of raw.split("\n")) {
9108
- const trimmed = line.trim();
9109
- if (!trimmed) continue;
9110
- let rec;
9707
+ if (isAuditBodyEntry(parsed)) entries.set(parsed.id, parsed);
9708
+ }
9709
+ return entries;
9710
+ }
9711
+ function withDictionary(auditDir, day, entries) {
9712
+ let needed = false;
9713
+ for (const entry of entries.values()) {
9714
+ if (entry.req?.base?.startsWith(DICT_BASE_PREFIX)) {
9715
+ needed = true;
9716
+ break;
9717
+ }
9718
+ }
9719
+ if (!needed) return entries;
9720
+ const base = (0, import_node_path13.join)(auditDir, day, AUDIT_BODIES_DIR, AUDIT_DICT_FILE);
9721
+ let raw = null;
9722
+ try {
9723
+ if ((0, import_node_fs21.existsSync)(base)) raw = (0, import_node_fs21.readFileSync)(base, "utf8");
9724
+ else if ((0, import_node_fs21.existsSync)(`${base}.gz`)) raw = (0, import_node_zlib2.gunzipSync)((0, import_node_fs21.readFileSync)(`${base}.gz`)).toString("utf8");
9725
+ } catch {
9726
+ return entries;
9727
+ }
9728
+ if (raw === null) return entries;
9729
+ for (const [id, entry] of parseShard(raw)) entries.set(id, entry);
9730
+ return entries;
9731
+ }
9732
+ function reconstructRequest(entries, entry) {
9733
+ if (!entry.req) return void 0;
9734
+ const chain = [];
9735
+ const visited = /* @__PURE__ */ new Set();
9736
+ let cursor = entry;
9737
+ while (cursor?.req) {
9738
+ if (visited.has(cursor.id)) return void 0;
9739
+ visited.add(cursor.id);
9740
+ chain.push(cursor);
9741
+ if (cursor.req.base === null) break;
9742
+ cursor = entries.get(cursor.req.base);
9743
+ }
9744
+ const anchor = chain[chain.length - 1];
9745
+ if (!anchor?.req || anchor.req.base !== null) return void 0;
9746
+ let text = anchor.req.ins;
9747
+ for (let i = chain.length - 2; i >= 0; i -= 1) {
9748
+ const delta = chain[i]?.req;
9749
+ if (!delta) return void 0;
9750
+ if (delta.pre > text.length || delta.suf > text.length - delta.pre) return void 0;
9751
+ text = applyBodyDelta(text, delta);
9752
+ }
9753
+ return text;
9754
+ }
9755
+ function readAuditBody(auditDir, query2) {
9756
+ if (!isSafeSessionKey(query2.sessionKey) || !query2.id) return {};
9757
+ if (!(0, import_node_fs21.existsSync)(auditDir)) return {};
9758
+ for (const day of candidateDays(auditDir, query2.ts)) {
9759
+ const raw = readShard(auditDir, day, query2.sessionKey);
9760
+ if (raw === null) continue;
9761
+ const entries = withDictionary(auditDir, day, parseShard(raw));
9762
+ const entry = entries.get(query2.id);
9763
+ if (!entry) continue;
9764
+ const result = {};
9765
+ const requestBody = reconstructRequest(entries, entry);
9766
+ if (requestBody !== void 0) result.requestBody = requestBody;
9767
+ if (entry.res !== void 0) result.responseBody = entry.res;
9768
+ return result;
9769
+ }
9770
+ return readLegacyInlineBody(auditDir, query2.id);
9771
+ }
9772
+ function readLegacyInlineBody(auditDir, id) {
9773
+ let names;
9774
+ try {
9775
+ names = (0, import_node_fs21.readdirSync)(auditDir).filter((name) => AUDIT_FILE_RE.test(name)).sort().reverse();
9776
+ } catch {
9777
+ return {};
9778
+ }
9779
+ const needle = JSON.stringify(id);
9780
+ let found = {};
9781
+ for (const name of names) {
9782
+ forEachLineFromTail((0, import_node_path13.join)(auditDir, name), (line) => {
9783
+ if (!line.includes(needle)) return false;
9784
+ let parsed;
9111
9785
  try {
9112
- rec = JSON.parse(trimmed);
9786
+ parsed = JSON.parse(line);
9113
9787
  } catch {
9114
- continue;
9788
+ return false;
9115
9789
  }
9116
- if (!isAuditRecord(rec)) continue;
9117
- if (query2.keyId !== void 0 && rec.keyId !== query2.keyId) continue;
9118
- if (rec.ts < from || rec.ts > to) continue;
9119
- matched.push(rec);
9790
+ const record = parsed;
9791
+ if (record.id !== id) return false;
9792
+ const result = {};
9793
+ if (typeof record.requestBody === "string") result.requestBody = record.requestBody;
9794
+ if (typeof record.responseBody === "string") result.responseBody = record.responseBody;
9795
+ found = result;
9796
+ return true;
9797
+ });
9798
+ if (found.requestBody !== void 0 || found.responseBody !== void 0) break;
9799
+ }
9800
+ return found;
9801
+ }
9802
+
9803
+ // src/audit/auditReader.ts
9804
+ var import_node_fs22 = require("fs");
9805
+ var import_node_path14 = require("path");
9806
+ var DEFAULT_LIMIT = 200;
9807
+ var MAX_LIMIT = 2e3;
9808
+ var OVERSCAN = 256;
9809
+ function daySources(auditDir) {
9810
+ let names;
9811
+ try {
9812
+ names = (0, import_node_fs22.readdirSync)(auditDir);
9813
+ } catch {
9814
+ return [];
9815
+ }
9816
+ const sources = [];
9817
+ for (const name of names) {
9818
+ const dateMs = auditFileDateMs(name);
9819
+ if (dateMs === null) continue;
9820
+ if (AUDIT_DAY_DIR_RE.test(name)) {
9821
+ const path2 = (0, import_node_path14.join)(auditDir, name, AUDIT_META_FILE);
9822
+ if ((0, import_node_fs22.existsSync)(path2)) sources.push({ path: path2, dateMs });
9823
+ } else if (AUDIT_FILE_RE.test(name)) {
9824
+ sources.push({ path: (0, import_node_path14.join)(auditDir, name), dateMs });
9120
9825
  }
9121
9826
  }
9122
- matched.sort((a, b) => b.ts - a.ts);
9123
- return matched.slice(0, limit);
9827
+ return sources.sort((a, b) => b.dateMs - a.dateMs);
9124
9828
  }
9125
9829
  function isAuditRecord(value) {
9126
9830
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
9127
9831
  const r = value;
9128
9832
  return typeof r["id"] === "string" && typeof r["ts"] === "number" && typeof r["method"] === "string" && typeof r["path"] === "string" && typeof r["status"] === "number";
9129
9833
  }
9834
+ function toMetaRecord(record) {
9835
+ if (record.requestBody === void 0 && record.responseBody === void 0) return record;
9836
+ const { requestBody: _req, responseBody: _res, ...meta } = record;
9837
+ return { ...meta, hasBody: true };
9838
+ }
9839
+ function readAuditRecords(auditDir, query2 = {}) {
9840
+ if (!(0, import_node_fs22.existsSync)(auditDir)) return [];
9841
+ const from = typeof query2.from === "number" ? query2.from : -Infinity;
9842
+ const to = typeof query2.to === "number" ? query2.to : Infinity;
9843
+ const limit = Math.min(MAX_LIMIT, Math.max(1, Math.trunc(query2.limit ?? DEFAULT_LIMIT)));
9844
+ const matched = [];
9845
+ for (const source of daySources(auditDir)) {
9846
+ const before = matched.length;
9847
+ forEachLineFromTail(source.path, (line) => {
9848
+ let parsed;
9849
+ try {
9850
+ parsed = JSON.parse(line);
9851
+ } catch {
9852
+ return false;
9853
+ }
9854
+ if (!isAuditRecord(parsed)) return false;
9855
+ if (query2.keyId !== void 0 && parsed.keyId !== query2.keyId) return false;
9856
+ if (query2.sessionKey !== void 0 && parsed.sessionKey !== query2.sessionKey) return false;
9857
+ if (parsed.ts < from || parsed.ts > to) return false;
9858
+ matched.push(toMetaRecord(parsed));
9859
+ return matched.length - before >= limit + OVERSCAN;
9860
+ });
9861
+ if (matched.length >= limit) break;
9862
+ }
9863
+ matched.sort((a, b) => b.ts - a.ts);
9864
+ return matched.slice(0, limit);
9865
+ }
9130
9866
 
9131
9867
  // src/audit/AuditWriter.ts
9132
- var import_node_fs20 = require("fs");
9133
- var import_node_path13 = require("path");
9868
+ var import_node_fs23 = require("fs");
9869
+ var import_node_path15 = require("path");
9134
9870
  var AuditWriter = class {
9135
9871
  constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
9136
9872
  this.auditDir = auditDir;
@@ -9140,10 +9876,13 @@ var AuditWriter = class {
9140
9876
  auditDir;
9141
9877
  logger;
9142
9878
  defer;
9143
- dirEnsured = false;
9879
+ /** Day directories already created this process (avoids an mkdir per record). */
9880
+ ensuredDirs = /* @__PURE__ */ new Set();
9881
+ /** Per-session encoding bases. Memory-only; a miss simply writes a full snapshot. */
9882
+ bases = new SessionBaseCache();
9144
9883
  /**
9145
- * Enqueue one record for append. Returns IMMEDIATELY (fire-and-forget); the fs
9146
- * write happens on the deferred tick. A failure is logged, never thrown.
9884
+ * Enqueue one record. Returns IMMEDIATELY (fire-and-forget); every fs write and
9885
+ * the delta encoding happen on the deferred tick. A failure is logged, never thrown.
9147
9886
  */
9148
9887
  record(record) {
9149
9888
  this.defer(() => {
@@ -9156,25 +9895,41 @@ var AuditWriter = class {
9156
9895
  }
9157
9896
  });
9158
9897
  }
9898
+ /** Drop all retained encoding bases (config reload / shutdown / test teardown). */
9899
+ reset() {
9900
+ this.bases.clear();
9901
+ this.ensuredDirs.clear();
9902
+ }
9159
9903
  /**
9160
- * Append synchronously — the awaitable form tests use to assert the line landed.
9161
- * Ensures the `audit/` directory exists on first write (lazy, like the usage
9162
- * store's lazy file creation).
9904
+ * Append synchronously — the awaitable form tests use to assert a line landed.
9905
+ * Writes the metadata line first (canonical), then the body shard.
9163
9906
  */
9164
9907
  appendNow(record) {
9165
- if (!this.dirEnsured) {
9166
- (0, import_node_fs20.mkdirSync)(this.auditDir, { recursive: true });
9167
- this.dirEnsured = true;
9168
- }
9169
- const file = (0, import_node_path13.join)(this.auditDir, auditFileName(record.ts));
9170
- const line = JSON.stringify(record) + "\n";
9171
- const auditBytesBefore = (0, import_node_fs20.existsSync)(file) ? (0, import_node_fs20.statSync)(file).size : 0;
9172
- (0, import_node_fs20.appendFileSync)(file, line, "utf8");
9908
+ const dayDir = auditDayDirName(record.ts);
9909
+ const dayPath = this.ensureDir((0, import_node_path15.join)(this.auditDir, dayDir));
9910
+ this.appendMeta(dayPath, record);
9911
+ this.appendBody(dayPath, dayDir, record);
9912
+ }
9913
+ /** Create a directory once per process and remember it. */
9914
+ ensureDir(path2) {
9915
+ if (!this.ensuredDirs.has(path2)) {
9916
+ (0, import_node_fs23.mkdirSync)(path2, { recursive: true });
9917
+ this.ensuredDirs.add(path2);
9918
+ }
9919
+ return path2;
9920
+ }
9921
+ /** Write the body-free metadata line + refresh the exact-count sidecar. */
9922
+ appendMeta(dayPath, record) {
9923
+ const { requestBody: _req, responseBody: _res, ...meta } = record;
9924
+ const file = (0, import_node_path15.join)(dayPath, AUDIT_META_FILE);
9925
+ const line = JSON.stringify(meta) + "\n";
9926
+ const bytesBefore = (0, import_node_fs23.existsSync)(file) ? (0, import_node_fs23.statSync)(file).size : 0;
9927
+ (0, import_node_fs23.appendFileSync)(file, line, "utf8");
9173
9928
  try {
9174
9929
  updateAuditStatsAfterAppend(
9175
9930
  file,
9176
- auditBytesBefore,
9177
- auditBytesBefore + Buffer.byteLength(line, "utf8"),
9931
+ bytesBefore,
9932
+ bytesBefore + Buffer.byteLength(line, "utf8"),
9178
9933
  record
9179
9934
  );
9180
9935
  } catch (error) {
@@ -9183,12 +9938,39 @@ var AuditWriter = class {
9183
9938
  });
9184
9939
  }
9185
9940
  }
9941
+ /**
9942
+ * Write the delta-encoded body shard for one record. A no-op when nothing was
9943
+ * captured or when the session key is missing/unsafe — in which case the body
9944
+ * is dropped rather than written to an unvalidated path.
9945
+ */
9946
+ appendBody(dayPath, dayDir, record) {
9947
+ if (record.requestBody === void 0 && record.responseBody === void 0) return;
9948
+ const sessionKey = record.sessionKey;
9949
+ if (!isSafeSessionKey(sessionKey)) {
9950
+ this.logger.warn("[AuditWriter] dropping audit body with no usable session key", {
9951
+ id: record.id
9952
+ });
9953
+ return;
9954
+ }
9955
+ try {
9956
+ const line = encodeBodyEntry(record, sessionKey, dayDir, this.bases);
9957
+ if (line === null) return;
9958
+ const bodiesPath = this.ensureDir((0, import_node_path15.join)(dayPath, AUDIT_BODIES_DIR));
9959
+ (0, import_node_fs23.appendFileSync)((0, import_node_path15.join)(bodiesPath, auditBodyFileName(sessionKey)), line + "\n", "utf8");
9960
+ } catch (error) {
9961
+ this.bases.forget(sessionKey);
9962
+ this.logger.warn("[AuditWriter] failed to append audit body shard", {
9963
+ id: record.id,
9964
+ error: error instanceof Error ? error.message : String(error)
9965
+ });
9966
+ }
9967
+ }
9186
9968
  };
9187
9969
 
9188
9970
  // src/billing/BillingPublisher.ts
9189
- var import_node_fs21 = require("fs");
9971
+ var import_node_fs24 = require("fs");
9190
9972
  var import_node_crypto13 = require("crypto");
9191
- var import_node_path14 = require("path");
9973
+ var import_node_path16 = require("path");
9192
9974
  var import_upstreamFetch6 = require("@omnicross/core/pipeline/upstreamFetch");
9193
9975
 
9194
9976
  // src/billing/billingFiles.ts
@@ -9259,8 +10041,8 @@ var BillingPublisher = class {
9259
10041
  */
9260
10042
  appendNow(event) {
9261
10043
  this.ensureDir();
9262
- const file = (0, import_node_path14.join)(this.billingDir, billingFileName(event.ts));
9263
- (0, import_node_fs21.appendFileSync)(file, JSON.stringify(event) + "\n", "utf8");
10044
+ const file = (0, import_node_path16.join)(this.billingDir, billingFileName(event.ts));
10045
+ (0, import_node_fs24.appendFileSync)(file, JSON.stringify(event) + "\n", "utf8");
9264
10046
  }
9265
10047
  /**
9266
10048
  * One best-effort delivery attempt for an event ALREADY in the ledger. POSTs the
@@ -9309,8 +10091,8 @@ var BillingPublisher = class {
9309
10091
  markDelivered(event) {
9310
10092
  try {
9311
10093
  this.ensureDir();
9312
- const file = (0, import_node_path14.join)(this.billingDir, deliveredFileName(event.ts));
9313
- (0, import_node_fs21.appendFileSync)(file, JSON.stringify({ id: event.id, deliveredAt: this.now() }) + "\n", "utf8");
10094
+ const file = (0, import_node_path16.join)(this.billingDir, deliveredFileName(event.ts));
10095
+ (0, import_node_fs24.appendFileSync)(file, JSON.stringify({ id: event.id, deliveredAt: this.now() }) + "\n", "utf8");
9314
10096
  } catch (error) {
9315
10097
  this.logger.warn("[BillingPublisher] failed to append delivery marker", {
9316
10098
  error: error instanceof Error ? error.message : String(error)
@@ -9319,20 +10101,20 @@ var BillingPublisher = class {
9319
10101
  }
9320
10102
  ensureDir() {
9321
10103
  if (this.dirEnsured) return;
9322
- (0, import_node_fs21.mkdirSync)(this.billingDir, { recursive: true });
10104
+ (0, import_node_fs24.mkdirSync)(this.billingDir, { recursive: true });
9323
10105
  this.dirEnsured = true;
9324
10106
  }
9325
10107
  };
9326
10108
 
9327
10109
  // src/billing/billingReader.ts
9328
- var import_node_fs22 = require("fs");
9329
- var import_node_path15 = require("path");
10110
+ var import_node_fs25 = require("fs");
10111
+ var import_node_path17 = require("path");
9330
10112
  function readBillingLedger(billingDir) {
9331
10113
  const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
9332
- if (!(0, import_node_fs22.existsSync)(billingDir)) return view;
10114
+ if (!(0, import_node_fs25.existsSync)(billingDir)) return view;
9333
10115
  let files;
9334
10116
  try {
9335
- files = (0, import_node_fs22.readdirSync)(billingDir);
10117
+ files = (0, import_node_fs25.readdirSync)(billingDir);
9336
10118
  } catch {
9337
10119
  return view;
9338
10120
  }
@@ -9363,7 +10145,7 @@ function readBillingStatus(billingDir) {
9363
10145
  function parseLines(dir, file) {
9364
10146
  let raw;
9365
10147
  try {
9366
- raw = (0, import_node_fs22.readFileSync)((0, import_node_path15.join)(dir, file), "utf8");
10148
+ raw = (0, import_node_fs25.readFileSync)((0, import_node_path17.join)(dir, file), "utf8");
9367
10149
  } catch {
9368
10150
  return [];
9369
10151
  }
@@ -9849,7 +10631,7 @@ function buildDaemon(config, paths) {
9849
10631
  }
9850
10632
  );
9851
10633
  const pricingStore = new JsonPricingStore(defaultPricingPath(paths.configPath));
9852
- const pricingEngine = new import_usage.PricingEngine(pricingStore, logger, {
10634
+ const pricingEngine = new import_usage2.PricingEngine(pricingStore, logger, {
9853
10635
  // Catalog egress follows the same global/env proxy policy as every other
9854
10636
  // daemon upstream call; no provider/account override applies here.
9855
10637
  fetchImpl: ((input, init) => (0, import_upstreamFetch8.fetchUpstream)(String(input), init ?? {}))
@@ -9865,8 +10647,10 @@ function buildDaemon(config, paths) {
9865
10647
  async (providerId, model) => await pricingEngine.getEntry(providerId, model) !== null
9866
10648
  );
9867
10649
  const keySpendTracker = new import_outbound_api5.KeySpendTracker(usageEventStore);
9868
- const usageRecorder = new import_usage.UsageRecorder(usageEventStore, pricingEngine, logger, {
9869
- onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at)
10650
+ const usageThroughput = (0, import_usage2.getSharedUsageThroughputTracker)();
10651
+ const usageRecorder = new import_usage2.UsageRecorder(usageEventStore, pricingEngine, logger, {
10652
+ onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at),
10653
+ onEvent: (row, at) => usageThroughput.record(row, at)
9870
10654
  });
9871
10655
  const providerProxy = (0, import_provider_proxy4.getProviderProxy)({ llmConfig, apiKeyPool, usageRecorder });
9872
10656
  const routeLeaseManager = new import_provider_proxy4.RouteLeaseManager(
@@ -10015,6 +10799,11 @@ function buildDaemon(config, paths) {
10015
10799
  // NEVER unauth, NEVER on `/health`. Routed in `AdminServer` (not `adminApi.ts`).
10016
10800
  auditReader: (query2) => readAuditRecords(auditDir, query2),
10017
10801
  auditStatsReader: (query2) => readAuditStats(auditDir, query2),
10802
+ // audit-store-sharding: bodies live in per-session shards, so opening ONE
10803
+ // record's payload is a separate authed call that replays its delta chain.
10804
+ auditBodyReader: (query2) => readAuditBody(auditDir, query2),
10805
+ // audit-store-sharding D8: the manual counterpart to the daily pass.
10806
+ auditCompactor: () => compactAllClosedAuditDays(auditDir),
10018
10807
  // billing-event-stream: the AUTHED `GET /admin/api/billing-status` returns the
10019
10808
  // secret-free total/delivered/pending counts of the durable ledger.
10020
10809
  billingStatusReader: () => readBillingStatus(billingDir)
@@ -10088,11 +10877,12 @@ function resetDaemonSingletonsForTests() {
10088
10877
  (0, import_SubscriptionIdentityStore3.__resetSharedIdentityStoreForTests)();
10089
10878
  (0, import_AccountAllowanceStore4.__resetSharedAccountAllowanceStoreForTests)();
10090
10879
  (0, import_AccountAllowanceScheduling5.__resetSharedAccountAllowanceSchedulingForTests)();
10880
+ (0, import_usage2.__resetSharedUsageThroughputTrackerForTests)();
10091
10881
  }
10092
10882
  function isTokensStoreReadable(tokensPath) {
10093
10883
  try {
10094
- if (!(0, import_node_fs23.existsSync)(tokensPath)) return true;
10095
- (0, import_node_fs23.accessSync)(tokensPath, import_node_fs23.constants.R_OK);
10884
+ if (!(0, import_node_fs26.existsSync)(tokensPath)) return true;
10885
+ (0, import_node_fs26.accessSync)(tokensPath, import_node_fs26.constants.R_OK);
10096
10886
  return true;
10097
10887
  } catch {
10098
10888
  return false;