eminiar 0.9.1 → 0.10.1

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/server.js CHANGED
@@ -116,7 +116,7 @@ var require_code = __commonJS({
116
116
  }
117
117
  exports._ = _;
118
118
  var plus = new _Code("+");
119
- function str2(strs, ...args) {
119
+ function str3(strs, ...args) {
120
120
  const expr = [safeStringify(strs[0])];
121
121
  let i = 0;
122
122
  while (i < args.length) {
@@ -127,7 +127,7 @@ var require_code = __commonJS({
127
127
  optimize(expr);
128
128
  return new _Code(expr);
129
129
  }
130
- exports.str = str2;
130
+ exports.str = str3;
131
131
  function addCodeArg(code, arg) {
132
132
  if (arg instanceof _Code)
133
133
  code.push(...arg._items);
@@ -170,7 +170,7 @@ var require_code = __commonJS({
170
170
  return;
171
171
  }
172
172
  function strConcat(c1, c2) {
173
- return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str2`${c1}${c2}`;
173
+ return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str3`${c1}${c2}`;
174
174
  }
175
175
  exports.strConcat = strConcat;
176
176
  function interpolate(x) {
@@ -1135,22 +1135,22 @@ var require_util = __commonJS({
1135
1135
  return (0, codegen_1._)`${topSchemaRef}${schemaPath}${(0, codegen_1.getProperty)(keyword)}`;
1136
1136
  }
1137
1137
  exports.schemaRefOrVal = schemaRefOrVal;
1138
- function unescapeFragment(str2) {
1139
- return unescapeJsonPointer(decodeURIComponent(str2));
1138
+ function unescapeFragment(str3) {
1139
+ return unescapeJsonPointer(decodeURIComponent(str3));
1140
1140
  }
1141
1141
  exports.unescapeFragment = unescapeFragment;
1142
- function escapeFragment(str2) {
1143
- return encodeURIComponent(escapeJsonPointer(str2));
1142
+ function escapeFragment(str3) {
1143
+ return encodeURIComponent(escapeJsonPointer(str3));
1144
1144
  }
1145
1145
  exports.escapeFragment = escapeFragment;
1146
- function escapeJsonPointer(str2) {
1147
- if (typeof str2 == "number")
1148
- return `${str2}`;
1149
- return str2.replace(/~/g, "~0").replace(/\//g, "~1");
1146
+ function escapeJsonPointer(str3) {
1147
+ if (typeof str3 == "number")
1148
+ return `${str3}`;
1149
+ return str3.replace(/~/g, "~0").replace(/\//g, "~1");
1150
1150
  }
1151
1151
  exports.escapeJsonPointer = escapeJsonPointer;
1152
- function unescapeJsonPointer(str2) {
1153
- return str2.replace(/~1/g, "/").replace(/~0/g, "~");
1152
+ function unescapeJsonPointer(str3) {
1153
+ return str3.replace(/~1/g, "/").replace(/~0/g, "~");
1154
1154
  }
1155
1155
  exports.unescapeJsonPointer = unescapeJsonPointer;
1156
1156
  function eachItem(xs, f) {
@@ -2187,8 +2187,8 @@ var require_json_schema_traverse = __commonJS({
2187
2187
  post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
2188
2188
  }
2189
2189
  }
2190
- function escapeJsonPtr(str2) {
2191
- return str2.replace(/~/g, "~0").replace(/\//g, "~1");
2190
+ function escapeJsonPtr(str3) {
2191
+ return str3.replace(/~/g, "~0").replace(/\//g, "~1");
2192
2192
  }
2193
2193
  }
2194
2194
  });
@@ -3019,7 +3019,7 @@ var require_compile = __commonJS({
3019
3019
  const schOrFunc = root.refs[ref];
3020
3020
  if (schOrFunc)
3021
3021
  return schOrFunc;
3022
- let _sch = resolve8.call(this, root, ref);
3022
+ let _sch = resolve9.call(this, root, ref);
3023
3023
  if (_sch === void 0) {
3024
3024
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
3025
3025
  const { schemaId } = this.opts;
@@ -3046,7 +3046,7 @@ var require_compile = __commonJS({
3046
3046
  function sameSchemaEnv(s1, s2) {
3047
3047
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3048
3048
  }
3049
- function resolve8(root, ref) {
3049
+ function resolve9(root, ref) {
3050
3050
  let sch;
3051
3051
  while (typeof (sch = this.refs[ref]) == "string")
3052
3052
  ref = sch;
@@ -3291,10 +3291,10 @@ var require_utils = __commonJS({
3291
3291
  isIPV6: true
3292
3292
  };
3293
3293
  }
3294
- function findToken(str2, token) {
3294
+ function findToken(str3, token) {
3295
3295
  let ind = 0;
3296
- for (let i = 0; i < str2.length; i++) {
3297
- if (str2[i] === token) ind++;
3296
+ for (let i = 0; i < str3.length; i++) {
3297
+ if (str3[i] === token) ind++;
3298
3298
  }
3299
3299
  return ind;
3300
3300
  }
@@ -3879,7 +3879,7 @@ var require_fast_uri = __commonJS({
3879
3879
  }
3880
3880
  return uri;
3881
3881
  }
3882
- function resolve8(baseURI, relativeURI, options) {
3882
+ function resolve9(baseURI, relativeURI, options) {
3883
3883
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3884
3884
  const {
3885
3885
  parsed: baseParsed,
@@ -4248,7 +4248,7 @@ var require_fast_uri = __commonJS({
4248
4248
  var fastUri = {
4249
4249
  SCHEMES,
4250
4250
  normalize: normalize7,
4251
- resolve: resolve8,
4251
+ resolve: resolve9,
4252
4252
  resolveComponent,
4253
4253
  equal,
4254
4254
  serialize,
@@ -4312,7 +4312,7 @@ var require_core = __commonJS({
4312
4312
  var util_1 = require_util();
4313
4313
  var $dataRefSchema = require_data();
4314
4314
  var uri_1 = require_uri();
4315
- var defaultRegExp = (str2, flags) => new RegExp(str2, flags);
4315
+ var defaultRegExp = (str3, flags) => new RegExp(str3, flags);
4316
4316
  defaultRegExp.code = "new RegExp";
4317
4317
  var META_IGNORE_OPTIONS = ["removeAdditional", "useDefaults", "coerceTypes"];
4318
4318
  var EXT_SCOPE_NAMES = /* @__PURE__ */ new Set([
@@ -5113,16 +5113,16 @@ var require_ucs2length = __commonJS({
5113
5113
  "use strict";
5114
5114
  init_esm_shims();
5115
5115
  Object.defineProperty(exports, "__esModule", { value: true });
5116
- function ucs2length(str2) {
5117
- const len = str2.length;
5116
+ function ucs2length(str3) {
5117
+ const len = str3.length;
5118
5118
  let length = 0;
5119
5119
  let pos = 0;
5120
5120
  let value;
5121
5121
  while (pos < len) {
5122
5122
  length++;
5123
- value = str2.charCodeAt(pos++);
5123
+ value = str3.charCodeAt(pos++);
5124
5124
  if (value >= 55296 && value <= 56319 && pos < len) {
5125
- value = str2.charCodeAt(pos);
5125
+ value = str3.charCodeAt(pos);
5126
5126
  if ((value & 64512) === 56320)
5127
5127
  pos++;
5128
5128
  }
@@ -7040,8 +7040,8 @@ var require_formats = __commonJS({
7040
7040
  }
7041
7041
  var DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
7042
7042
  var DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
7043
- function date5(str2) {
7044
- const matches2 = DATE.exec(str2);
7043
+ function date5(str3) {
7044
+ const matches2 = DATE.exec(str3);
7045
7045
  if (!matches2)
7046
7046
  return false;
7047
7047
  const year = +matches2[1];
@@ -7060,8 +7060,8 @@ var require_formats = __commonJS({
7060
7060
  }
7061
7061
  var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
7062
7062
  function getTime(strictTimeZone) {
7063
- return function time3(str2) {
7064
- const matches2 = TIME.exec(str2);
7063
+ return function time3(str3) {
7064
+ const matches2 = TIME.exec(str3);
7065
7065
  if (!matches2)
7066
7066
  return false;
7067
7067
  const hr = +matches2[1];
@@ -7107,9 +7107,9 @@ var require_formats = __commonJS({
7107
7107
  var DATE_TIME_SEPARATOR = /t|\s/i;
7108
7108
  function getDateTime(strictTimeZone) {
7109
7109
  const time3 = getTime(strictTimeZone);
7110
- return function date_time(str2) {
7111
- const dateTime = str2.split(DATE_TIME_SEPARATOR);
7112
- return dateTime.length === 2 && date5(dateTime[0]) && time3(dateTime[1]);
7110
+ return function date_time(str3) {
7111
+ const dateTime2 = str3.split(DATE_TIME_SEPARATOR);
7112
+ return dateTime2.length === 2 && date5(dateTime2[0]) && time3(dateTime2[1]);
7113
7113
  };
7114
7114
  }
7115
7115
  function compareDateTime(dt1, dt2) {
@@ -7133,13 +7133,13 @@ var require_formats = __commonJS({
7133
7133
  }
7134
7134
  var NOT_URI_FRAGMENT = /\/|:/;
7135
7135
  var URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
7136
- function uri(str2) {
7137
- return NOT_URI_FRAGMENT.test(str2) && URI.test(str2);
7136
+ function uri(str3) {
7137
+ return NOT_URI_FRAGMENT.test(str3) && URI.test(str3);
7138
7138
  }
7139
7139
  var BYTE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gm;
7140
- function byte(str2) {
7140
+ function byte(str3) {
7141
7141
  BYTE.lastIndex = 0;
7142
- return BYTE.test(str2);
7142
+ return BYTE.test(str3);
7143
7143
  }
7144
7144
  var MIN_INT32 = -(2 ** 31);
7145
7145
  var MAX_INT32 = 2 ** 31 - 1;
@@ -7153,11 +7153,11 @@ var require_formats = __commonJS({
7153
7153
  return true;
7154
7154
  }
7155
7155
  var Z_ANCHOR = /[^\\]\\Z/;
7156
- function regex(str2) {
7157
- if (Z_ANCHOR.test(str2))
7156
+ function regex(str3) {
7157
+ if (Z_ANCHOR.test(str3))
7158
7158
  return false;
7159
7159
  try {
7160
- new RegExp(str2);
7160
+ new RegExp(str3);
7161
7161
  return true;
7162
7162
  } catch (e) {
7163
7163
  return false;
@@ -7644,14 +7644,14 @@ var require_is_extglob = __commonJS({
7644
7644
  "node_modules/is-extglob/index.js"(exports, module) {
7645
7645
  "use strict";
7646
7646
  init_esm_shims();
7647
- module.exports = function isExtglob(str2) {
7648
- if (typeof str2 !== "string" || str2 === "") {
7647
+ module.exports = function isExtglob(str3) {
7648
+ if (typeof str3 !== "string" || str3 === "") {
7649
7649
  return false;
7650
7650
  }
7651
7651
  var match;
7652
- while (match = /(\\).|([@?!+*]\(.*\))/g.exec(str2)) {
7652
+ while (match = /(\\).|([@?!+*]\(.*\))/g.exec(str3)) {
7653
7653
  if (match[2]) return true;
7654
- str2 = str2.slice(match.index + match[0].length);
7654
+ str3 = str3.slice(match.index + match[0].length);
7655
7655
  }
7656
7656
  return false;
7657
7657
  };
@@ -7665,8 +7665,8 @@ var require_is_glob = __commonJS({
7665
7665
  init_esm_shims();
7666
7666
  var isExtglob = require_is_extglob();
7667
7667
  var chars = { "{": "}", "(": ")", "[": "]" };
7668
- var strictCheck = function(str2) {
7669
- if (str2[0] === "!") {
7668
+ var strictCheck = function(str3) {
7669
+ if (str3[0] === "!") {
7670
7670
  return true;
7671
7671
  }
7672
7672
  var index = 0;
@@ -7675,70 +7675,70 @@ var require_is_glob = __commonJS({
7675
7675
  var closeCurlyIndex = -2;
7676
7676
  var closeParenIndex = -2;
7677
7677
  var backSlashIndex = -2;
7678
- while (index < str2.length) {
7679
- if (str2[index] === "*") {
7678
+ while (index < str3.length) {
7679
+ if (str3[index] === "*") {
7680
7680
  return true;
7681
7681
  }
7682
- if (str2[index + 1] === "?" && /[\].+)]/.test(str2[index])) {
7682
+ if (str3[index + 1] === "?" && /[\].+)]/.test(str3[index])) {
7683
7683
  return true;
7684
7684
  }
7685
- if (closeSquareIndex !== -1 && str2[index] === "[" && str2[index + 1] !== "]") {
7685
+ if (closeSquareIndex !== -1 && str3[index] === "[" && str3[index + 1] !== "]") {
7686
7686
  if (closeSquareIndex < index) {
7687
- closeSquareIndex = str2.indexOf("]", index);
7687
+ closeSquareIndex = str3.indexOf("]", index);
7688
7688
  }
7689
7689
  if (closeSquareIndex > index) {
7690
7690
  if (backSlashIndex === -1 || backSlashIndex > closeSquareIndex) {
7691
7691
  return true;
7692
7692
  }
7693
- backSlashIndex = str2.indexOf("\\", index);
7693
+ backSlashIndex = str3.indexOf("\\", index);
7694
7694
  if (backSlashIndex === -1 || backSlashIndex > closeSquareIndex) {
7695
7695
  return true;
7696
7696
  }
7697
7697
  }
7698
7698
  }
7699
- if (closeCurlyIndex !== -1 && str2[index] === "{" && str2[index + 1] !== "}") {
7700
- closeCurlyIndex = str2.indexOf("}", index);
7699
+ if (closeCurlyIndex !== -1 && str3[index] === "{" && str3[index + 1] !== "}") {
7700
+ closeCurlyIndex = str3.indexOf("}", index);
7701
7701
  if (closeCurlyIndex > index) {
7702
- backSlashIndex = str2.indexOf("\\", index);
7702
+ backSlashIndex = str3.indexOf("\\", index);
7703
7703
  if (backSlashIndex === -1 || backSlashIndex > closeCurlyIndex) {
7704
7704
  return true;
7705
7705
  }
7706
7706
  }
7707
7707
  }
7708
- if (closeParenIndex !== -1 && str2[index] === "(" && str2[index + 1] === "?" && /[:!=]/.test(str2[index + 2]) && str2[index + 3] !== ")") {
7709
- closeParenIndex = str2.indexOf(")", index);
7708
+ if (closeParenIndex !== -1 && str3[index] === "(" && str3[index + 1] === "?" && /[:!=]/.test(str3[index + 2]) && str3[index + 3] !== ")") {
7709
+ closeParenIndex = str3.indexOf(")", index);
7710
7710
  if (closeParenIndex > index) {
7711
- backSlashIndex = str2.indexOf("\\", index);
7711
+ backSlashIndex = str3.indexOf("\\", index);
7712
7712
  if (backSlashIndex === -1 || backSlashIndex > closeParenIndex) {
7713
7713
  return true;
7714
7714
  }
7715
7715
  }
7716
7716
  }
7717
- if (pipeIndex !== -1 && str2[index] === "(" && str2[index + 1] !== "|") {
7717
+ if (pipeIndex !== -1 && str3[index] === "(" && str3[index + 1] !== "|") {
7718
7718
  if (pipeIndex < index) {
7719
- pipeIndex = str2.indexOf("|", index);
7719
+ pipeIndex = str3.indexOf("|", index);
7720
7720
  }
7721
- if (pipeIndex !== -1 && str2[pipeIndex + 1] !== ")") {
7722
- closeParenIndex = str2.indexOf(")", pipeIndex);
7721
+ if (pipeIndex !== -1 && str3[pipeIndex + 1] !== ")") {
7722
+ closeParenIndex = str3.indexOf(")", pipeIndex);
7723
7723
  if (closeParenIndex > pipeIndex) {
7724
- backSlashIndex = str2.indexOf("\\", pipeIndex);
7724
+ backSlashIndex = str3.indexOf("\\", pipeIndex);
7725
7725
  if (backSlashIndex === -1 || backSlashIndex > closeParenIndex) {
7726
7726
  return true;
7727
7727
  }
7728
7728
  }
7729
7729
  }
7730
7730
  }
7731
- if (str2[index] === "\\") {
7732
- var open3 = str2[index + 1];
7731
+ if (str3[index] === "\\") {
7732
+ var open3 = str3[index + 1];
7733
7733
  index += 2;
7734
7734
  var close = chars[open3];
7735
7735
  if (close) {
7736
- var n = str2.indexOf(close, index);
7736
+ var n = str3.indexOf(close, index);
7737
7737
  if (n !== -1) {
7738
7738
  index = n + 1;
7739
7739
  }
7740
7740
  }
7741
- if (str2[index] === "!") {
7741
+ if (str3[index] === "!") {
7742
7742
  return true;
7743
7743
  }
7744
7744
  } else {
@@ -7747,26 +7747,26 @@ var require_is_glob = __commonJS({
7747
7747
  }
7748
7748
  return false;
7749
7749
  };
7750
- var relaxedCheck = function(str2) {
7751
- if (str2[0] === "!") {
7750
+ var relaxedCheck = function(str3) {
7751
+ if (str3[0] === "!") {
7752
7752
  return true;
7753
7753
  }
7754
7754
  var index = 0;
7755
- while (index < str2.length) {
7756
- if (/[*?{}()[\]]/.test(str2[index])) {
7755
+ while (index < str3.length) {
7756
+ if (/[*?{}()[\]]/.test(str3[index])) {
7757
7757
  return true;
7758
7758
  }
7759
- if (str2[index] === "\\") {
7760
- var open3 = str2[index + 1];
7759
+ if (str3[index] === "\\") {
7760
+ var open3 = str3[index + 1];
7761
7761
  index += 2;
7762
7762
  var close = chars[open3];
7763
7763
  if (close) {
7764
- var n = str2.indexOf(close, index);
7764
+ var n = str3.indexOf(close, index);
7765
7765
  if (n !== -1) {
7766
7766
  index = n + 1;
7767
7767
  }
7768
7768
  }
7769
- if (str2[index] === "!") {
7769
+ if (str3[index] === "!") {
7770
7770
  return true;
7771
7771
  }
7772
7772
  } else {
@@ -7775,18 +7775,18 @@ var require_is_glob = __commonJS({
7775
7775
  }
7776
7776
  return false;
7777
7777
  };
7778
- module.exports = function isGlob(str2, options) {
7779
- if (typeof str2 !== "string" || str2 === "") {
7778
+ module.exports = function isGlob(str3, options) {
7779
+ if (typeof str3 !== "string" || str3 === "") {
7780
7780
  return false;
7781
7781
  }
7782
- if (isExtglob(str2)) {
7782
+ if (isExtglob(str3)) {
7783
7783
  return true;
7784
7784
  }
7785
7785
  var check3 = strictCheck;
7786
7786
  if (options && options.strict === false) {
7787
7787
  check3 = relaxedCheck;
7788
7788
  }
7789
- return check3(str2);
7789
+ return check3(str3);
7790
7790
  };
7791
7791
  }
7792
7792
  });
@@ -7804,19 +7804,19 @@ var require_glob_parent = __commonJS({
7804
7804
  var enclosure = /[\{\[].*[\}\]]$/;
7805
7805
  var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/;
7806
7806
  var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g;
7807
- module.exports = function globParent(str2, opts) {
7807
+ module.exports = function globParent(str3, opts) {
7808
7808
  var options = Object.assign({ flipBackslashes: true }, opts);
7809
- if (options.flipBackslashes && isWin32 && str2.indexOf(slash) < 0) {
7810
- str2 = str2.replace(backslash, slash);
7809
+ if (options.flipBackslashes && isWin32 && str3.indexOf(slash) < 0) {
7810
+ str3 = str3.replace(backslash, slash);
7811
7811
  }
7812
- if (enclosure.test(str2)) {
7813
- str2 += slash;
7812
+ if (enclosure.test(str3)) {
7813
+ str3 += slash;
7814
7814
  }
7815
- str2 += "a";
7815
+ str3 += "a";
7816
7816
  do {
7817
- str2 = pathPosixDirname(str2);
7818
- } while (isGlob(str2) || globby.test(str2));
7819
- return str2.replace(escaped, "$1");
7817
+ str3 = pathPosixDirname(str3);
7818
+ } while (isGlob(str3) || globby.test(str3));
7819
+ return str3.replace(escaped, "$1");
7820
7820
  };
7821
7821
  }
7822
7822
  });
@@ -7826,12 +7826,12 @@ var require_utils2 = __commonJS({
7826
7826
  "node_modules/braces/lib/utils.js"(exports) {
7827
7827
  "use strict";
7828
7828
  init_esm_shims();
7829
- exports.isInteger = (num2) => {
7830
- if (typeof num2 === "number") {
7831
- return Number.isInteger(num2);
7829
+ exports.isInteger = (num5) => {
7830
+ if (typeof num5 === "number") {
7831
+ return Number.isInteger(num5);
7832
7832
  }
7833
- if (typeof num2 === "string" && num2.trim() !== "") {
7834
- return Number.isInteger(Number(num2));
7833
+ if (typeof num5 === "string" && num5.trim() !== "") {
7834
+ return Number.isInteger(Number(num5));
7835
7835
  }
7836
7836
  return false;
7837
7837
  };
@@ -7941,12 +7941,12 @@ var require_is_number = __commonJS({
7941
7941
  "node_modules/is-number/index.js"(exports, module) {
7942
7942
  "use strict";
7943
7943
  init_esm_shims();
7944
- module.exports = function(num2) {
7945
- if (typeof num2 === "number") {
7946
- return num2 - num2 === 0;
7944
+ module.exports = function(num5) {
7945
+ if (typeof num5 === "number") {
7946
+ return num5 - num5 === 0;
7947
7947
  }
7948
- if (typeof num2 === "string" && num2.trim() !== "") {
7949
- return Number.isFinite ? Number.isFinite(+num2) : isFinite(+num2);
7948
+ if (typeof num5 === "string" && num5.trim() !== "") {
7949
+ return Number.isFinite ? Number.isFinite(+num5) : isFinite(+num5);
7950
7950
  }
7951
7951
  return false;
7952
7952
  };
@@ -8070,7 +8070,7 @@ var require_to_regex_range = __commonJS({
8070
8070
  }
8071
8071
  return { pattern: pattern2, count: [count], digits };
8072
8072
  }
8073
- function splitToPatterns(min, max, tok, options) {
8073
+ function splitToPatterns(min, max, tok2, options) {
8074
8074
  let ranges = splitToRanges(min, max);
8075
8075
  let tokens = [];
8076
8076
  let start = min;
@@ -8079,7 +8079,7 @@ var require_to_regex_range = __commonJS({
8079
8079
  let max2 = ranges[i];
8080
8080
  let obj = rangeToPattern(String(start), String(max2), options);
8081
8081
  let zeros = "";
8082
- if (!tok.isPadded && prev && prev.pattern === obj.pattern) {
8082
+ if (!tok2.isPadded && prev && prev.pattern === obj.pattern) {
8083
8083
  if (prev.count.length > 1) {
8084
8084
  prev.count.pop();
8085
8085
  }
@@ -8088,8 +8088,8 @@ var require_to_regex_range = __commonJS({
8088
8088
  start = max2 + 1;
8089
8089
  continue;
8090
8090
  }
8091
- if (tok.isPadded) {
8092
- zeros = padZeros(max2, tok, options);
8091
+ if (tok2.isPadded) {
8092
+ zeros = padZeros(max2, tok2, options);
8093
8093
  }
8094
8094
  obj.string = zeros + obj.pattern + toQuantifier(obj.count);
8095
8095
  tokens.push(obj);
@@ -8138,14 +8138,14 @@ var require_to_regex_range = __commonJS({
8138
8138
  function toCharacterClass(a, b, options) {
8139
8139
  return `[${a}${b - a === 1 ? "" : "-"}${b}]`;
8140
8140
  }
8141
- function hasPadding(str2) {
8142
- return /^-?(0+)\d/.test(str2);
8141
+ function hasPadding(str3) {
8142
+ return /^-?(0+)\d/.test(str3);
8143
8143
  }
8144
- function padZeros(value, tok, options) {
8145
- if (!tok.isPadded) {
8144
+ function padZeros(value, tok2, options) {
8145
+ if (!tok2.isPadded) {
8146
8146
  return value;
8147
8147
  }
8148
- let diff = Math.abs(tok.maxLen - String(value).length);
8148
+ let diff = Math.abs(tok2.maxLen - String(value).length);
8149
8149
  let relax = options.relaxZeros !== false;
8150
8150
  switch (diff) {
8151
8151
  case 0:
@@ -8179,7 +8179,7 @@ var require_fill_range = __commonJS({
8179
8179
  var isValidValue = (value) => {
8180
8180
  return typeof value === "number" || typeof value === "string" && value !== "";
8181
8181
  };
8182
- var isNumber = (num2) => Number.isInteger(+num2);
8182
+ var isNumber = (num5) => Number.isInteger(+num5);
8183
8183
  var zeros = (input2) => {
8184
8184
  let value = `${input2}`;
8185
8185
  let index = -1;
@@ -8289,7 +8289,7 @@ var require_fill_range = __commonJS({
8289
8289
  return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options);
8290
8290
  }
8291
8291
  let parts = { negatives: [], positives: [] };
8292
- let push = (num2) => parts[num2 < 0 ? "negatives" : "positives"].push(Math.abs(num2));
8292
+ let push = (num5) => parts[num5 < 0 ? "negatives" : "positives"].push(Math.abs(num5));
8293
8293
  let range = [];
8294
8294
  let index = 0;
8295
8295
  while (descending ? a >= b : a <= b) {
@@ -9129,12 +9129,12 @@ var require_utils3 = __commonJS({
9129
9129
  REGEX_SPECIAL_CHARS_GLOBAL
9130
9130
  } = require_constants2();
9131
9131
  exports.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val);
9132
- exports.hasRegexChars = (str2) => REGEX_SPECIAL_CHARS.test(str2);
9133
- exports.isRegexChar = (str2) => str2.length === 1 && exports.hasRegexChars(str2);
9134
- exports.escapeRegex = (str2) => str2.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1");
9135
- exports.toPosixSlashes = (str2) => str2.replace(REGEX_BACKSLASH, "/");
9136
- exports.removeBackslashes = (str2) => {
9137
- return str2.replace(REGEX_REMOVE_BACKSLASH, (match) => {
9132
+ exports.hasRegexChars = (str3) => REGEX_SPECIAL_CHARS.test(str3);
9133
+ exports.isRegexChar = (str3) => str3.length === 1 && exports.hasRegexChars(str3);
9134
+ exports.escapeRegex = (str3) => str3.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1");
9135
+ exports.toPosixSlashes = (str3) => str3.replace(REGEX_BACKSLASH, "/");
9136
+ exports.removeBackslashes = (str3) => {
9137
+ return str3.replace(REGEX_REMOVE_BACKSLASH, (match) => {
9138
9138
  return match === "\\" ? "" : match;
9139
9139
  });
9140
9140
  };
@@ -9230,7 +9230,7 @@ var require_scan = __commonJS({
9230
9230
  const slashes = [];
9231
9231
  const tokens = [];
9232
9232
  const parts = [];
9233
- let str2 = input2;
9233
+ let str3 = input2;
9234
9234
  let index = -1;
9235
9235
  let start = 0;
9236
9236
  let lastIndex = 0;
@@ -9249,10 +9249,10 @@ var require_scan = __commonJS({
9249
9249
  let code;
9250
9250
  let token = { value: "", depth: 0, isGlob: false };
9251
9251
  const eos = () => index >= length;
9252
- const peek = () => str2.charCodeAt(index + 1);
9252
+ const peek = () => str3.charCodeAt(index + 1);
9253
9253
  const advance = () => {
9254
9254
  prev = code;
9255
- return str2.charCodeAt(++index);
9255
+ return str3.charCodeAt(++index);
9256
9256
  };
9257
9257
  while (index < length) {
9258
9258
  code = advance();
@@ -9420,24 +9420,24 @@ var require_scan = __commonJS({
9420
9420
  isExtglob = false;
9421
9421
  isGlob = false;
9422
9422
  }
9423
- let base = str2;
9423
+ let base = str3;
9424
9424
  let prefix = "";
9425
9425
  let glob2 = "";
9426
9426
  if (start > 0) {
9427
- prefix = str2.slice(0, start);
9428
- str2 = str2.slice(start);
9427
+ prefix = str3.slice(0, start);
9428
+ str3 = str3.slice(start);
9429
9429
  lastIndex -= start;
9430
9430
  }
9431
9431
  if (base && isGlob === true && lastIndex > 0) {
9432
- base = str2.slice(0, lastIndex);
9433
- glob2 = str2.slice(lastIndex);
9432
+ base = str3.slice(0, lastIndex);
9433
+ glob2 = str3.slice(lastIndex);
9434
9434
  } else if (isGlob === true) {
9435
9435
  base = "";
9436
- glob2 = str2;
9436
+ glob2 = str3;
9437
9437
  } else {
9438
- base = str2;
9438
+ base = str3;
9439
9439
  }
9440
- if (base && base !== "" && base !== "/" && base !== str2) {
9440
+ if (base && base !== "" && base !== "/" && base !== str3) {
9441
9441
  if (isPathSeparator(base.charCodeAt(base.length - 1))) {
9442
9442
  base = base.slice(0, -1);
9443
9443
  }
@@ -9816,9 +9816,9 @@ var require_parse2 = __commonJS({
9816
9816
  const peek = state.peek = (n = 1) => input2[state.index + n];
9817
9817
  const advance = state.advance = () => input2[++state.index] || "";
9818
9818
  const remaining = () => input2.slice(state.index + 1);
9819
- const consume = (value2 = "", num2 = 0) => {
9819
+ const consume = (value2 = "", num5 = 0) => {
9820
9820
  state.consumed += value2;
9821
- state.index += num2;
9821
+ state.index += num5;
9822
9822
  };
9823
9823
  const append = (token) => {
9824
9824
  state.output += token.output != null ? token.output : token.value;
@@ -9846,11 +9846,11 @@ var require_parse2 = __commonJS({
9846
9846
  state[type]--;
9847
9847
  stack.pop();
9848
9848
  };
9849
- const push = (tok) => {
9849
+ const push = (tok2) => {
9850
9850
  if (prev.type === "globstar") {
9851
- const isBrace = state.braces > 0 && (tok.type === "comma" || tok.type === "brace");
9852
- const isExtglob = tok.extglob === true || extglobs.length && (tok.type === "pipe" || tok.type === "paren");
9853
- if (tok.type !== "slash" && tok.type !== "paren" && !isBrace && !isExtglob) {
9851
+ const isBrace = state.braces > 0 && (tok2.type === "comma" || tok2.type === "brace");
9852
+ const isExtglob = tok2.extglob === true || extglobs.length && (tok2.type === "pipe" || tok2.type === "paren");
9853
+ if (tok2.type !== "slash" && tok2.type !== "paren" && !isBrace && !isExtglob) {
9854
9854
  state.output = state.output.slice(0, -prev.output.length);
9855
9855
  prev.type = "star";
9856
9856
  prev.value = "*";
@@ -9858,18 +9858,18 @@ var require_parse2 = __commonJS({
9858
9858
  state.output += prev.output;
9859
9859
  }
9860
9860
  }
9861
- if (extglobs.length && tok.type !== "paren") {
9862
- extglobs[extglobs.length - 1].inner += tok.value;
9861
+ if (extglobs.length && tok2.type !== "paren") {
9862
+ extglobs[extglobs.length - 1].inner += tok2.value;
9863
9863
  }
9864
- if (tok.value || tok.output) append(tok);
9865
- if (prev && prev.type === "text" && tok.type === "text") {
9866
- prev.value += tok.value;
9867
- prev.output = (prev.output || "") + tok.value;
9864
+ if (tok2.value || tok2.output) append(tok2);
9865
+ if (prev && prev.type === "text" && tok2.type === "text") {
9866
+ prev.value += tok2.value;
9867
+ prev.output = (prev.output || "") + tok2.value;
9868
9868
  return;
9869
9869
  }
9870
- tok.prev = prev;
9871
- tokens.push(tok);
9872
- prev = tok;
9870
+ tok2.prev = prev;
9871
+ tokens.push(tok2);
9872
+ prev = tok2;
9873
9873
  };
9874
9874
  const extglobOpen = (type, value2) => {
9875
9875
  const token = { ...EXTGLOB_CHARS[value2], conditions: 1, inner: "" };
@@ -10473,8 +10473,8 @@ var require_parse2 = __commonJS({
10473
10473
  if (opts2.noglobstar === true) return star;
10474
10474
  return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
10475
10475
  };
10476
- const create = (str2) => {
10477
- switch (str2) {
10476
+ const create = (str3) => {
10477
+ switch (str3) {
10478
10478
  case "*":
10479
10479
  return `${nodot}${ONE_CHAR}${star}`;
10480
10480
  case ".*":
@@ -10492,7 +10492,7 @@ var require_parse2 = __commonJS({
10492
10492
  case "**/.*":
10493
10493
  return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`;
10494
10494
  default: {
10495
- const match = /^(.*?)\.(\w+)$/.exec(str2);
10495
+ const match = /^(.*?)\.(\w+)$/.exec(str3);
10496
10496
  if (!match) return;
10497
10497
  const source2 = create(match[1]);
10498
10498
  if (!source2) return;
@@ -10525,9 +10525,9 @@ var require_picomatch = __commonJS({
10525
10525
  var picomatch = (glob2, options, returnState = false) => {
10526
10526
  if (Array.isArray(glob2)) {
10527
10527
  const fns = glob2.map((input2) => picomatch(input2, options, returnState));
10528
- const arrayMatcher = (str2) => {
10528
+ const arrayMatcher = (str3) => {
10529
10529
  for (const isMatch of fns) {
10530
- const state2 = isMatch(str2);
10530
+ const state2 = isMatch(str3);
10531
10531
  if (state2) return state2;
10532
10532
  }
10533
10533
  return false;
@@ -10603,7 +10603,7 @@ var require_picomatch = __commonJS({
10603
10603
  const regex = glob2 instanceof RegExp ? glob2 : picomatch.makeRe(glob2, options);
10604
10604
  return regex.test(path3.basename(input2));
10605
10605
  };
10606
- picomatch.isMatch = (str2, patterns, options) => picomatch(patterns, options)(str2);
10606
+ picomatch.isMatch = (str3, patterns, options) => picomatch(patterns, options)(str3);
10607
10607
  picomatch.parse = (pattern2, options) => {
10608
10608
  if (Array.isArray(pattern2)) return pattern2.map((p) => picomatch.parse(p, options));
10609
10609
  return parse3(pattern2, { ...options, fastpaths: false });
@@ -10719,7 +10719,7 @@ var require_micromatch = __commonJS({
10719
10719
  };
10720
10720
  micromatch.match = micromatch;
10721
10721
  micromatch.matcher = (pattern2, options) => picomatch(pattern2, options);
10722
- micromatch.isMatch = (str2, patterns, options) => picomatch(patterns, options)(str2);
10722
+ micromatch.isMatch = (str3, patterns, options) => picomatch(patterns, options)(str3);
10723
10723
  micromatch.any = micromatch.isMatch;
10724
10724
  micromatch.not = (list2, patterns, options = {}) => {
10725
10725
  patterns = [].concat(patterns).map(String);
@@ -10737,22 +10737,22 @@ var require_micromatch = __commonJS({
10737
10737
  }
10738
10738
  return [...result];
10739
10739
  };
10740
- micromatch.contains = (str2, pattern2, options) => {
10741
- if (typeof str2 !== "string") {
10742
- throw new TypeError(`Expected a string: "${util2.inspect(str2)}"`);
10740
+ micromatch.contains = (str3, pattern2, options) => {
10741
+ if (typeof str3 !== "string") {
10742
+ throw new TypeError(`Expected a string: "${util2.inspect(str3)}"`);
10743
10743
  }
10744
10744
  if (Array.isArray(pattern2)) {
10745
- return pattern2.some((p) => micromatch.contains(str2, p, options));
10745
+ return pattern2.some((p) => micromatch.contains(str3, p, options));
10746
10746
  }
10747
10747
  if (typeof pattern2 === "string") {
10748
- if (isEmptyString(str2) || isEmptyString(pattern2)) {
10748
+ if (isEmptyString(str3) || isEmptyString(pattern2)) {
10749
10749
  return false;
10750
10750
  }
10751
- if (str2.includes(pattern2) || str2.startsWith("./") && str2.slice(2).includes(pattern2)) {
10751
+ if (str3.includes(pattern2) || str3.startsWith("./") && str3.slice(2).includes(pattern2)) {
10752
10752
  return true;
10753
10753
  }
10754
10754
  }
10755
- return micromatch.isMatch(str2, pattern2, { ...options, contains: true });
10755
+ return micromatch.isMatch(str3, pattern2, { ...options, contains: true });
10756
10756
  };
10757
10757
  micromatch.matchKeys = (obj, patterns, options) => {
10758
10758
  if (!utils.isObject(obj)) {
@@ -10783,11 +10783,11 @@ var require_micromatch = __commonJS({
10783
10783
  }
10784
10784
  return true;
10785
10785
  };
10786
- micromatch.all = (str2, patterns, options) => {
10787
- if (typeof str2 !== "string") {
10788
- throw new TypeError(`Expected a string: "${util2.inspect(str2)}"`);
10786
+ micromatch.all = (str3, patterns, options) => {
10787
+ if (typeof str3 !== "string") {
10788
+ throw new TypeError(`Expected a string: "${util2.inspect(str3)}"`);
10789
10789
  }
10790
- return [].concat(patterns).every((p) => picomatch(p, options)(str2));
10790
+ return [].concat(patterns).every((p) => picomatch(p, options)(str3));
10791
10791
  };
10792
10792
  micromatch.capture = (glob2, input2, options) => {
10793
10793
  let posix2 = utils.isWindows(options);
@@ -10802,8 +10802,8 @@ var require_micromatch = __commonJS({
10802
10802
  micromatch.parse = (patterns, options) => {
10803
10803
  let res = [];
10804
10804
  for (let pattern2 of [].concat(patterns || [])) {
10805
- for (let str2 of braces(String(pattern2), options)) {
10806
- res.push(picomatch.parse(str2, options));
10805
+ for (let str3 of braces(String(pattern2), options)) {
10806
+ res.push(picomatch.parse(str3, options));
10807
10807
  }
10808
10808
  }
10809
10809
  return res;
@@ -10926,8 +10926,8 @@ var require_pattern2 = __commonJS({
10926
10926
  }
10927
10927
  exports.endsWithSlashGlobStar = endsWithSlashGlobStar;
10928
10928
  function isAffectDepthOfReadingPattern(pattern2) {
10929
- const basename2 = path3.basename(pattern2);
10930
- return endsWithSlashGlobStar(pattern2) || isStaticPattern(basename2);
10929
+ const basename3 = path3.basename(pattern2);
10930
+ return endsWithSlashGlobStar(pattern2) || isStaticPattern(basename3);
10931
10931
  }
10932
10932
  exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
10933
10933
  function expandPatternsWithBraceExpansion(patterns) {
@@ -11411,11 +11411,11 @@ var require_out = __commonJS({
11411
11411
  async.read(path3, getSettings(optionsOrSettingsOrCallback), callback);
11412
11412
  }
11413
11413
  exports.stat = stat;
11414
- function statSync11(path3, optionsOrSettings) {
11414
+ function statSync12(path3, optionsOrSettings) {
11415
11415
  const settings = getSettings(optionsOrSettings);
11416
11416
  return sync.read(path3, settings);
11417
11417
  }
11418
- exports.statSync = statSync11;
11418
+ exports.statSync = statSync12;
11419
11419
  function getSettings(settingsOrOptions = {}) {
11420
11420
  if (settingsOrOptions instanceof settings_1.default) {
11421
11421
  return settingsOrOptions;
@@ -12096,41 +12096,41 @@ var require_queue = __commonJS({
12096
12096
  queue.drained = drained;
12097
12097
  return queue;
12098
12098
  function push(value) {
12099
- var p = new Promise(function(resolve8, reject) {
12099
+ var p = new Promise(function(resolve9, reject) {
12100
12100
  pushCb(value, function(err, result) {
12101
12101
  if (err) {
12102
12102
  reject(err);
12103
12103
  return;
12104
12104
  }
12105
- resolve8(result);
12105
+ resolve9(result);
12106
12106
  });
12107
12107
  });
12108
12108
  p.catch(noop);
12109
12109
  return p;
12110
12110
  }
12111
12111
  function unshift(value) {
12112
- var p = new Promise(function(resolve8, reject) {
12112
+ var p = new Promise(function(resolve9, reject) {
12113
12113
  unshiftCb(value, function(err, result) {
12114
12114
  if (err) {
12115
12115
  reject(err);
12116
12116
  return;
12117
12117
  }
12118
- resolve8(result);
12118
+ resolve9(result);
12119
12119
  });
12120
12120
  });
12121
12121
  p.catch(noop);
12122
12122
  return p;
12123
12123
  }
12124
12124
  function drained() {
12125
- var p = new Promise(function(resolve8) {
12125
+ var p = new Promise(function(resolve9) {
12126
12126
  process.nextTick(function() {
12127
12127
  if (queue.idle()) {
12128
- resolve8();
12128
+ resolve9();
12129
12129
  } else {
12130
12130
  var previousDrain = queue.drain;
12131
12131
  queue.drain = function() {
12132
12132
  if (typeof previousDrain === "function") previousDrain();
12133
- resolve8();
12133
+ resolve9();
12134
12134
  queue.drain = previousDrain;
12135
12135
  };
12136
12136
  }
@@ -12627,9 +12627,9 @@ var require_stream3 = __commonJS({
12627
12627
  });
12628
12628
  }
12629
12629
  _getStat(filepath) {
12630
- return new Promise((resolve8, reject) => {
12630
+ return new Promise((resolve9, reject) => {
12631
12631
  this._stat(filepath, this._fsStatSettings, (error62, stats) => {
12632
- return error62 === null ? resolve8(stats) : reject(error62);
12632
+ return error62 === null ? resolve9(stats) : reject(error62);
12633
12633
  });
12634
12634
  });
12635
12635
  }
@@ -12654,10 +12654,10 @@ var require_async5 = __commonJS({
12654
12654
  this._readerStream = new stream_1.default(this._settings);
12655
12655
  }
12656
12656
  dynamic(root, options) {
12657
- return new Promise((resolve8, reject) => {
12657
+ return new Promise((resolve9, reject) => {
12658
12658
  this._walkAsync(root, options, (error62, entries) => {
12659
12659
  if (error62 === null) {
12660
- resolve8(entries);
12660
+ resolve9(entries);
12661
12661
  } else {
12662
12662
  reject(error62);
12663
12663
  }
@@ -12667,10 +12667,10 @@ var require_async5 = __commonJS({
12667
12667
  async static(patterns, options) {
12668
12668
  const entries = [];
12669
12669
  const stream = this._readerStream.static(patterns, options);
12670
- return new Promise((resolve8, reject) => {
12670
+ return new Promise((resolve9, reject) => {
12671
12671
  stream.once("error", reject);
12672
12672
  stream.on("data", (entry) => entries.push(entry));
12673
- stream.once("end", () => resolve8(entries));
12673
+ stream.once("end", () => resolve9(entries));
12674
12674
  });
12675
12675
  }
12676
12676
  };
@@ -13518,7 +13518,7 @@ function chunkMarkdown(text2, baseTitle, opts = DEFAULT_CHUNKER) {
13518
13518
  if (/^\s*(```|~~~)/.test(line)) inFence = !inFence;
13519
13519
  const m = !inFence && /^(#{1,6})\s+(.+?)\s*#*\s*$/.exec(line);
13520
13520
  if (m) {
13521
- if (current.lines.length) sections.push(current);
13521
+ sections.push(current);
13522
13522
  const level = m[1].length;
13523
13523
  while (stack.length && stack[stack.length - 1].level >= level) stack.pop();
13524
13524
  stack.push({ level, text: m[2] });
@@ -13527,14 +13527,23 @@ function chunkMarkdown(text2, baseTitle, opts = DEFAULT_CHUNKER) {
13527
13527
  }
13528
13528
  current.lines.push(line);
13529
13529
  });
13530
- if (current.lines.length) sections.push(current);
13530
+ sections.push(current);
13531
13531
  const out = [];
13532
- for (const s of sections) {
13532
+ sections.forEach((s, k) => {
13533
13533
  const title = s.path.length ? `${baseTitle} > ${s.path.join(" > ")}` : baseTitle;
13534
13534
  const body = s.lines.join("\n");
13535
+ if (!body.trim()) {
13536
+ const next = sections[k + 1];
13537
+ const isParent = next !== void 0 && next.path.length > s.path.length && s.path.every((p, j) => next.path[j] === p);
13538
+ if (s.path.length && !isParent) {
13539
+ const heading = s.path[s.path.length - 1];
13540
+ out.push({ title, body: heading, startLine: s.startLine, endLine: s.startLine });
13541
+ }
13542
+ return;
13543
+ }
13535
13544
  if (body.length <= opts.maxChars) {
13536
13545
  out.push({ title, body, startLine: s.startLine, endLine: s.startLine + s.lines.length - 1 });
13537
- continue;
13546
+ return;
13538
13547
  }
13539
13548
  let fence2 = false;
13540
13549
  const fenceState = s.lines.map((l) => {
@@ -13550,7 +13559,7 @@ function chunkMarkdown(text2, baseTitle, opts = DEFAULT_CHUNKER) {
13550
13559
  s.startLine - 1
13551
13560
  )
13552
13561
  );
13553
- }
13562
+ });
13554
13563
  return out;
13555
13564
  }
13556
13565
  var init_markdown = __esm({
@@ -16953,7 +16962,20 @@ import { readFileSync as readFileSync6, statSync as statSync4 } from "fs";
16953
16962
  import { relative as relative5, resolve as resolve6 } from "path";
16954
16963
  function indexContent(db, input2) {
16955
16964
  const kind = input2.kind ?? (input2.contentType === "code" ? "code" : input2.contentType === "json" ? "json" : void 0);
16956
- const { chunks, kind: k } = input2.chunks ? { chunks: finalize2(input2.chunks, input2.contentType ?? "code"), kind: kind ?? "code" } : chunk(input2.content, input2.title, kind, input2.uri);
16965
+ const cut = input2.chunks ? { chunks: finalize2(input2.chunks, input2.contentType ?? "code"), kind: kind ?? "code" } : chunk(input2.content, input2.title, kind, input2.uri);
16966
+ const k = cut.kind;
16967
+ const fallback = cut.chunks.length === 0 && input2.content.trim().length > 0;
16968
+ const chunks = fallback ? finalize2(
16969
+ [
16970
+ {
16971
+ title: input2.title,
16972
+ body: input2.content,
16973
+ startLine: 1,
16974
+ endLine: input2.content.split("\n").length
16975
+ }
16976
+ ],
16977
+ kindToContentType(k)
16978
+ ) : cut.chunks;
16957
16979
  const contentHash = hashText(input2.content);
16958
16980
  const existing = input2.uri ? db.findDocumentByUri(input2.uri) : void 0;
16959
16981
  if (existing && existing.content_hash === contentHash) {
@@ -16963,7 +16985,8 @@ function indexContent(db, input2) {
16963
16985
  created: false,
16964
16986
  chunks: chunks.length,
16965
16987
  chars: input2.content.length,
16966
- kind: k
16988
+ kind: k,
16989
+ fallback
16967
16990
  };
16968
16991
  }
16969
16992
  db.deleteDocument(existing.id);
@@ -16980,11 +17003,25 @@ function indexContent(db, input2) {
16980
17003
  },
16981
17004
  chunks
16982
17005
  );
16983
- return { documentId: id, created, chunks: chunks.length, chars: input2.content.length, kind: k };
17006
+ return {
17007
+ documentId: id,
17008
+ created,
17009
+ chunks: chunks.length,
17010
+ chars: input2.content.length,
17011
+ kind: k,
17012
+ fallback
17013
+ };
16984
17014
  }
16985
17015
  function indexPath(db, root, target, opts = {}) {
16986
17016
  const abs = resolve6(root, target);
16987
- const summary = { files: 0, indexed: 0, unchanged: 0, skipped: [], chunks: 0 };
17017
+ const summary = {
17018
+ files: 0,
17019
+ indexed: 0,
17020
+ unchanged: 0,
17021
+ skipped: [],
17022
+ chunks: 0,
17023
+ fallbacks: 0
17024
+ };
16988
17025
  const st = statSync4(abs);
16989
17026
  const files = st.isDirectory() ? import_fast_glob.default.sync(opts.globs ?? ["**/*.md"], {
16990
17027
  cwd: abs,
@@ -17019,6 +17056,7 @@ function indexPath(db, root, target, opts = {}) {
17019
17056
  if (r.created) summary.indexed++;
17020
17057
  else summary.unchanged++;
17021
17058
  summary.chunks += r.chunks;
17059
+ if (r.fallback) summary.fallbacks++;
17022
17060
  } catch (err) {
17023
17061
  summary.skipped.push({ path: rel, reason: err.message });
17024
17062
  }
@@ -18982,14 +19020,14 @@ function buildHistory(ctx, db, opts = {}) {
18982
19020
  };
18983
19021
  });
18984
19022
  const bySha = new Map(commits.map((c) => [c.sha, c]));
18985
- const resolve8 = (prefix) => {
19023
+ const resolve9 = (prefix) => {
18986
19024
  if (bySha.has(prefix)) return bySha.get(prefix);
18987
19025
  for (const c of commits) if (c.sha.startsWith(prefix)) return c;
18988
19026
  return void 0;
18989
19027
  };
18990
19028
  for (const c of commits) {
18991
19029
  if (c.kind !== "revert" || !c.revertOf) continue;
18992
- const target = resolve8(c.revertOf);
19030
+ const target = resolve9(c.revertOf);
18993
19031
  if (target) {
18994
19032
  target.revertedBy = c.sha;
18995
19033
  c.origins.push(target.sha);
@@ -24006,14 +24044,14 @@ function promiseAllObject(promisesObj) {
24006
24044
  }
24007
24045
  function randomString(length = 10) {
24008
24046
  const chars = "abcdefghijklmnopqrstuvwxyz";
24009
- let str2 = "";
24047
+ let str3 = "";
24010
24048
  for (let i = 0; i < length; i++) {
24011
- str2 += chars[Math.floor(Math.random() * chars.length)];
24049
+ str3 += chars[Math.floor(Math.random() * chars.length)];
24012
24050
  }
24013
- return str2;
24051
+ return str3;
24014
24052
  }
24015
- function esc(str2) {
24016
- return JSON.stringify(str2);
24053
+ function esc(str3) {
24054
+ return JSON.stringify(str3);
24017
24055
  }
24018
24056
  function slugify(input2) {
24019
24057
  return input2.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
@@ -24127,8 +24165,8 @@ var primitiveTypes = /* @__PURE__ */ new Set([
24127
24165
  "symbol",
24128
24166
  "undefined"
24129
24167
  ]);
24130
- function escapeRegex(str2) {
24131
- return str2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24168
+ function escapeRegex(str3) {
24169
+ return str3.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24132
24170
  }
24133
24171
  function clone(inst, def, params) {
24134
24172
  const cl = new inst._zod.constr(def ?? inst._zod.def);
@@ -24394,13 +24432,13 @@ function getSizableOrigin(input2) {
24394
24432
  return "unknown";
24395
24433
  }
24396
24434
  var highSurrogate = /[\uD800-\uDBFF]/;
24397
- function codePointLength(str2) {
24398
- const units = str2.length;
24399
- if (!highSurrogate.test(str2))
24435
+ function codePointLength(str3) {
24436
+ const units = str3.length;
24437
+ if (!highSurrogate.test(str3))
24400
24438
  return units;
24401
24439
  let count = units;
24402
24440
  for (let i = 0; i < units - 1; i++) {
24403
- if ((str2.charCodeAt(i) & 64512) === 55296 && (str2.charCodeAt(i + 1) & 64512) === 56320) {
24441
+ if ((str3.charCodeAt(i) & 64512) === 55296 && (str3.charCodeAt(i + 1) & 64512) === 56320) {
24404
24442
  count--;
24405
24443
  i++;
24406
24444
  }
@@ -25213,8 +25251,8 @@ function anchor(source) {
25213
25251
  }
25214
25252
  var date = /* @__PURE__ */ anchor(dateSource);
25215
25253
  function timeSource(args) {
25216
- const hhmm = `(?:[01]\\d|2[0-3]):[0-5]\\d`;
25217
- const regex = typeof args.precision === "number" ? args.precision === -1 ? `${hhmm}` : args.precision === 0 ? `${hhmm}:[0-5]\\d` : `${hhmm}:[0-5]\\d\\.\\d{${args.precision}}` : args.seconds ? `${hhmm}:[0-5]\\d(?:\\.\\d+)?` : `${hhmm}(?::[0-5]\\d(?:\\.\\d+)?)?`;
25254
+ const hhmm2 = `(?:[01]\\d|2[0-3]):[0-5]\\d`;
25255
+ const regex = typeof args.precision === "number" ? args.precision === -1 ? `${hhmm2}` : args.precision === 0 ? `${hhmm2}:[0-5]\\d` : `${hhmm2}:[0-5]\\d\\.\\d{${args.precision}}` : args.seconds ? `${hhmm2}:[0-5]\\d(?:\\.\\d+)?` : `${hhmm2}(?::[0-5]\\d(?:\\.\\d+)?)?`;
25218
25256
  return regex;
25219
25257
  }
25220
25258
  function time(args) {
@@ -28252,7 +28290,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
28252
28290
  var NONE = 0;
28253
28291
  var ASSUMED = 1;
28254
28292
  var PROVEN = 2;
28255
- function isRecursive(inst, stack, resolve8) {
28293
+ function isRecursive(inst, stack, resolve9) {
28256
28294
  const cached5 = recursive.get(inst);
28257
28295
  if (cached5 !== void 0)
28258
28296
  return cached5 ? PROVEN : NONE;
@@ -28262,7 +28300,7 @@ function isRecursive(inst, stack, resolve8) {
28262
28300
  let result = NONE;
28263
28301
  const check3 = (child) => {
28264
28302
  if (result !== PROVEN && child?._zod) {
28265
- const answer = isRecursive(child, stack, resolve8);
28303
+ const answer = isRecursive(child, stack, resolve9);
28266
28304
  if (answer > result)
28267
28305
  result = answer;
28268
28306
  }
@@ -28273,7 +28311,7 @@ function isRecursive(inst, stack, resolve8) {
28273
28311
  const desc = Object.getOwnPropertyDescriptor(sh, key);
28274
28312
  if (spread && !desc.enumerable)
28275
28313
  continue;
28276
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve8) : NONE;
28314
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve9) : NONE;
28277
28315
  if (child > answer)
28278
28316
  answer = child;
28279
28317
  }
@@ -28337,7 +28375,7 @@ function isRecursive(inst, stack, resolve8) {
28337
28375
  break;
28338
28376
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
28339
28377
  case "lazy": {
28340
- const inner = def._cachedInner ?? (resolve8 ? inst._zod.innerType : void 0);
28378
+ const inner = def._cachedInner ?? (resolve9 ? inst._zod.innerType : void 0);
28341
28379
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
28342
28380
  break;
28343
28381
  }
@@ -46930,7 +46968,7 @@ var Protocol = class {
46930
46968
  return;
46931
46969
  }
46932
46970
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
46933
- await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
46971
+ await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
46934
46972
  options?.signal?.throwIfAborted();
46935
46973
  }
46936
46974
  } catch (error62) {
@@ -46947,7 +46985,7 @@ var Protocol = class {
46947
46985
  */
46948
46986
  request(request, resultSchema, options) {
46949
46987
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
46950
- return new Promise((resolve8, reject) => {
46988
+ return new Promise((resolve9, reject) => {
46951
46989
  const earlyReject = (error62) => {
46952
46990
  reject(error62);
46953
46991
  };
@@ -47025,7 +47063,7 @@ var Protocol = class {
47025
47063
  if (!parseResult.success) {
47026
47064
  reject(parseResult.error);
47027
47065
  } else {
47028
- resolve8(parseResult.data);
47066
+ resolve9(parseResult.data);
47029
47067
  }
47030
47068
  } catch (error62) {
47031
47069
  reject(error62);
@@ -47287,12 +47325,12 @@ var Protocol = class {
47287
47325
  }
47288
47326
  } catch {
47289
47327
  }
47290
- return new Promise((resolve8, reject) => {
47328
+ return new Promise((resolve9, reject) => {
47291
47329
  if (signal.aborted) {
47292
47330
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
47293
47331
  return;
47294
47332
  }
47295
- const timeoutId = setTimeout(resolve8, interval);
47333
+ const timeoutId = setTimeout(resolve9, interval);
47296
47334
  signal.addEventListener("abort", () => {
47297
47335
  clearTimeout(timeoutId);
47298
47336
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -48435,7 +48473,7 @@ var McpServer = class {
48435
48473
  let task = createTaskResult.task;
48436
48474
  const pollInterval = task.pollInterval ?? 5e3;
48437
48475
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
48438
- await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
48476
+ await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
48439
48477
  const updatedTask = await extra.taskStore.getTask(taskId);
48440
48478
  if (!updatedTask) {
48441
48479
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -49101,12 +49139,12 @@ var StdioServerTransport = class {
49101
49139
  this.onclose?.();
49102
49140
  }
49103
49141
  send(message) {
49104
- return new Promise((resolve8) => {
49142
+ return new Promise((resolve9) => {
49105
49143
  const json2 = serializeMessage(message);
49106
49144
  if (this._stdout.write(json2)) {
49107
- resolve8();
49145
+ resolve9();
49108
49146
  } else {
49109
- this._stdout.once("drain", resolve8);
49147
+ this._stdout.once("drain", resolve9);
49110
49148
  }
49111
49149
  });
49112
49150
  }
@@ -49271,6 +49309,33 @@ var ConfigSchema = external_exports.object({
49271
49309
  }).optional()
49272
49310
  })
49273
49311
  ).default([])
49312
+ }).prefault({}),
49313
+ usage: external_exports.object({
49314
+ /** Ingest Claude Code transcripts (tokens, cache, cost per model call) and record statusline snapshots. */
49315
+ enabled: external_exports.boolean().default(true),
49316
+ /** Soft budgets; crossing one adds a one-line warning to the next prompt (never blocks). */
49317
+ budgets: external_exports.object({
49318
+ sessionUsd: external_exports.number().positive().optional(),
49319
+ dailyUsd: external_exports.number().positive().optional(),
49320
+ /** A single API call above this is flagged as an expensive turn. */
49321
+ turnUsd: external_exports.number().positive().optional()
49322
+ }).prefault({}),
49323
+ /**
49324
+ * Price overrides per model, USD per million tokens, matched by substring of the model id
49325
+ * (first match wins). Missing fields fall back to the bundled table.
49326
+ */
49327
+ pricing: external_exports.record(
49328
+ external_exports.string(),
49329
+ external_exports.object({
49330
+ input: external_exports.number().nonnegative().optional(),
49331
+ output: external_exports.number().nonnegative().optional(),
49332
+ cacheWrite5m: external_exports.number().nonnegative().optional(),
49333
+ cacheWrite1h: external_exports.number().nonnegative().optional(),
49334
+ cacheRead: external_exports.number().nonnegative().optional()
49335
+ })
49336
+ ).default({}),
49337
+ /** Show cost, context and plan usage segments in `eminiar statusline`. */
49338
+ statusline: external_exports.boolean().default(true)
49274
49339
  }).prefault({})
49275
49340
  });
49276
49341
  function readJson(file3) {
@@ -49520,6 +49585,10 @@ function topTermsOf(bodies, limit) {
49520
49585
 
49521
49586
  // src/storage/sqlite/schema.ts
49522
49587
  init_esm_shims();
49588
+ function hasColumn(db, table, column) {
49589
+ const cols = db.pragma(`table_info(${table})`);
49590
+ return cols.some((c) => c.name === column);
49591
+ }
49523
49592
  var migrations = [
49524
49593
  {
49525
49594
  version: 1,
@@ -49781,6 +49850,116 @@ CREATE TABLE IF NOT EXISTS index_runs (
49781
49850
  detail TEXT
49782
49851
  );
49783
49852
  `
49853
+ },
49854
+ {
49855
+ // Usage insights: api_calls mirrors one API request per Claude Code transcript entry
49856
+ // (deduped on request_id + message_id; cost cached at ingest), tool_uses pairs each
49857
+ // tool_use block with its tool_result size, context_events records compactions,
49858
+ // transcript_files keeps the byte offset so ingest only reads new bytes, and
49859
+ // statusline_snapshots stores what `eminiar statusline` saw. Column names = row fields.
49860
+ version: 5,
49861
+ sql: `
49862
+ CREATE TABLE IF NOT EXISTS api_calls (
49863
+ request_id TEXT NOT NULL,
49864
+ message_id TEXT NOT NULL,
49865
+ session_id TEXT NOT NULL,
49866
+ agent_id TEXT,
49867
+ sidechain INTEGER NOT NULL DEFAULT 0,
49868
+ ts INTEGER NOT NULL,
49869
+ model TEXT NOT NULL,
49870
+ input_tokens INTEGER NOT NULL DEFAULT 0,
49871
+ output_tokens INTEGER NOT NULL DEFAULT 0,
49872
+ cache_create_5m INTEGER NOT NULL DEFAULT 0,
49873
+ cache_create_1h INTEGER NOT NULL DEFAULT 0,
49874
+ cache_read INTEGER NOT NULL DEFAULT 0,
49875
+ thinking_tokens INTEGER NOT NULL DEFAULT 0,
49876
+ web_searches INTEGER NOT NULL DEFAULT 0,
49877
+ web_fetches INTEGER NOT NULL DEFAULT 0,
49878
+ service_tier TEXT,
49879
+ speed TEXT,
49880
+ stop_reason TEXT,
49881
+ prompt_id TEXT,
49882
+ git_branch TEXT,
49883
+ cwd TEXT,
49884
+ effort TEXT,
49885
+ version TEXT,
49886
+ entrypoint TEXT,
49887
+ cost_usd REAL,
49888
+ priced INTEGER NOT NULL DEFAULT 0,
49889
+ PRIMARY KEY (request_id, message_id)
49890
+ );
49891
+ CREATE INDEX IF NOT EXISTS api_calls_session_ts ON api_calls(session_id, ts);
49892
+ CREATE INDEX IF NOT EXISTS api_calls_ts ON api_calls(ts);
49893
+
49894
+ CREATE TABLE IF NOT EXISTS tool_uses (
49895
+ tool_use_id TEXT PRIMARY KEY,
49896
+ session_id TEXT NOT NULL,
49897
+ agent_id TEXT,
49898
+ ts INTEGER NOT NULL,
49899
+ tool TEXT NOT NULL,
49900
+ input_chars INTEGER NOT NULL DEFAULT 0,
49901
+ result_chars INTEGER,
49902
+ is_error INTEGER NOT NULL DEFAULT 0,
49903
+ request_id TEXT
49904
+ );
49905
+ CREATE INDEX IF NOT EXISTS tool_uses_session_ts ON tool_uses(session_id, ts);
49906
+ CREATE INDEX IF NOT EXISTS tool_uses_ts ON tool_uses(ts);
49907
+
49908
+ CREATE TABLE IF NOT EXISTS context_events (
49909
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
49910
+ session_id TEXT NOT NULL,
49911
+ ts INTEGER NOT NULL,
49912
+ kind TEXT NOT NULL,
49913
+ trigger TEXT,
49914
+ pre_tokens INTEGER,
49915
+ post_tokens INTEGER
49916
+ );
49917
+ CREATE INDEX IF NOT EXISTS context_events_session_ts ON context_events(session_id, ts);
49918
+ CREATE INDEX IF NOT EXISTS context_events_ts ON context_events(ts);
49919
+
49920
+ CREATE TABLE IF NOT EXISTS transcript_files (
49921
+ path TEXT PRIMARY KEY,
49922
+ session_id TEXT,
49923
+ offset INTEGER NOT NULL DEFAULT 0,
49924
+ size INTEGER NOT NULL DEFAULT 0,
49925
+ mtime INTEGER NOT NULL DEFAULT 0,
49926
+ ingested_at INTEGER NOT NULL
49927
+ );
49928
+
49929
+ CREATE TABLE IF NOT EXISTS statusline_snapshots (
49930
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
49931
+ session_id TEXT NOT NULL,
49932
+ ts INTEGER NOT NULL,
49933
+ model_id TEXT,
49934
+ cost_usd REAL,
49935
+ api_ms INTEGER,
49936
+ duration_ms INTEGER,
49937
+ ctx_input_tokens INTEGER,
49938
+ ctx_output_tokens INTEGER,
49939
+ ctx_size INTEGER,
49940
+ ctx_used_pct REAL,
49941
+ cache_warm INTEGER,
49942
+ cache_ttl TEXT,
49943
+ five_hour_pct REAL,
49944
+ five_hour_resets_at INTEGER,
49945
+ seven_day_pct REAL,
49946
+ seven_day_resets_at INTEGER,
49947
+ lines_added INTEGER,
49948
+ lines_removed INTEGER
49949
+ );
49950
+ CREATE INDEX IF NOT EXISTS statusline_snapshots_session_ts ON statusline_snapshots(session_id, ts);
49951
+ CREATE INDEX IF NOT EXISTS statusline_snapshots_ts ON statusline_snapshots(ts);
49952
+ `
49953
+ },
49954
+ {
49955
+ // transcript_files.head_hash (fingerprint of the file head, so a rewrite with the same
49956
+ // size is noticed) was first added to v5 after v5 had already run on real databases, so
49957
+ // version-5 databases exist both with and without the column. Add it only when missing.
49958
+ version: 6,
49959
+ up(db) {
49960
+ if (!hasColumn(db, "transcript_files", "head_hash"))
49961
+ db.exec("ALTER TABLE transcript_files ADD COLUMN head_hash TEXT");
49962
+ }
49784
49963
  }
49785
49964
  ];
49786
49965
  function vecTableSql(dims) {
@@ -49819,6 +49998,27 @@ function filterSql(f) {
49819
49998
  }
49820
49999
  return { where: clauses.length ? ` AND ${clauses.join(" AND ")}` : "", params };
49821
50000
  }
50001
+ function usageWhere(f, opts = {}) {
50002
+ const clauses = ["1=1"];
50003
+ const params = [];
50004
+ if (f.sessionId) {
50005
+ clauses.push("session_id = ?");
50006
+ params.push(f.sessionId);
50007
+ }
50008
+ if (f.since !== void 0) {
50009
+ clauses.push("ts >= ?");
50010
+ params.push(f.since);
50011
+ }
50012
+ if (f.until !== void 0) {
50013
+ clauses.push("ts < ?");
50014
+ params.push(f.until);
50015
+ }
50016
+ if (f.sidechain !== void 0 && opts.sidechain !== null) {
50017
+ const expr = opts.sidechain ?? "sidechain = 1";
50018
+ clauses.push(f.sidechain ? expr : `NOT (${expr})`);
50019
+ }
50020
+ return { where: clauses.join(" AND "), params };
50021
+ }
49822
50022
  var SqliteAdapter = class _SqliteAdapter {
49823
50023
  constructor(path3, opts = {}) {
49824
50024
  this.path = path3;
@@ -49871,7 +50071,8 @@ var SqliteAdapter = class _SqliteAdapter {
49871
50071
  if (pending2.length) {
49872
50072
  this.transaction(() => {
49873
50073
  for (const m of pending2) {
49874
- this.db.exec(m.sql);
50074
+ if (m.sql) this.db.exec(m.sql);
50075
+ m.up?.(this.db);
49875
50076
  this.metaSet("schema_version", String(m.version));
49876
50077
  }
49877
50078
  });
@@ -50090,18 +50291,42 @@ var SqliteAdapter = class _SqliteAdapter {
50090
50291
  }
50091
50292
  vecSearch(embedding, k, filters) {
50092
50293
  if (!this.vectors) return [];
50093
- const raw = this.db.prepare(
50094
- "SELECT chunk_id id, distance FROM chunk_vec WHERE embedding MATCH ? AND k = ? ORDER BY distance"
50095
- ).all(embedding, BigInt(filters ? k * 4 : k));
50096
- if (!filters || !raw.length) return raw.map((r) => ({ chunkId: r.id, distance: r.distance }));
50097
50294
  const { where, params } = filterSql(filters);
50098
- const allowed = new Set(
50099
- this.db.prepare(
50100
- `SELECT c.id FROM chunks c JOIN documents d ON d.id = c.document_id
50101
- WHERE c.id IN (${raw.map(() => "?").join(",")})${where}`
50102
- ).all(...raw.map((r) => r.id), ...params).map((r) => r.id)
50103
- );
50104
- return raw.filter((r) => allowed.has(r.id)).slice(0, k).map((r) => ({ chunkId: r.id, distance: r.distance }));
50295
+ if (!where) return this.knn(embedding, k);
50296
+ try {
50297
+ return this.knn(
50298
+ embedding,
50299
+ k,
50300
+ `AND chunk_id IN (SELECT c.id FROM chunks c JOIN documents d ON d.id = c.document_id WHERE 1=1${where})`,
50301
+ params
50302
+ );
50303
+ } catch {
50304
+ return this.vecSearchWidening(embedding, k, where, params);
50305
+ }
50306
+ }
50307
+ knn(embedding, k, extra = "", params = []) {
50308
+ return this.db.prepare(
50309
+ `SELECT chunk_id id, distance FROM chunk_vec WHERE embedding MATCH ? AND k = ? ${extra} ORDER BY distance`
50310
+ ).all(embedding, BigInt(k), ...params).map((r) => ({ chunkId: r.id, distance: r.distance }));
50311
+ }
50312
+ /** Fallback filtered KNN: fetch k*4, k*16, ... nearest until k pass the filter or all were seen. */
50313
+ vecSearchWidening(embedding, k, where, params) {
50314
+ const total = this.vecCount();
50315
+ for (let fetch2 = k * 4; ; fetch2 *= 4) {
50316
+ const raw = this.knn(embedding, Math.min(fetch2, total));
50317
+ if (!raw.length) return [];
50318
+ const allowed = /* @__PURE__ */ new Set();
50319
+ for (let i = 0; i < raw.length; i += 400) {
50320
+ const ids = raw.slice(i, i + 400).map((r) => r.chunkId);
50321
+ for (const r of this.db.prepare(
50322
+ `SELECT c.id FROM chunks c JOIN documents d ON d.id = c.document_id
50323
+ WHERE c.id IN (${ids.map(() => "?").join(",")})${where}`
50324
+ ).all(...ids, ...params))
50325
+ allowed.add(r.id);
50326
+ }
50327
+ const hits = raw.filter((r) => allowed.has(r.chunkId)).slice(0, k);
50328
+ if (hits.length >= k || fetch2 >= total) return hits;
50329
+ }
50105
50330
  }
50106
50331
  unembeddedChunkIds(limit) {
50107
50332
  return this.db.prepare("SELECT id FROM chunks WHERE embedded = 0 ORDER BY id LIMIT ?").all(limit).map((r) => r.id);
@@ -50380,6 +50605,26 @@ var SqliteAdapter = class _SqliteAdapter {
50380
50605
  }
50381
50606
  return out;
50382
50607
  }
50608
+ graphDependentFileCounts(paths2, kinds) {
50609
+ const out = /* @__PURE__ */ new Map();
50610
+ if (!paths2.length || !kinds.length) return out;
50611
+ const kph = kinds.map(() => "?").join(",");
50612
+ for (let i = 0; i < paths2.length; i += 400) {
50613
+ const batch = paths2.slice(i, i + 400);
50614
+ const pph = batch.map(() => "?").join(",");
50615
+ for (const r of this.db.prepare(
50616
+ `SELECT d.path p, COUNT(DISTINCT s.path) n
50617
+ FROM graph_nodes d
50618
+ JOIN graph_edges e ON e.dst = d.id AND e.kind IN (${kph})
50619
+ JOIN graph_nodes s ON s.id = e.src
50620
+ WHERE d.path IN (${pph}) AND s.path IS NOT NULL AND s.path <> d.path
50621
+ GROUP BY d.path`
50622
+ ).all(...kinds, ...batch)) {
50623
+ out.set(r.p, r.n);
50624
+ }
50625
+ }
50626
+ return out;
50627
+ }
50383
50628
  edgesWhere(col, ids, kinds) {
50384
50629
  if (!ids.length) return [];
50385
50630
  const out = [];
@@ -50694,6 +50939,233 @@ var SqliteAdapter = class _SqliteAdapter {
50694
50939
  memoryCount() {
50695
50940
  return this.db.prepare("SELECT COUNT(*) n FROM memories").get().n;
50696
50941
  }
50942
+ // --- usage: model calls, tool results, context events, statusline ------------
50943
+ apiCallsUpsert(rows) {
50944
+ if (!rows.length) return 0;
50945
+ const ins = this.prep(
50946
+ `INSERT INTO api_calls (request_id, message_id, session_id, agent_id, sidechain, ts, model,
50947
+ input_tokens, output_tokens, cache_create_5m, cache_create_1h, cache_read, thinking_tokens,
50948
+ web_searches, web_fetches, service_tier, speed, stop_reason, prompt_id, git_branch, cwd, effort,
50949
+ version, entrypoint, cost_usd, priced)
50950
+ VALUES (@request_id, @message_id, @session_id, @agent_id, @sidechain, @ts, @model,
50951
+ @input_tokens, @output_tokens, @cache_create_5m, @cache_create_1h, @cache_read, @thinking_tokens,
50952
+ @web_searches, @web_fetches, @service_tier, @speed, @stop_reason, @prompt_id, @git_branch, @cwd, @effort,
50953
+ @version, @entrypoint, @cost_usd, @priced)
50954
+ ON CONFLICT(request_id, message_id) DO UPDATE SET
50955
+ cost_usd = CASE WHEN excluded.output_tokens >= api_calls.output_tokens THEN excluded.cost_usd ELSE api_calls.cost_usd END,
50956
+ priced = CASE WHEN excluded.output_tokens >= api_calls.output_tokens THEN excluded.priced ELSE api_calls.priced END,
50957
+ input_tokens = MAX(api_calls.input_tokens, excluded.input_tokens),
50958
+ output_tokens = MAX(api_calls.output_tokens, excluded.output_tokens),
50959
+ cache_create_5m = MAX(api_calls.cache_create_5m, excluded.cache_create_5m),
50960
+ cache_create_1h = MAX(api_calls.cache_create_1h, excluded.cache_create_1h),
50961
+ cache_read = MAX(api_calls.cache_read, excluded.cache_read),
50962
+ thinking_tokens = MAX(api_calls.thinking_tokens, excluded.thinking_tokens),
50963
+ web_searches = MAX(api_calls.web_searches, excluded.web_searches),
50964
+ web_fetches = MAX(api_calls.web_fetches, excluded.web_fetches),
50965
+ stop_reason = COALESCE(excluded.stop_reason, api_calls.stop_reason),
50966
+ service_tier = COALESCE(excluded.service_tier, api_calls.service_tier),
50967
+ speed = COALESCE(excluded.speed, api_calls.speed),
50968
+ prompt_id = COALESCE(api_calls.prompt_id, excluded.prompt_id),
50969
+ effort = COALESCE(api_calls.effort, excluded.effort)`
50970
+ );
50971
+ const count = this.prep("SELECT COUNT(*) AS n FROM api_calls");
50972
+ return this.transaction(() => {
50973
+ const before = count.get().n;
50974
+ for (const r of rows) {
50975
+ ins.run({
50976
+ request_id: r.request_id,
50977
+ message_id: r.message_id,
50978
+ session_id: r.session_id,
50979
+ agent_id: r.agent_id ?? null,
50980
+ sidechain: r.sidechain ? 1 : 0,
50981
+ ts: r.ts,
50982
+ model: r.model,
50983
+ input_tokens: r.input_tokens,
50984
+ output_tokens: r.output_tokens,
50985
+ cache_create_5m: r.cache_create_5m,
50986
+ cache_create_1h: r.cache_create_1h,
50987
+ cache_read: r.cache_read,
50988
+ thinking_tokens: r.thinking_tokens,
50989
+ web_searches: r.web_searches,
50990
+ web_fetches: r.web_fetches,
50991
+ service_tier: r.service_tier ?? null,
50992
+ speed: r.speed ?? null,
50993
+ stop_reason: r.stop_reason ?? null,
50994
+ prompt_id: r.prompt_id ?? null,
50995
+ git_branch: r.git_branch ?? null,
50996
+ cwd: r.cwd ?? null,
50997
+ effort: r.effort ?? null,
50998
+ version: r.version ?? null,
50999
+ entrypoint: r.entrypoint ?? null,
51000
+ cost_usd: r.cost_usd ?? null,
51001
+ priced: r.priced ?? 0
51002
+ });
51003
+ }
51004
+ return count.get().n - before;
51005
+ });
51006
+ }
51007
+ apiCalls(filter = {}) {
51008
+ const { where, params } = usageWhere(filter);
51009
+ params.push(filter.limit ?? 1e5);
51010
+ return this.prep(
51011
+ `SELECT * FROM api_calls WHERE ${where} ORDER BY ts ASC, rowid ASC LIMIT ?`
51012
+ ).all(...params);
51013
+ }
51014
+ apiCallsLatestTs() {
51015
+ const row = this.prep("SELECT MAX(ts) AS ts FROM api_calls").get();
51016
+ return row?.ts ?? void 0;
51017
+ }
51018
+ toolUsesUpsert(rows) {
51019
+ if (!rows.length) return 0;
51020
+ const ins = this.prep(
51021
+ `INSERT INTO tool_uses (tool_use_id, session_id, agent_id, ts, tool, input_chars, result_chars, is_error, request_id)
51022
+ VALUES (@tool_use_id, @session_id, @agent_id, @ts, @tool, @input_chars, @result_chars, @is_error, @request_id)
51023
+ ON CONFLICT(tool_use_id) DO UPDATE SET
51024
+ tool = CASE WHEN tool_uses.tool = '' THEN excluded.tool ELSE tool_uses.tool END,
51025
+ input_chars = MAX(tool_uses.input_chars, excluded.input_chars),
51026
+ result_chars = COALESCE(excluded.result_chars, tool_uses.result_chars),
51027
+ is_error = MAX(tool_uses.is_error, excluded.is_error),
51028
+ request_id = COALESCE(tool_uses.request_id, excluded.request_id),
51029
+ agent_id = COALESCE(tool_uses.agent_id, excluded.agent_id),
51030
+ ts = MIN(tool_uses.ts, excluded.ts)`
51031
+ );
51032
+ const count = this.prep("SELECT COUNT(*) AS n FROM tool_uses");
51033
+ return this.transaction(() => {
51034
+ const before = count.get().n;
51035
+ for (const r of rows) {
51036
+ ins.run({
51037
+ tool_use_id: r.tool_use_id,
51038
+ session_id: r.session_id,
51039
+ agent_id: r.agent_id ?? null,
51040
+ ts: r.ts,
51041
+ tool: r.tool,
51042
+ input_chars: r.input_chars,
51043
+ result_chars: r.result_chars ?? null,
51044
+ is_error: r.is_error ? 1 : 0,
51045
+ request_id: r.request_id ?? null
51046
+ });
51047
+ }
51048
+ return count.get().n - before;
51049
+ });
51050
+ }
51051
+ toolUses(filter = {}) {
51052
+ const { where, params } = usageWhere(filter, { sidechain: "agent_id IS NOT NULL" });
51053
+ params.push(filter.limit ?? 1e5);
51054
+ return this.prep(
51055
+ `SELECT * FROM tool_uses WHERE ${where} ORDER BY ts ASC, rowid ASC LIMIT ?`
51056
+ ).all(...params);
51057
+ }
51058
+ contextEventInsert(e) {
51059
+ const info = this.prep(
51060
+ `INSERT INTO context_events (session_id, ts, kind, trigger, pre_tokens, post_tokens)
51061
+ VALUES (?, ?, ?, ?, ?, ?)`
51062
+ ).run(
51063
+ e.session_id,
51064
+ e.ts,
51065
+ e.kind,
51066
+ e.trigger ?? null,
51067
+ e.pre_tokens ?? null,
51068
+ e.post_tokens ?? null
51069
+ );
51070
+ return Number(info.lastInsertRowid);
51071
+ }
51072
+ contextEvents(filter = {}) {
51073
+ const { where, params } = usageWhere(filter, { sidechain: null });
51074
+ params.push(filter.limit ?? 1e5);
51075
+ return this.prep(
51076
+ `SELECT * FROM context_events WHERE ${where} ORDER BY ts ASC, id ASC LIMIT ?`
51077
+ ).all(...params);
51078
+ }
51079
+ transcriptFileGet(path3) {
51080
+ return this.prep("SELECT * FROM transcript_files WHERE path = ?").get(path3);
51081
+ }
51082
+ transcriptFileSet(row) {
51083
+ this.prep(
51084
+ `INSERT INTO transcript_files (path, session_id, offset, size, mtime, head_hash, ingested_at)
51085
+ VALUES (@path, @session_id, @offset, @size, @mtime, @head_hash, @ingested_at)
51086
+ ON CONFLICT(path) DO UPDATE SET
51087
+ session_id = COALESCE(excluded.session_id, transcript_files.session_id),
51088
+ offset = excluded.offset, size = excluded.size, mtime = excluded.mtime,
51089
+ head_hash = excluded.head_hash, ingested_at = excluded.ingested_at`
51090
+ ).run({
51091
+ path: row.path,
51092
+ session_id: row.session_id ?? null,
51093
+ offset: row.offset,
51094
+ size: row.size,
51095
+ mtime: row.mtime,
51096
+ head_hash: row.head_hash ?? null,
51097
+ ingested_at: row.ingested_at
51098
+ });
51099
+ }
51100
+ transcriptFiles() {
51101
+ return this.prep("SELECT * FROM transcript_files ORDER BY path").all();
51102
+ }
51103
+ statuslineSnapshotInsert(s) {
51104
+ const info = this.prep(
51105
+ `INSERT INTO statusline_snapshots (session_id, ts, model_id, cost_usd, api_ms, duration_ms,
51106
+ ctx_input_tokens, ctx_output_tokens, ctx_size, ctx_used_pct, cache_warm, cache_ttl,
51107
+ five_hour_pct, five_hour_resets_at, seven_day_pct, seven_day_resets_at, lines_added, lines_removed)
51108
+ VALUES (@session_id, @ts, @model_id, @cost_usd, @api_ms, @duration_ms,
51109
+ @ctx_input_tokens, @ctx_output_tokens, @ctx_size, @ctx_used_pct, @cache_warm, @cache_ttl,
51110
+ @five_hour_pct, @five_hour_resets_at, @seven_day_pct, @seven_day_resets_at, @lines_added, @lines_removed)`
51111
+ ).run({
51112
+ session_id: s.session_id,
51113
+ ts: s.ts,
51114
+ model_id: s.model_id ?? null,
51115
+ cost_usd: s.cost_usd ?? null,
51116
+ api_ms: s.api_ms ?? null,
51117
+ duration_ms: s.duration_ms ?? null,
51118
+ ctx_input_tokens: s.ctx_input_tokens ?? null,
51119
+ ctx_output_tokens: s.ctx_output_tokens ?? null,
51120
+ ctx_size: s.ctx_size ?? null,
51121
+ ctx_used_pct: s.ctx_used_pct ?? null,
51122
+ cache_warm: s.cache_warm ?? null,
51123
+ cache_ttl: s.cache_ttl ?? null,
51124
+ five_hour_pct: s.five_hour_pct ?? null,
51125
+ five_hour_resets_at: s.five_hour_resets_at ?? null,
51126
+ seven_day_pct: s.seven_day_pct ?? null,
51127
+ seven_day_resets_at: s.seven_day_resets_at ?? null,
51128
+ lines_added: s.lines_added ?? null,
51129
+ lines_removed: s.lines_removed ?? null
51130
+ });
51131
+ return Number(info.lastInsertRowid);
51132
+ }
51133
+ statuslineSnapshots(filter = {}) {
51134
+ const { where, params } = usageWhere(filter, { sidechain: null });
51135
+ params.push(filter.limit ?? 1e5);
51136
+ return this.prep(
51137
+ `SELECT * FROM statusline_snapshots WHERE ${where} ORDER BY ts ASC, id ASC LIMIT ?`
51138
+ ).all(...params);
51139
+ }
51140
+ statuslineLatest(sessionId) {
51141
+ if (sessionId) {
51142
+ return this.prep(
51143
+ "SELECT * FROM statusline_snapshots WHERE session_id = ? ORDER BY ts DESC, id DESC LIMIT 1"
51144
+ ).get(sessionId);
51145
+ }
51146
+ return this.prep(
51147
+ "SELECT * FROM statusline_snapshots ORDER BY ts DESC, id DESC LIMIT 1"
51148
+ ).get();
51149
+ }
51150
+ statuslineSnapshotsPrune(opts) {
51151
+ const keep = Math.max(0, Math.floor(opts.keepPerSession));
51152
+ return this.transaction(() => {
51153
+ let n = this.prep("DELETE FROM statusline_snapshots WHERE ts < ?").run(
51154
+ opts.olderThan
51155
+ ).changes;
51156
+ n += this.prep(
51157
+ `DELETE FROM statusline_snapshots WHERE id IN (
51158
+ SELECT s.id FROM statusline_snapshots s
51159
+ WHERE s.id NOT IN (
51160
+ SELECT k.id FROM statusline_snapshots k
51161
+ WHERE k.session_id = s.session_id
51162
+ ORDER BY k.ts DESC, k.id DESC LIMIT ?
51163
+ )
51164
+ )`
51165
+ ).run(keep).changes;
51166
+ return n;
51167
+ });
51168
+ }
50697
51169
  // --- misc --------------------------------------------------------------
50698
51170
  metaGet(key) {
50699
51171
  const row = this.db.prepare("SELECT value FROM meta WHERE key = ?").get(key);
@@ -50724,6 +51196,11 @@ var SqliteAdapter = class _SqliteAdapter {
50724
51196
  "DELETE FROM graph_edges; DELETE FROM graph_nodes; DELETE FROM graph_metrics;"
50725
51197
  );
50726
51198
  }
51199
+ if (scope === "usage" || scope === "all") {
51200
+ this.db.exec(
51201
+ "DELETE FROM api_calls; DELETE FROM tool_uses; DELETE FROM context_events; DELETE FROM transcript_files; DELETE FROM statusline_snapshots;"
51202
+ );
51203
+ }
50727
51204
  if (scope === "memories" || scope === "all") {
50728
51205
  this.db.exec("DELETE FROM memories;");
50729
51206
  if (scope === "memories") {
@@ -50813,7 +51290,7 @@ function impact(db, ref, opts = {}) {
50813
51290
  const depth = opts.depth ?? 2;
50814
51291
  const kinds = opts.edgeKinds ?? STRUCTURAL;
50815
51292
  const hits = /* @__PURE__ */ new Map();
50816
- const add = (id, score, hops, reason) => {
51293
+ const add2 = (id, score, hops, reason) => {
50817
51294
  const key = toFile(id);
50818
51295
  if (key === toFile(root.id)) return;
50819
51296
  const h = hits.get(key) ?? { id: key, score: 0, hops, reasons: [] };
@@ -50830,7 +51307,7 @@ function impact(db, ref, opts = {}) {
50830
51307
  const next = [];
50831
51308
  for (const e of incoming) {
50832
51309
  const via = e.dst === root.id ? root.name : e.dst.replace(/^sym:[^#]*#/, "");
50833
- add(
51310
+ add2(
50834
51311
  e.src,
50835
51312
  e.confidence * e.weight / hop,
50836
51313
  hop,
@@ -50847,14 +51324,14 @@ function impact(db, ref, opts = {}) {
50847
51324
  for (const e of db.graphEdgesTo(start, ["mentions", "documents"])) {
50848
51325
  const via = e.dst === root.id ? root.name : e.dst.replace(/^sym:[^#]*#/, "");
50849
51326
  const who = e.kind === "documents" ? "doc documents" : "memory mentions";
50850
- add(e.src, 0.5 * e.confidence, 1, `${who} ${via}`);
51327
+ add2(e.src, 0.5 * e.confidence, 1, `${who} ${via}`);
50851
51328
  }
50852
51329
  if (opts.includeGit !== false) {
50853
51330
  const file3 = toFile(root.id);
50854
51331
  for (const e of db.graphEdgesFrom([file3], ["co_changes"])) {
50855
51332
  if (e.weight < 0.3) continue;
50856
51333
  const meta4 = e.meta ? JSON.parse(e.meta) : {};
50857
- add(e.dst, e.weight, 1, `co-changed ${meta4.count ?? "?"}\xD7 (${Math.round(e.weight * 100)}%)`);
51334
+ add2(e.dst, e.weight, 1, `co-changed ${meta4.count ?? "?"}\xD7 (${Math.round(e.weight * 100)}%)`);
50858
51335
  }
50859
51336
  }
50860
51337
  const list2 = [...hits.values()].sort((a, b) => b.score - a.score);
@@ -51198,7 +51675,7 @@ function pkgJson(root) {
51198
51675
  }
51199
51676
  function folderRows(files, languages2) {
51200
51677
  const acc = /* @__PURE__ */ new Map();
51201
- const add = (key, r) => {
51678
+ const add2 = (key, r) => {
51202
51679
  const a = acc.get(key) ?? { files: 0, loc: 0, score: 0, langs: {} };
51203
51680
  a.files++;
51204
51681
  a.loc += r.loc;
@@ -51209,8 +51686,8 @@ function folderRows(files, languages2) {
51209
51686
  };
51210
51687
  for (const r of files) {
51211
51688
  const parts = (r.path ?? "").split("/");
51212
- add(parts.length > 1 ? `${parts[0]}/` : "(root)", r);
51213
- if (parts.length > 2) add(`${parts[0]}/${parts[1]}/`, r);
51689
+ add2(parts.length > 1 ? `${parts[0]}/` : "(root)", r);
51690
+ if (parts.length > 2) add2(`${parts[0]}/${parts[1]}/`, r);
51214
51691
  }
51215
51692
  const rows = [...acc.entries()].map(([path3, a]) => ({
51216
51693
  path: path3,
@@ -52283,6 +52760,25 @@ function findOnPath(bin) {
52283
52760
  }
52284
52761
  return void 0;
52285
52762
  }
52763
+ var REQUIRE_PREAMBLE = 'import { createRequire } from "node:module"; const require = createRequire(process.env.EMINIAR_PROJECT_ROOT ? process.env.EMINIAR_PROJECT_ROOT + "/package.json" : import.meta.url);';
52764
+ function requirePreamble(code) {
52765
+ if (!/\brequire\s*\(/.test(code)) return void 0;
52766
+ if (/\b(?:const|let|var|function)\s+require\b/.test(code) || /\bcreateRequire\b/.test(code)) {
52767
+ return void 0;
52768
+ }
52769
+ return `${REQUIRE_PREAMBLE}
52770
+ `;
52771
+ }
52772
+ function prepareCode(lang, code) {
52773
+ const pre = lang.preamble?.(code);
52774
+ if (!pre) return code;
52775
+ if (code.startsWith("#!")) {
52776
+ const nl = code.indexOf("\n");
52777
+ return nl === -1 ? `${code}
52778
+ ${pre}` : code.slice(0, nl + 1) + pre + code.slice(nl + 1);
52779
+ }
52780
+ return pre + code;
52781
+ }
52286
52782
  function preferredShell() {
52287
52783
  const sh = process.env.SHELL ?? "";
52288
52784
  if (/\/(zsh|bash)$/.test(sh) && existsSync11(sh)) return sh;
@@ -52315,14 +52811,16 @@ var LANGUAGES = [
52315
52811
  aliases: ["js", "node", "mjs"],
52316
52812
  ext: ".mjs",
52317
52813
  detect: () => process.execPath,
52318
- argv: (bin, file3) => [bin, "--no-warnings", file3]
52814
+ argv: (bin, file3) => [bin, "--no-warnings", file3],
52815
+ preamble: requirePreamble
52319
52816
  },
52320
52817
  {
52321
52818
  id: "typescript",
52322
52819
  aliases: ["ts"],
52323
52820
  ext: ".ts",
52324
52821
  detect: () => process.execPath,
52325
- argv: (bin, file3) => [bin, "--no-warnings", tsxCli(), file3]
52822
+ argv: (bin, file3) => [bin, "--no-warnings", tsxCli(), file3],
52823
+ preamble: requirePreamble
52326
52824
  },
52327
52825
  {
52328
52826
  id: "python",
@@ -52425,7 +52923,7 @@ init_esm_shims();
52425
52923
  import { spawn } from "child_process";
52426
52924
  function runProcess(argv, opts) {
52427
52925
  const started = Date.now();
52428
- return new Promise((resolve8) => {
52926
+ return new Promise((resolve9) => {
52429
52927
  const [cmd, ...args] = argv;
52430
52928
  const child = spawn(cmd, args, {
52431
52929
  cwd: opts.cwd,
@@ -52468,7 +52966,7 @@ function runProcess(argv, opts) {
52468
52966
  if (done) return;
52469
52967
  done = true;
52470
52968
  clearTimeout(timer);
52471
- resolve8({
52969
+ resolve9({
52472
52970
  stdout: Buffer.concat(out).toString("utf8"),
52473
52971
  stderr: errTail,
52474
52972
  exitCode,
@@ -52508,7 +53006,7 @@ async function execute(ctx, input2) {
52508
53006
  );
52509
53007
  }
52510
53008
  }
52511
- const file3 = writeScratch(input2.code, lang.ext);
53009
+ const file3 = writeScratch(prepareCode(lang, input2.code), lang.ext);
52512
53010
  const env = buildEnv(ctx.config.sandbox.envAllow, {
52513
53011
  EMINIAR_PROJECT_ROOT: ctx.root,
52514
53012
  NODE_PATH: [join17(ctx.root, "node_modules"), process.env.NODE_PATH].filter(Boolean).join(":"),
@@ -52958,10 +53456,22 @@ searchable terms: ${terms.join(", ")}`);
52958
53456
  }
52959
53457
 
52960
53458
  // src/tools/em_batch_execute.ts
52961
- var INLINE_LIMIT = 1024;
53459
+ var INLINE_LIMIT_WITH_QUERIES = 1024;
53460
+ var DEFAULT_MAX_TOKENS = 2e3;
53461
+ function chooseInline(lengths, perOutputCap, budgetChars) {
53462
+ const candidates2 = lengths.map((len, i) => ({ len, i })).filter((c) => c.len <= perOutputCap).sort((a, b) => a.len - b.len);
53463
+ const inline = /* @__PURE__ */ new Set();
53464
+ let remaining = budgetChars;
53465
+ for (const c of candidates2) {
53466
+ if (c.len > remaining) break;
53467
+ inline.add(c.i);
53468
+ remaining -= c.len;
53469
+ }
53470
+ return inline;
53471
+ }
52962
53472
  var emBatchExecute = defineTool({
52963
53473
  name: "em_batch_execute",
52964
- description: "Run several labelled commands in parallel, index every output under its label, and answer `queries` against all of them in the same round trip. Ideal for research: git status + recent commits + test output + dependency list in one call. Small outputs are returned inline; large ones only through the query hits.",
53474
+ description: "Run several labelled commands in parallel, index every output under its label, and answer `queries` against all of them in the same round trip. Ideal for research: git status + recent commits + test output + dependency list in one call. Without `queries`/`intent`, each output up to the index threshold (5 KB) is returned inline, within `max_tokens` overall; with them, only outputs up to 1 KB are inlined and the query hits carry the rest. Every output is indexed; an elided one is fetched with em_search {document_ids: [N]}.",
52965
53475
  schema: {
52966
53476
  commands: external_exports.array(
52967
53477
  external_exports.object({
@@ -52982,6 +53492,9 @@ var emBatchExecute = defineTool({
52982
53492
  const concurrency = args.concurrency ?? 4;
52983
53493
  const results = [];
52984
53494
  let rawChars = 0;
53495
+ const queries = [...args.queries ?? []];
53496
+ if (args.intent && !queries.includes(args.intent)) queries.unshift(args.intent);
53497
+ const maxTokens = args.max_tokens ?? DEFAULT_MAX_TOKENS;
52985
53498
  const queue = [...args.commands];
52986
53499
  const worker = async () => {
52987
53500
  for (; ; ) {
@@ -53003,7 +53516,7 @@ var emBatchExecute = defineTool({
53003
53516
  results.push({
53004
53517
  label: cmd.label,
53005
53518
  header: describeRun(r),
53006
- inline: r.stdout.length <= INLINE_LIMIT ? r.stdout : void 0,
53519
+ stdout: r.stdout,
53007
53520
  docId: p.documentId,
53008
53521
  stderr: r.exitCode !== 0 ? r.stderr.trim().slice(-800) : void 0
53009
53522
  });
@@ -53015,15 +53528,21 @@ var emBatchExecute = defineTool({
53015
53528
  await Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, worker));
53016
53529
  const order = new Map(args.commands.map((c, i) => [c.label, i]));
53017
53530
  results.sort((a, b) => (order.get(a.label) ?? 0) - (order.get(b.label) ?? 0));
53531
+ const perOutputCap = queries.length ? INLINE_LIMIT_WITH_QUERIES : ctx.config.sandbox.indexThreshold;
53532
+ const inline = chooseInline(
53533
+ results.map((r) => r.stdout?.length ?? Number.POSITIVE_INFINITY),
53534
+ perOutputCap,
53535
+ maxTokens * 4
53536
+ );
53018
53537
  const lines = [];
53019
- for (const r of results) {
53020
- lines.push(`## ${r.label} \u2014 ${r.header}${r.docId ? ` \xB7 doc ${r.docId}` : ""}`);
53021
- if (r.inline !== void 0) lines.push(r.inline.trimEnd());
53538
+ results.forEach((r, i) => {
53539
+ const shown = inline.has(i) && r.stdout !== void 0;
53540
+ const hint = r.docId && !shown ? ` \xB7 em_search {document_ids: [${r.docId}]} for the output` : "";
53541
+ lines.push(`## ${r.label} \u2014 ${r.header}${r.docId ? ` \xB7 doc ${r.docId}` : ""}${hint}`);
53542
+ if (shown) lines.push(r.stdout.trimEnd());
53022
53543
  if (r.stderr) lines.push(`stderr: ${r.stderr}`);
53023
53544
  lines.push("");
53024
- }
53025
- const queries = [...args.queries ?? []];
53026
- if (args.intent && !queries.includes(args.intent)) queries.unshift(args.intent);
53545
+ });
53027
53546
  const docIds = results.map((r) => r.docId).filter((d) => d !== void 0);
53028
53547
  if (queries.length && docIds.length) {
53029
53548
  const responses = [];
@@ -53033,7 +53552,7 @@ var emBatchExecute = defineTool({
53033
53552
  );
53034
53553
  }
53035
53554
  lines.push("# query hits");
53036
- lines.push(formatResults(responses, args.max_tokens ?? 2e3));
53555
+ lines.push(formatResults(responses, maxTokens));
53037
53556
  }
53038
53557
  const out = lines.join("\n");
53039
53558
  recordStat(ctx, "em_batch_execute", {
@@ -54095,6 +54614,9 @@ var NAME_RE2 = /^[a-z0-9][a-z0-9._-]{0,63}$/;
54095
54614
  function docsTag(name) {
54096
54615
  return `${DOCS_PREFIX}${name}`;
54097
54616
  }
54617
+ function nameFromTag(tag) {
54618
+ return tag.startsWith(DOCS_PREFIX) ? tag.slice(DOCS_PREFIX.length) : void 0;
54619
+ }
54098
54620
  function isValidName2(name) {
54099
54621
  return NAME_RE2.test(name);
54100
54622
  }
@@ -55026,7 +55548,7 @@ function discoverDocs(ctx, opts = {}) {
55026
55548
  }
55027
55549
  }
55028
55550
  const byName = /* @__PURE__ */ new Map();
55029
- const add = (c) => {
55551
+ const add2 = (c) => {
55030
55552
  const cur = byName.get(c.name);
55031
55553
  if (cur) {
55032
55554
  if (!cur.usedBy.includes(c.usedBy)) cur.usedBy.push(c.usedBy);
@@ -55052,7 +55574,7 @@ function discoverDocs(ctx, opts = {}) {
55052
55574
  const entry = catalogLookup(s.kind, s.key, catalog);
55053
55575
  const evidence = `${s.kind}:${s.key}`;
55054
55576
  if (entry) {
55055
- add({
55577
+ add2({
55056
55578
  name: entry.id,
55057
55579
  provider: `${s.kind}:${s.key}`,
55058
55580
  title: entry.title,
@@ -55069,7 +55591,7 @@ function discoverDocs(ctx, opts = {}) {
55069
55591
  continue;
55070
55592
  }
55071
55593
  if (s.kind === "npm" || s.kind === "pypi" || s.kind === "go") {
55072
- add({
55594
+ add2({
55073
55595
  name: slugify3(s.key.replace(/^github\.com\//, "")),
55074
55596
  provider: `${s.kind}:${s.key}`,
55075
55597
  title: s.key,
@@ -55079,7 +55601,7 @@ function discoverDocs(ctx, opts = {}) {
55079
55601
  evidence
55080
55602
  });
55081
55603
  } else if (s.kind === "image" && !s.key.includes(".") && !s.key.includes("/")) {
55082
- add({
55604
+ add2({
55083
55605
  name: slugify3(s.key.split(":")[0]),
55084
55606
  provider: `image:${s.key.split(":")[0]}`,
55085
55607
  title: s.key.split(":")[0],
@@ -55094,7 +55616,7 @@ function discoverDocs(ctx, opts = {}) {
55094
55616
  if (info.vars.length < 2) continue;
55095
55617
  const provider = `env:${token}`;
55096
55618
  for (const p of info.projects)
55097
- add({
55619
+ add2({
55098
55620
  name: slugify3(token),
55099
55621
  provider,
55100
55622
  title: token,
@@ -55113,7 +55635,7 @@ function discoverDocs(ctx, opts = {}) {
55113
55635
  if (!h.calls) continue;
55114
55636
  const evidence = `api:${host} (${h.samples.slice(0, 2).join(", ")})${specUrls.length ? ` \xB7 spec ${specUrls[0]}` : ""}`;
55115
55637
  if (entry) {
55116
- add({
55638
+ add2({
55117
55639
  name: entry.id,
55118
55640
  provider: `api:${host}`,
55119
55641
  title: entry.title,
@@ -55129,7 +55651,7 @@ function discoverDocs(ctx, opts = {}) {
55129
55651
  for (const u of specUrls) if (!c.urls.includes(u)) c.urls.push(u);
55130
55652
  } else {
55131
55653
  const domain2 = rootDomain(host);
55132
- add({
55654
+ add2({
55133
55655
  name: slugify3(domain2.replace(/\.[a-z.]+$/, "")),
55134
55656
  provider: `api:${host}`,
55135
55657
  title: `${domain2} API`,
@@ -55148,7 +55670,7 @@ function discoverDocs(ctx, opts = {}) {
55148
55670
  for (const l of list2) {
55149
55671
  const evidence = `link:${host} (${l.samples.slice(0, 2).join(", ")})`;
55150
55672
  if (entry) {
55151
- add({
55673
+ add2({
55152
55674
  name: entry.id,
55153
55675
  provider: `link:${host}`,
55154
55676
  title: entry.title,
@@ -55172,7 +55694,7 @@ function discoverDocs(ctx, opts = {}) {
55172
55694
  if (!existing.usedBy.includes(l.project || ".")) existing.usedBy.push(l.project || ".");
55173
55695
  continue;
55174
55696
  }
55175
- add({
55697
+ add2({
55176
55698
  name,
55177
55699
  provider: `link:${host}`,
55178
55700
  title: `${host} (documentation linked from code)`,
@@ -56546,7 +57068,7 @@ function removeDocSource(ctx, db, name) {
56546
57068
  const removed = removeSource(ctx, name);
56547
57069
  const documents = db.deleteDocumentsBySource(docsTag(name));
56548
57070
  clearSyncState(db, name);
56549
- if (removed) syncDocsGraphQuietly(ctx, db);
57071
+ if (removed || documents) syncDocsGraphQuietly(ctx, db);
56550
57072
  return { removed, documents };
56551
57073
  }
56552
57074
  function formatAdd(o) {
@@ -56617,6 +57139,26 @@ function ago(ts) {
56617
57139
  if (s < 172800) return `${Math.round(s / 3600)}h ago`;
56618
57140
  return `${Math.round(s / 86400)}d ago`;
56619
57141
  }
57142
+ function orphanedDocSources(db, registered, stats = db.documentSourceStats(DOCS_PREFIX)) {
57143
+ const known = new Set(registered);
57144
+ const out = [];
57145
+ for (const s of stats) {
57146
+ const name = nameFromTag(s.source);
57147
+ if (name === void 0 || known.has(name)) continue;
57148
+ out.push({ name, documents: s.documents, chunks: s.chunks, latest: s.latest });
57149
+ }
57150
+ return out;
57151
+ }
57152
+ function formatOrphans(orphans2) {
57153
+ const names = orphans2.map((o) => o.name);
57154
+ return [
57155
+ `# Orphaned in the index (${orphans2.length}; pages without a manifest entry)`,
57156
+ ...orphans2.map(
57157
+ (o) => `- ${o.name} \xB7 ${o.documents} doc(s) \xB7 ${o.chunks} chunks \xB7 indexed ${ago(o.latest)}`
57158
+ ),
57159
+ `em_docs {action:"remove", names:${JSON.stringify(names)}} drops them; {action:"add", pick:"recommended"} or {action:"add", name, url|path} re-registers.`
57160
+ ];
57161
+ }
56620
57162
  function formatDiscovery(d, max = 40) {
56621
57163
  const lines = [];
56622
57164
  lines.push(
@@ -56675,8 +57217,8 @@ function formatDiscovery(d, max = 40) {
56675
57217
  for (const p of list2.slice(0, max)) {
56676
57218
  const used = p.usedBy.length > 1 ? ` \xB7 used by ${p.usedBy.length} projects` : p.usedBy[0] && p.usedBy[0] !== "." ? ` \xB7 ${p.usedBy[0]}` : "";
56677
57219
  const where = p.urls[0] ? ` \xB7 ${p.urls[0]}` : "";
56678
- const ev = conf === "probe" || conf === "unknown" ? ` \xB7 ${p.evidence[0]}` : "";
56679
- lines.push(`- ${p.name} \u2190 ${p.provider}${used}${where}${ev}${reg(p.registered)}`);
57220
+ const ev2 = conf === "probe" || conf === "unknown" ? ` \xB7 ${p.evidence[0]}` : "";
57221
+ lines.push(`- ${p.name} \u2190 ${p.provider}${used}${where}${ev2}${reg(p.registered)}`);
56680
57222
  }
56681
57223
  if (list2.length > max) lines.push(` \u2026 ${list2.length - max} more`);
56682
57224
  }
@@ -56764,19 +57306,30 @@ var emDocs = defineTool({
56764
57306
  case "remove": {
56765
57307
  const names = [...args.name ? [args.name] : [], ...args.names ?? []];
56766
57308
  if (!names.length) return errorResult("remove needs `name` or `names`");
57309
+ let unknown2 = 0;
56767
57310
  const lines = names.map((n) => {
56768
57311
  const r = removeDocSource(ctx, db, n);
56769
- return r.removed ? `removed ${n} (${r.documents} document(s) dropped)` : `${n}: not registered${r.documents ? ` (${r.documents} orphan document(s) dropped)` : ""}`;
57312
+ if (r.removed) return `removed ${n} (${r.documents} document(s) dropped)`;
57313
+ if (r.documents) return `removed ${n} (orphaned; ${r.documents} document(s) dropped)`;
57314
+ unknown2++;
57315
+ return `${n}: not registered and not in the index; em_docs {action:"list"} shows both`;
56770
57316
  });
56771
- return done(lines.join("\n"));
57317
+ const out = lines.join("\n");
57318
+ return unknown2 === names.length ? errorResult(out) : done(out);
56772
57319
  }
56773
57320
  case "list": {
56774
57321
  const m = readManifest(ctx);
56775
- if (!m.sources.length)
56776
- return done(
56777
- `no documentation sources registered (${manifestPath(ctx)}). em_docs {action:"discover"} finds local docs and the libraries, services and APIs this project uses.`
56778
- );
56779
- const stats = new Map(db.documentSourceStats(DOCS_PREFIX).map((s) => [s.source, s]));
57322
+ const sourceStats = db.documentSourceStats(DOCS_PREFIX);
57323
+ const orphans2 = orphanedDocSources(
57324
+ db,
57325
+ m.sources.map((s) => s.name),
57326
+ sourceStats
57327
+ );
57328
+ if (!m.sources.length) {
57329
+ const none = `no documentation sources registered (${manifestPath(ctx)}). em_docs {action:"discover"} finds local docs and the libraries, services and APIs this project uses.`;
57330
+ return done(orphans2.length ? [none, "", ...formatOrphans(orphans2)].join("\n") : none);
57331
+ }
57332
+ const stats = new Map(sourceStats.map((s) => [s.source, s]));
56780
57333
  const catalog = mergedCatalog(ctx.config.docs.catalog);
56781
57334
  const categorised = m.sources.map((s) => ({
56782
57335
  s,
@@ -56807,9 +57360,10 @@ var emDocs = defineTool({
56807
57360
  `- ${s.name} [${s.kind}]${s.provider ? ` \u2190 ${s.provider}` : ""}${s.tags?.length ? ` (${s.tags.join(", ")})` : ""} \xB7 ${st?.documents ?? 0} doc(s) \xB7 ${st?.chunks ?? 0} chunks \xB7 ${state} \xB7 ${where}`
56808
57361
  );
56809
57362
  }
57363
+ if (orphans2.length) lines.push("", ...formatOrphans(orphans2));
56810
57364
  lines.push(
56811
57365
  "",
56812
- `${m.sources.length} source(s); em_docs {action:"lookup", query} searches them all, {action:"sync"} refreshes.`
57366
+ `${m.sources.length} source(s)${orphans2.length ? ` + ${orphans2.length} orphaned` : ""}; em_docs {action:"lookup", query} searches them all, {action:"sync"} refreshes.`
56813
57367
  );
56814
57368
  return done(lines.join("\n"));
56815
57369
  }
@@ -56855,10 +57409,18 @@ ${r.urls.map((u) => ` ${u}`).join("\n")}
56855
57409
  raw += r.hits.reduce((n, h) => n + h.chunk.body.length, 0);
56856
57410
  responses.push(r);
56857
57411
  }
56858
- const out = formatResults(
56859
- responses,
56860
- args.max_tokens ?? ctx.config.search.defaultMaxTokens
57412
+ let out = formatResults(responses, args.max_tokens ?? ctx.config.search.defaultMaxTokens);
57413
+ const hitSources = new Set(
57414
+ responses.flatMap((r) => r.hits.map((h) => h.chunk.document.source))
56861
57415
  );
57416
+ const orphaned = orphanedDocSources(
57417
+ db,
57418
+ m.sources.map((s) => s.name)
57419
+ ).filter((o) => hitSources.has(docsTag(o.name)));
57420
+ if (orphaned.length)
57421
+ out += `
57422
+
57423
+ note: ${orphaned.map((o) => o.name).join(", ")} ${orphaned.length === 1 ? "is" : "are"} not in the manifest (orphaned pages); em_docs {action:"list"} explains`;
56862
57424
  recordStat(ctx, "em_docs", {
56863
57425
  rawChars: raw,
56864
57426
  returnedChars: out.length,
@@ -57089,7 +57651,7 @@ var LANG_ENUM = external_exports.string().describe(
57089
57651
  );
57090
57652
  var emExecute = defineTool({
57091
57653
  name: "em_execute",
57092
- description: "Run code in a sandbox inside the project root and return only stdout. Output over the index threshold (5 KB) is indexed; pass `intent` to get back just the matching sections plus a vocabulary of searchable terms. Use this instead of Bash for anything that prints a lot: git log/diff, test runs, dependency trees, logs, API calls. Shell snippets honour Claude Code's Bash deny rules.",
57654
+ description: "Run code in a sandbox inside the project root and return only stdout. Output over the index threshold (5 KB) is indexed; pass `intent` to get back just the matching sections plus a vocabulary of searchable terms. Use this instead of Bash for anything that prints a lot: git log/diff, test runs, dependency trees, logs, API calls. Shell snippets honour Claude Code's Bash deny rules. javascript and typescript run as ES modules; `import` and `require` both work.",
57093
57655
  schema: {
57094
57656
  language: LANG_ENUM,
57095
57657
  code: external_exports.string().min(1).describe("Program or shell script to run"),
@@ -57171,7 +57733,7 @@ function refuse(check3, target) {
57171
57733
  // src/tools/em_execute_file.ts
57172
57734
  var emExecuteFile = defineTool({
57173
57735
  name: "em_execute_file",
57174
- description: "Process a file in the sandbox without reading it into context. Your code receives the absolute path as $EM_FILE (shell) / process.env.EM_FILE (node) / os.environ['EM_FILE'] (python); print only what you need. The file must be inside the project root unless a Read(<path>) allow rule opts it in; a matching Read deny or ask rule refuses the path, inside the root or out. Omit `code` to get a quick summary (size, lines, head).",
57736
+ description: "Process a file in the sandbox without reading it into context. Your code receives the absolute path as $EM_FILE (shell) / process.env.EM_FILE (node) / os.environ['EM_FILE'] (python); print only what you need. The file must be inside the project root unless a Read(<path>) allow rule opts it in; a matching Read deny or ask rule refuses the path, inside the root or out. Omit `code` to get a quick summary (size, lines, head). javascript and typescript run as ES modules; `import` and `require` both work.",
57175
57737
  schema: {
57176
57738
  path: external_exports.string().describe("File to process, relative to the project root or absolute"),
57177
57739
  language: LANG_ENUM.optional(),
@@ -58147,7 +58709,7 @@ var emIndex = defineTool({
58147
58709
  });
58148
58710
  kickEmbedding(ctx, db);
58149
58711
  return text(
58150
- `${r.created ? "indexed" : "unchanged"} "${args.title}" as doc ${r.documentId}: ${r.chunks} chunks, ${r.chars} chars (${r.kind})`
58712
+ `${r.created ? "indexed" : "unchanged"} "${args.title}" as doc ${r.documentId}: ${r.chunks} chunks, ${r.chars} chars (${r.kind})${r.fallback ? " (no sections found; indexed as one chunk)" : ""}`
58151
58713
  );
58152
58714
  }
58153
58715
  if (!args.path) return errorResult("provide `path` or `content`");
@@ -58157,7 +58719,7 @@ var emIndex = defineTool({
58157
58719
  recursive: args.recursive ?? true
58158
58720
  });
58159
58721
  const lines = [
58160
- `${args.path}: ${s.files} file(s), ${s.indexed} indexed, ${s.unchanged} unchanged, ${s.chunks} chunks`
58722
+ `${args.path}: ${s.files} file(s), ${s.indexed} indexed, ${s.unchanged} unchanged, ${s.chunks} chunks${s.fallbacks ? ` (${s.fallbacks} with no sections found; indexed as one chunk)` : ""}`
58161
58723
  ];
58162
58724
  for (const sk of s.skipped.slice(0, 10)) lines.push(` skipped ${sk.path}: ${sk.reason}`);
58163
58725
  if (s.skipped.length > 10) lines.push(` \u2026 ${s.skipped.length - 10} more skipped`);
@@ -58341,9 +58903,9 @@ ${body}`);
58341
58903
  init_esm_shims();
58342
58904
  var emPurge = defineTool({
58343
58905
  name: "em_purge",
58344
- description: "Permanently delete eminiar data for this project. scope: index (documents/chunks/vectors/caches), sessions (events, snapshots, stats), graph, memories (the database mirror only; the markdown files under .eminiar/memories stay and are re-imported on the next memory call \u2014 delete files with em_memory), or all. Requires confirm: true.",
58906
+ description: "Permanently delete eminiar data for this project. scope: index (documents/chunks/vectors/caches), sessions (events, snapshots, stats), graph, memories (the database mirror only; the markdown files under .eminiar/memories stay and are re-imported on the next memory call \u2014 delete files with em_memory), usage (API calls, tool uses, context events, transcript offsets, statusline snapshots), or all. Requires confirm: true.",
58345
58907
  schema: {
58346
- scope: external_exports.enum(["index", "sessions", "graph", "memories", "all"]),
58908
+ scope: external_exports.enum(["index", "sessions", "graph", "memories", "usage", "all"]),
58347
58909
  confirm: external_exports.boolean().describe("Must be true; this cannot be undone")
58348
58910
  },
58349
58911
  handler: async (args, ctx) => {
@@ -58533,7 +59095,10 @@ var emSearch = defineTool({
58533
59095
  }
58534
59096
  }
58535
59097
  const out = formatResults(responses, args.max_tokens ?? ctx.config.search.defaultMaxTokens);
58536
- const note = mode !== "lexical" && mode !== "regex" && !vectorSearch && ctx.config.embeddings.enabled ? "\n\n(embeddings: model still loading \u2014 lexical results only this time)" : "";
59098
+ let note = mode !== "lexical" && mode !== "regex" && !vectorSearch && ctx.config.embeddings.enabled ? "\n\n(embeddings: model still loading \u2014 lexical results only this time)" : "";
59099
+ const sources = Array.isArray(args.source) ? args.source : args.source ? [args.source] : [];
59100
+ if (mode === "semantic" && !ctx.config.docs.embed && sources.some((s) => s.startsWith("docs:")) && responses.every((r) => r.hits.length === 0))
59101
+ note += '\n\n(docs:* pages are indexed without vectors because docs.embed is off; use mode "hybrid" or "lexical", or set docs.embed: true in eminiar.config.json and em_docs {action:"sync", force:true})';
58537
59102
  recordStat(ctx, "em_search", {
58538
59103
  rawChars: responses.reduce(
58539
59104
  (a, r) => a + r.hits.reduce((b, h) => b + h.chunk.body.length, 0),
@@ -58552,6 +59117,7 @@ init_esm_shims();
58552
59117
  // src/session/recall.ts
58553
59118
  init_esm_shims();
58554
59119
  init_ids();
59120
+ init_metrics();
58555
59121
  init_model();
58556
59122
  var RECALL_CATEGORY = "recall";
58557
59123
  function recallStats(events) {
@@ -58632,8 +59198,8 @@ var top = (m, n = 4) => Object.entries(m).sort((a, b) => b[1] - a[1]).slice(0, n
58632
59198
  function formatRoutingStats(title, s) {
58633
59199
  if (!s.decisions) return `${title}: no routing decisions recorded yet`;
58634
59200
  const tools2 = Object.entries(s.byTool).sort((a, b) => b[1].calls - a[1].calls).map(([tool, r]) => {
58635
- const pct = Math.round(100 * r.denied / r.calls);
58636
- const parts = [`${tool} ${r.denied}/${r.calls} denied (${pct}%)`];
59201
+ const pct3 = Math.round(100 * r.denied / r.calls);
59202
+ const parts = [`${tool} ${r.denied}/${r.calls} denied (${pct3}%)`];
58637
59203
  if (r.annotated) parts.push(`${r.annotated} annotated`);
58638
59204
  return parts.join(", ");
58639
59205
  });
@@ -58649,6 +59215,1787 @@ function formatRoutingStats(title, s) {
58649
59215
  return lines.join("\n");
58650
59216
  }
58651
59217
 
59218
+ // src/usage/brief.ts
59219
+ init_esm_shims();
59220
+
59221
+ // src/usage/aggregate.ts
59222
+ init_esm_shims();
59223
+
59224
+ // src/usage/blocks.ts
59225
+ init_esm_shims();
59226
+ var HOUR = 36e5;
59227
+ function floorToHour(ts) {
59228
+ return Math.floor(ts / HOUR) * HOUR;
59229
+ }
59230
+ function newBlock(call, hours) {
59231
+ const start = floorToHour(call.ts);
59232
+ return {
59233
+ id: new Date(start).toISOString(),
59234
+ start,
59235
+ end: start + hours * HOUR,
59236
+ firstCall: call.ts,
59237
+ lastCall: call.ts,
59238
+ calls: 0,
59239
+ tokens: { input: 0, output: 0, cacheCreate: 0, cacheRead: 0 },
59240
+ costUsd: 0,
59241
+ pricedCalls: 0,
59242
+ unpricedCalls: 0,
59243
+ models: [],
59244
+ active: false
59245
+ };
59246
+ }
59247
+ function addCall(block, call) {
59248
+ block.calls += 1;
59249
+ block.lastCall = Math.max(block.lastCall, call.ts);
59250
+ block.tokens.input += call.input_tokens || 0;
59251
+ block.tokens.output += call.output_tokens || 0;
59252
+ block.tokens.cacheCreate += (call.cache_create_5m || 0) + (call.cache_create_1h || 0);
59253
+ block.tokens.cacheRead += call.cache_read || 0;
59254
+ if (call.priced && call.cost_usd != null && Number.isFinite(call.cost_usd)) {
59255
+ block.costUsd += call.cost_usd;
59256
+ block.pricedCalls += 1;
59257
+ } else {
59258
+ block.unpricedCalls += 1;
59259
+ }
59260
+ if (call.model && !block.models.includes(call.model)) block.models.push(call.model);
59261
+ }
59262
+ function billingBlocks(calls, now = Date.now(), hours = 5) {
59263
+ const sorted = calls.filter((c) => Number.isFinite(c.ts) && c.model !== "<synthetic>").slice().sort((a, b) => a.ts - b.ts);
59264
+ const blocks = [];
59265
+ let current;
59266
+ for (const call of sorted) {
59267
+ if (!current || call.ts >= current.end) {
59268
+ const next = newBlock(call, hours);
59269
+ if (current) next.gapBefore = Math.max(0, call.ts - current.lastCall);
59270
+ blocks.push(next);
59271
+ current = next;
59272
+ }
59273
+ addCall(current, call);
59274
+ }
59275
+ for (const b of blocks) {
59276
+ b.models.sort();
59277
+ b.active = now >= b.start && now < b.end;
59278
+ }
59279
+ return blocks;
59280
+ }
59281
+ function activeBlock(blocks, now = Date.now()) {
59282
+ return blocks.find((b) => now >= b.start && now < b.end);
59283
+ }
59284
+
59285
+ // src/usage/pricing.ts
59286
+ init_esm_shims();
59287
+ var WEB_SEARCH_PER_1K = 10;
59288
+ function price(input2, output2, cacheRead, extra = {}) {
59289
+ return {
59290
+ input: input2,
59291
+ output: output2,
59292
+ cacheWrite5m: input2 * 1.25,
59293
+ cacheWrite1h: input2 * 2,
59294
+ cacheRead,
59295
+ webSearch: WEB_SEARCH_PER_1K,
59296
+ ...extra
59297
+ };
59298
+ }
59299
+ var FABLE_5_1 = price(10, 50, 0.25);
59300
+ var FABLE_5 = price(10, 50, 1);
59301
+ var OPUS_5_5 = price(4, 20, 0.2);
59302
+ var OPUS_5 = price(5, 25, 0.5);
59303
+ var OPUS_4_LATE = price(5, 25, 0.5);
59304
+ var OPUS_4_EARLY = price(15, 75, 1.5);
59305
+ var SONNET_5_5 = price(2, 10, 0.1);
59306
+ var SONNET_5 = price(2, 10, 0.2);
59307
+ var SONNET_4 = price(3, 15, 0.3);
59308
+ var HAIKU_5_5 = price(0.1, 0.5, 0.01, {
59309
+ longContext: { threshold: 1e5, input: 0.5, output: 2.5 }
59310
+ });
59311
+ var HAIKU_4_5 = price(1, 5, 0.1);
59312
+ var HAIKU_3_5 = price(0.8, 4, 0.08);
59313
+ var FAMILIES = [
59314
+ ["fable-5-1", FABLE_5_1],
59315
+ ["mythos-5-1", FABLE_5_1],
59316
+ ["fable-5", FABLE_5],
59317
+ ["mythos-5", FABLE_5],
59318
+ ["opus-5-5", OPUS_5_5],
59319
+ ["opus-5", OPUS_5],
59320
+ ["opus-4-8", OPUS_4_LATE],
59321
+ ["opus-4-7", OPUS_4_LATE],
59322
+ ["opus-4-6", OPUS_4_LATE],
59323
+ ["opus-4-5", OPUS_4_LATE],
59324
+ ["opus-4-1", OPUS_4_EARLY],
59325
+ ["opus-4", OPUS_4_EARLY],
59326
+ ["sonnet-5-5", SONNET_5_5],
59327
+ ["sonnet-5", SONNET_5],
59328
+ ["sonnet-4-6", SONNET_4],
59329
+ ["sonnet-4-5", SONNET_4],
59330
+ ["sonnet-4", SONNET_4],
59331
+ ["haiku-5-5", HAIKU_5_5],
59332
+ ["haiku-4-5", HAIKU_4_5],
59333
+ ["3-5-haiku", HAIKU_3_5]
59334
+ ];
59335
+ var ALIASES = [
59336
+ ["fable", FABLE_5_1],
59337
+ ["mythos", FABLE_5_1],
59338
+ ["opus", OPUS_5_5],
59339
+ ["sonnet", SONNET_5_5],
59340
+ ["haiku", HAIKU_4_5]
59341
+ ];
59342
+ function bundledPrice(id) {
59343
+ for (const [pattern2, p] of FAMILIES) if (id.includes(pattern2)) return p;
59344
+ if (/\d/.test(id)) return void 0;
59345
+ for (const [pattern2, p] of ALIASES) if (id.includes(pattern2)) return p;
59346
+ return void 0;
59347
+ }
59348
+ function finite(n) {
59349
+ return typeof n === "number" && Number.isFinite(n) && n >= 0;
59350
+ }
59351
+ function resolvePrice(model, overrides) {
59352
+ const id = String(model ?? "").toLowerCase();
59353
+ if (!id) return void 0;
59354
+ const base = bundledPrice(id);
59355
+ if (!overrides) return base;
59356
+ for (const [pattern2, o] of Object.entries(overrides)) {
59357
+ if (!pattern2 || !id.includes(pattern2.toLowerCase())) continue;
59358
+ const input2 = finite(o.input) ? o.input : base?.input;
59359
+ const output2 = finite(o.output) ? o.output : base?.output;
59360
+ if (input2 === void 0 || output2 === void 0) return base;
59361
+ const merged = {
59362
+ input: input2,
59363
+ output: output2,
59364
+ cacheWrite5m: finite(o.cacheWrite5m) ? o.cacheWrite5m : base && !finite(o.input) ? base.cacheWrite5m : input2 * 1.25,
59365
+ cacheWrite1h: finite(o.cacheWrite1h) ? o.cacheWrite1h : base && !finite(o.input) ? base.cacheWrite1h : input2 * 2,
59366
+ cacheRead: finite(o.cacheRead) ? o.cacheRead : base && !finite(o.input) ? base.cacheRead : input2 * 0.1,
59367
+ webSearch: finite(o.webSearch) ? o.webSearch : base?.webSearch ?? WEB_SEARCH_PER_1K
59368
+ };
59369
+ if (o.longContext) merged.longContext = o.longContext;
59370
+ else if (base?.longContext) merged.longContext = base.longContext;
59371
+ return merged;
59372
+ }
59373
+ return base;
59374
+ }
59375
+ var tok = (n) => finite(n) ? n : 0;
59376
+ function costOfCall(call, price2) {
59377
+ const input2 = tok(call.input_tokens);
59378
+ const output2 = tok(call.output_tokens);
59379
+ const c5m = tok(call.cache_create_5m);
59380
+ const c1h = tok(call.cache_create_1h);
59381
+ const read2 = tok(call.cache_read);
59382
+ const searches = tok(call.web_searches);
59383
+ let p = price2;
59384
+ const lc = price2.longContext;
59385
+ if (lc && input2 + c5m + c1h + read2 > lc.threshold) {
59386
+ const ratio = price2.input > 0 ? lc.input / price2.input : 1;
59387
+ p = {
59388
+ ...price2,
59389
+ input: lc.input,
59390
+ output: lc.output,
59391
+ cacheWrite5m: price2.cacheWrite5m * ratio,
59392
+ cacheWrite1h: price2.cacheWrite1h * ratio,
59393
+ cacheRead: price2.cacheRead * ratio
59394
+ };
59395
+ }
59396
+ const M = 1e6;
59397
+ const breakdown = {
59398
+ input: input2 * p.input / M,
59399
+ output: output2 * p.output / M,
59400
+ cacheWrite: (c5m * p.cacheWrite5m + c1h * p.cacheWrite1h) / M,
59401
+ cacheRead: read2 * p.cacheRead / M,
59402
+ tools: searches / 1e3 * (p.webSearch ?? 0)
59403
+ };
59404
+ return {
59405
+ ...breakdown,
59406
+ total: breakdown.input + breakdown.output + breakdown.cacheWrite + breakdown.cacheRead + breakdown.tools
59407
+ };
59408
+ }
59409
+ function formatUsd(n) {
59410
+ if (!Number.isFinite(n) || n === 0) return "$0.00";
59411
+ const sign = n < 0 ? "-" : "";
59412
+ const v = Math.abs(n);
59413
+ if (v < 1e-4) return sign ? "-$0.0001" : "<$0.0001";
59414
+ if (v < 1) {
59415
+ const fixed = v.toFixed(4);
59416
+ if (fixed.startsWith("1")) return `${sign}$1.00`;
59417
+ return `${sign}$${fixed.replace(/0+$/, "").replace(/\.(\d)$/, ".$10")}`;
59418
+ }
59419
+ const two = v.toFixed(2);
59420
+ if (Number(two) >= 1e3) return `${sign}$${Math.round(v).toLocaleString("en-US")}`;
59421
+ return `${sign}$${two}`;
59422
+ }
59423
+ function estTokensFromChars(chars) {
59424
+ return finite(chars) ? Math.ceil(chars / 4) : 0;
59425
+ }
59426
+
59427
+ // src/usage/aggregate.ts
59428
+ var num2 = (n) => typeof n === "number" && Number.isFinite(n) ? n : 0;
59429
+ var isRealCall = (c) => c.model !== "<synthetic>";
59430
+ function totals(calls) {
59431
+ const t = {
59432
+ calls: 0,
59433
+ input: 0,
59434
+ output: 0,
59435
+ cacheCreate5m: 0,
59436
+ cacheCreate1h: 0,
59437
+ cacheRead: 0,
59438
+ thinking: 0,
59439
+ costUsd: 0,
59440
+ unpricedCalls: 0,
59441
+ cacheHitRate: 0,
59442
+ models: {}
59443
+ };
59444
+ for (const c of calls) {
59445
+ if (!isRealCall(c)) continue;
59446
+ t.calls += 1;
59447
+ t.input += num2(c.input_tokens);
59448
+ t.output += num2(c.output_tokens);
59449
+ t.cacheCreate5m += num2(c.cache_create_5m);
59450
+ t.cacheCreate1h += num2(c.cache_create_1h);
59451
+ t.cacheRead += num2(c.cache_read);
59452
+ t.thinking += num2(c.thinking_tokens);
59453
+ if (c.priced && c.cost_usd != null && Number.isFinite(c.cost_usd)) t.costUsd += c.cost_usd;
59454
+ else t.unpricedCalls += 1;
59455
+ const m = c.model || "unknown";
59456
+ t.models[m] = (t.models[m] ?? 0) + 1;
59457
+ }
59458
+ const denom = t.input + t.cacheCreate5m + t.cacheCreate1h + t.cacheRead;
59459
+ t.cacheHitRate = denom > 0 ? t.cacheRead / denom : 0;
59460
+ return t;
59461
+ }
59462
+ function groupBy(calls, keyFn) {
59463
+ const buckets = /* @__PURE__ */ new Map();
59464
+ for (const c of calls) {
59465
+ if (!isRealCall(c)) continue;
59466
+ const k = keyFn(c);
59467
+ if (k == null) continue;
59468
+ const list2 = buckets.get(k);
59469
+ if (list2) list2.push(c);
59470
+ else buckets.set(k, [c]);
59471
+ }
59472
+ return [...buckets.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, rows]) => ({ key, totals: totals(rows) }));
59473
+ }
59474
+ var fmtCache = /* @__PURE__ */ new Map();
59475
+ function formatter(tz) {
59476
+ const key = tz ?? "";
59477
+ let f = fmtCache.get(key);
59478
+ if (!f) {
59479
+ f = new Intl.DateTimeFormat("en-CA", {
59480
+ year: "numeric",
59481
+ month: "2-digit",
59482
+ day: "2-digit",
59483
+ ...tz ? { timeZone: tz } : {}
59484
+ });
59485
+ fmtCache.set(key, f);
59486
+ }
59487
+ return f;
59488
+ }
59489
+ function localYmd(ts, tz) {
59490
+ const parts = formatter(tz).formatToParts(new Date(ts));
59491
+ const get = (type) => Number(parts.find((p) => p.type === type)?.value ?? 0);
59492
+ return { y: get("year"), m: get("month"), d: get("day") };
59493
+ }
59494
+ var pad2 = (n) => String(n).padStart(2, "0");
59495
+ var ymdKey = ({ y, m, d }) => `${y}-${pad2(m)}-${pad2(d)}`;
59496
+ function dayKey(ts, tz) {
59497
+ return ymdKey(localYmd(ts, tz));
59498
+ }
59499
+ function weekKey(ts, tz) {
59500
+ const { y, m, d } = localYmd(ts, tz);
59501
+ const date5 = new Date(Date.UTC(y, m - 1, d));
59502
+ const back = (date5.getUTCDay() + 6) % 7;
59503
+ date5.setUTCDate(date5.getUTCDate() - back);
59504
+ return ymdKey({ y: date5.getUTCFullYear(), m: date5.getUTCMonth() + 1, d: date5.getUTCDate() });
59505
+ }
59506
+ function monthKey(ts, tz) {
59507
+ const { y, m } = localYmd(ts, tz);
59508
+ return `${y}-${pad2(m)}`;
59509
+ }
59510
+ var byModel = (calls) => groupBy(calls, (c) => c.model || "unknown");
59511
+ var bySession = (calls) => groupBy(calls, (c) => c.session_id);
59512
+ var byEffort = (calls) => groupBy(calls, (c) => c.effort || "default");
59513
+ var bySpeed = (calls) => groupBy(calls, (c) => c.speed === "fast" ? "fast" : "standard");
59514
+ function byAgent(calls) {
59515
+ const mainRows = calls.filter((c) => !c.sidechain);
59516
+ const side = calls.filter((c) => !!c.sidechain);
59517
+ const main = totals(mainRows);
59518
+ const subagents = groupBy(side, (c) => c.agent_id || "unknown").map((g) => ({ agentId: g.key, totals: g.totals })).sort((a, b) => b.totals.costUsd - a.totals.costUsd || b.totals.calls - a.totals.calls);
59519
+ const subCost = subagents.reduce((s, a) => s + a.totals.costUsd, 0);
59520
+ const total = main.costUsd + subCost;
59521
+ return { main, subagents, delegationShare: total > 0 ? subCost / total : 0 };
59522
+ }
59523
+ function cacheStats(calls) {
59524
+ const t = totals(calls);
59525
+ const writes = t.cacheCreate5m + t.cacheCreate1h;
59526
+ const first = /* @__PURE__ */ new Map();
59527
+ for (const c of calls) {
59528
+ if (!isRealCall(c)) continue;
59529
+ const prev = first.get(c.session_id);
59530
+ if (!prev || c.ts < prev.ts || c.ts === prev.ts && c.request_id < prev.request_id)
59531
+ first.set(c.session_id, c);
59532
+ }
59533
+ let coldCalls = 0;
59534
+ for (const c of calls) {
59535
+ if (!isRealCall(c) || first.get(c.session_id) === c) continue;
59536
+ if (num2(c.cache_read) === 0) coldCalls += 1;
59537
+ }
59538
+ return {
59539
+ hitRate: t.cacheHitRate,
59540
+ cacheWrites: writes,
59541
+ cacheReads: t.cacheRead,
59542
+ writeReadRatio: t.cacheRead > 0 ? writes / t.cacheRead : 0,
59543
+ share1h: writes > 0 ? t.cacheCreate1h / writes : 0,
59544
+ coldCalls,
59545
+ calls: t.calls
59546
+ };
59547
+ }
59548
+ function contextTimeline(sessionCalls, contextEvents = []) {
59549
+ const points = sessionCalls.filter(isRealCall).sort((a, b) => a.ts - b.ts).map((c) => ({
59550
+ ts: c.ts,
59551
+ requestId: c.request_id,
59552
+ model: c.model,
59553
+ contextTokens: num2(c.input_tokens) + num2(c.cache_create_5m) + num2(c.cache_create_1h) + num2(c.cache_read),
59554
+ output: num2(c.output_tokens),
59555
+ costUsd: c.cost_usd != null && Number.isFinite(c.cost_usd) ? c.cost_usd : 0
59556
+ }));
59557
+ const events = contextEvents.slice().sort((a, b) => a.ts - b.ts);
59558
+ let i = 0;
59559
+ for (const ev2 of events) {
59560
+ while (i < points.length && points[i].ts < ev2.ts) i += 1;
59561
+ const p = points[i];
59562
+ if (p) p.compactBefore = true;
59563
+ }
59564
+ return points;
59565
+ }
59566
+ var BURN_MIN_MINUTES = 5;
59567
+ var BURN_MIN_CALLS = 2;
59568
+ function burnRate(calls, now = Date.now()) {
59569
+ const none = {
59570
+ tokensPerMin: 0,
59571
+ usdPerHour: 0,
59572
+ projectedBlockUsd: null,
59573
+ windowMinutes: 0,
59574
+ reliable: false
59575
+ };
59576
+ const block = activeBlock(billingBlocks(calls, now), now);
59577
+ if (!block) return none;
59578
+ const windowMinutes = Math.max(0, (Math.min(now, block.end) - block.firstCall) / 6e4);
59579
+ if (windowMinutes < BURN_MIN_MINUTES || block.calls < BURN_MIN_CALLS)
59580
+ return { ...none, windowMinutes };
59581
+ const tokens = block.tokens.input + block.tokens.output + block.tokens.cacheCreate + block.tokens.cacheRead;
59582
+ const usdPerHour = block.costUsd / windowMinutes * 60;
59583
+ const remainingHours = Math.max(0, block.end - now) / 36e5;
59584
+ return {
59585
+ tokensPerMin: tokens / windowMinutes,
59586
+ usdPerHour,
59587
+ projectedBlockUsd: block.costUsd + usdPerHour * remainingHours,
59588
+ windowMinutes,
59589
+ reliable: true
59590
+ };
59591
+ }
59592
+ function topToolConsumers(toolUses, limit = 10) {
59593
+ const byTool = /* @__PURE__ */ new Map();
59594
+ for (const u of toolUses) {
59595
+ const tool = u.tool || "unknown";
59596
+ let row = byTool.get(tool);
59597
+ if (!row) {
59598
+ row = { tool, calls: 0, chars: 0, estTokens: 0, avgChars: 0, errors: 0 };
59599
+ byTool.set(tool, row);
59600
+ }
59601
+ row.calls += 1;
59602
+ row.chars += num2(u.result_chars);
59603
+ if (u.is_error) row.errors += 1;
59604
+ }
59605
+ return [...byTool.values()].map((r) => ({
59606
+ ...r,
59607
+ estTokens: estTokensFromChars(r.chars),
59608
+ avgChars: r.calls > 0 ? Math.round(r.chars / r.calls) : 0
59609
+ })).sort((a, b) => b.chars - a.chars || b.calls - a.calls || a.tool.localeCompare(b.tool)).slice(0, Math.max(0, limit));
59610
+ }
59611
+
59612
+ // src/usage/insights.ts
59613
+ init_esm_shims();
59614
+
59615
+ // src/usage/savings.ts
59616
+ init_esm_shims();
59617
+ var FALLBACK_MODEL = "claude-sonnet-5-5";
59618
+ function dominantModel(calls) {
59619
+ const counts = /* @__PURE__ */ new Map();
59620
+ for (const c of calls) {
59621
+ if (!c.model) continue;
59622
+ counts.set(c.model, (counts.get(c.model) ?? 0) + 1);
59623
+ }
59624
+ let best;
59625
+ let bestN = 0;
59626
+ for (const [m, n] of counts) {
59627
+ if (n > bestN) {
59628
+ best = m;
59629
+ bestN = n;
59630
+ }
59631
+ }
59632
+ return best;
59633
+ }
59634
+ function pricedSavings(stats, calls, overrides, opts = {}) {
59635
+ const assumptions = [
59636
+ "Saved tokens = ceil(raw_chars / 4) - ceil(returned_chars / 4) per stat row, floored at 0 (same estimate as em_stats).",
59637
+ "Direct savings price saved tokens at the 5-minute cache-write rate: a tool result enters context once and is written to cache on that turn.",
59638
+ "Compounding savings price saved tokens at the cache-read rate once per later API call in the same session; stat rows carry no timestamps, so half of the session's calls are taken as the expected number of later calls.",
59639
+ "Sessions with no recorded API calls earn no compounding savings."
59640
+ ];
59641
+ const global2 = dominantModel(calls);
59642
+ let fallbackUsed = false;
59643
+ const priceFor = (model2) => {
59644
+ const m = model2 ?? global2;
59645
+ const p = m ? resolvePrice(m, overrides) : void 0;
59646
+ if (m && p) return { model: m, price: p };
59647
+ fallbackUsed = true;
59648
+ const fb = resolvePrice(FALLBACK_MODEL, overrides) ?? resolvePrice(FALLBACK_MODEL);
59649
+ return { model: FALLBACK_MODEL, price: fb };
59650
+ };
59651
+ const bySession2 = /* @__PURE__ */ new Map();
59652
+ for (const c of calls) {
59653
+ const list2 = bySession2.get(c.session_id);
59654
+ if (list2) list2.push(c);
59655
+ else bySession2.set(c.session_id, [c]);
59656
+ }
59657
+ const sessionModel = /* @__PURE__ */ new Map();
59658
+ for (const [sid, rows] of bySession2) sessionModel.set(sid, dominantModel(rows));
59659
+ const windowed = opts.since !== void 0;
59660
+ assumptions.push(
59661
+ windowed ? `Savings cover sessions active in the window (since ${new Date(opts.since).toISOString()}): stat rows carry no timestamps, so rows of sessions without an API call in the window are left out.` : "All-time: every stat row of the project is counted."
59662
+ );
59663
+ let savedTokens = 0;
59664
+ let directUsd = 0;
59665
+ let compoundingUsd = 0;
59666
+ const laterBySession = /* @__PURE__ */ new Map();
59667
+ for (const row of stats) {
59668
+ if (windowed && !bySession2.has(row.session_id)) continue;
59669
+ const saved = Math.max(
59670
+ 0,
59671
+ estTokensFromChars(row.raw_chars) - estTokensFromChars(row.returned_chars)
59672
+ );
59673
+ if (saved === 0) continue;
59674
+ savedTokens += saved;
59675
+ const { price: price2 } = priceFor(sessionModel.get(row.session_id));
59676
+ directUsd += saved * price2.cacheWrite5m / 1e6;
59677
+ const later = (bySession2.get(row.session_id)?.length ?? 0) / 2;
59678
+ laterBySession.set(row.session_id, later);
59679
+ compoundingUsd += saved * price2.cacheRead * later / 1e6;
59680
+ }
59681
+ const laterValues = [...laterBySession.values()];
59682
+ const laterCallsPerSession = laterValues.length > 0 ? laterValues.reduce((a, b) => a + b, 0) / laterValues.length : 0;
59683
+ const model = global2 ?? FALLBACK_MODEL;
59684
+ if (!global2) assumptions.push(`No API calls recorded; priced at ${FALLBACK_MODEL} list prices.`);
59685
+ else
59686
+ assumptions.push(
59687
+ `Each session is priced at its own dominant model (by calls); ${model} overall.`
59688
+ );
59689
+ if (fallbackUsed && global2)
59690
+ assumptions.push(`Some sessions had no priceable model and were priced at ${FALLBACK_MODEL}.`);
59691
+ return { savedTokens, directUsd, compoundingUsd, laterCallsPerSession, model, assumptions };
59692
+ }
59693
+
59694
+ // src/usage/insights.ts
59695
+ var THRASH_WRITE_SHARE = 0.2;
59696
+ var THRASH_RUN = 3;
59697
+ var TTL_5M_MS = 5 * 6e4;
59698
+ var TTL_1H_MS = 60 * 6e4;
59699
+ var COLD_WRITE_SHARE = 0.5;
59700
+ var BLOAT_TOKENS = 8e3;
59701
+ var BLOAT_TOOLS = /* @__PURE__ */ new Set(["Read", "Bash", "Grep", "WebFetch", "Glob"]);
59702
+ var CHURN_COMPACTIONS = 3;
59703
+ var CHURN_EARLY_SHARE = 0.5;
59704
+ var DEFAULT_WINDOW = 2e5;
59705
+ var TRIVIAL_OUTPUT = 100;
59706
+ var TRIVIAL_SHARE = 0.3;
59707
+ var MIN_PREMIUM_CALLS = 5;
59708
+ var FAST_COST_SHARE = 0.25;
59709
+ var THINKING_SHARE = 0.4;
59710
+ var MIN_OUTPUT_FOR_THINKING = 1e3;
59711
+ var DELEGATION_SHARE = 0.6;
59712
+ var DEFAULT_TURN_USD = 1;
59713
+ var ERROR_RUN = 3;
59714
+ var NEAR_LIMIT_PCT = 85;
59715
+ var NEAR_LIMIT_HIGH = 95;
59716
+ var num3 = (n) => typeof n === "number" && Number.isFinite(n) ? n : 0;
59717
+ var pct = (x) => `${Math.round(x * 100)}%`;
59718
+ var usd = (n) => n >= 1 ? `$${n.toFixed(2)}` : `$${n.toFixed(3)}`;
59719
+ var round4 = (n) => Math.round(n * 1e4) / 1e4;
59720
+ var clamp = (n) => Math.max(0, Math.min(100, Math.round(n)));
59721
+ var fmtK = (n) => n >= 1e6 ? `${(n / 1e6).toFixed(1)}M` : n >= 1e3 ? `${Math.round(n / 1e3)}k` : `${n}`;
59722
+ var contextOf = (c) => num3(c.input_tokens) + num3(c.cache_create_5m) + num3(c.cache_create_1h) + num3(c.cache_read);
59723
+ var writesOf = (c) => num3(c.cache_create_5m) + num3(c.cache_create_1h);
59724
+ var costOf = (c) => c.priced && c.cost_usd != null && Number.isFinite(c.cost_usd) ? c.cost_usd : 0;
59725
+ var isPremium = (model) => /opus|fable|mythos/i.test(model);
59726
+ function threads(calls) {
59727
+ const out = /* @__PURE__ */ new Map();
59728
+ for (const c of calls) {
59729
+ const key = `${c.session_id}\0${c.sidechain ? c.agent_id ?? "sidechain" : ""}`;
59730
+ const list2 = out.get(key);
59731
+ if (list2) list2.push(c);
59732
+ else out.set(key, [c]);
59733
+ }
59734
+ for (const list2 of out.values()) list2.sort((a, b) => a.ts - b.ts);
59735
+ return out;
59736
+ }
59737
+ function groupBySession(rows) {
59738
+ const out = /* @__PURE__ */ new Map();
59739
+ for (const r of rows) {
59740
+ const list2 = out.get(r.session_id);
59741
+ if (list2) list2.push(r);
59742
+ else out.set(r.session_id, [r]);
59743
+ }
59744
+ return out;
59745
+ }
59746
+ function dominantModel2(calls) {
59747
+ const counts = /* @__PURE__ */ new Map();
59748
+ for (const c of calls) counts.set(c.model, (counts.get(c.model) ?? 0) + 1);
59749
+ let best;
59750
+ let n = 0;
59751
+ for (const [m, k] of counts)
59752
+ if (k > n) {
59753
+ best = m;
59754
+ n = k;
59755
+ }
59756
+ return best;
59757
+ }
59758
+ function writePrice(c, p) {
59759
+ return num3(c.cache_create_1h) > num3(c.cache_create_5m) ? p.cacheWrite1h : p.cacheWrite5m;
59760
+ }
59761
+ function scopeFor(sessionIds) {
59762
+ const ids = [...new Set(sessionIds)];
59763
+ return ids.length === 1 ? { scope: "session", sessionId: ids[0] } : { scope: "project" };
59764
+ }
59765
+ function withScope(base, sessionIds) {
59766
+ const s = scopeFor(sessionIds);
59767
+ const out = { ...base, scope: s.scope };
59768
+ if (s.sessionId) out.sessionId = s.sessionId;
59769
+ return out;
59770
+ }
59771
+ var SEVERITY_RANK = { high: 0, warn: 1, info: 2 };
59772
+ function hasCompactionBetween(ctx, sessionId, from, to) {
59773
+ const list2 = ctx.compactionsBySession.get(sessionId);
59774
+ if (!list2) return false;
59775
+ return list2.some((t) => t > from && t <= to);
59776
+ }
59777
+ function detectCacheThrash(ctx) {
59778
+ let runs = 0;
59779
+ let longest = 0;
59780
+ let rewritten = 0;
59781
+ let savings = 0;
59782
+ const sessions = /* @__PURE__ */ new Set();
59783
+ for (const list2 of ctx.threads.values()) {
59784
+ let run = [];
59785
+ const flush = () => {
59786
+ if (run.length >= THRASH_RUN) {
59787
+ runs += 1;
59788
+ longest = Math.max(longest, run.length);
59789
+ for (const c of run) {
59790
+ const w = writesOf(c);
59791
+ rewritten += w;
59792
+ sessions.add(c.session_id);
59793
+ const p = ctx.priceOf(c.model);
59794
+ if (p) savings += w * (writePrice(c, p) - p.cacheRead) / 1e6;
59795
+ }
59796
+ }
59797
+ run = [];
59798
+ };
59799
+ for (let i = 1; i < list2.length; i++) {
59800
+ const c = list2[i];
59801
+ const prev = list2[i - 1];
59802
+ const ctxTokens = contextOf(c);
59803
+ const heavy = ctxTokens > 0 && writesOf(c) / ctxTokens >= THRASH_WRITE_SHARE;
59804
+ const broken = hasCompactionBetween(ctx, c.session_id, prev.ts, c.ts) || c.ts - prev.ts > ttlFor(list2);
59805
+ if (!heavy || broken) {
59806
+ flush();
59807
+ continue;
59808
+ }
59809
+ run.push(c);
59810
+ }
59811
+ flush();
59812
+ }
59813
+ if (runs === 0) return void 0;
59814
+ return withScope(
59815
+ {
59816
+ id: "cache-thrash",
59817
+ severity: runs >= 3 || rewritten >= 5e5 ? "high" : "warn",
59818
+ title: "Prompt cache keeps being rewritten",
59819
+ reason: `${runs} run${runs === 1 ? "" : "s"} of ${THRASH_RUN}+ consecutive calls rewrote \u2265 ${pct(THRASH_WRITE_SHARE)} of their context (longest run ${longest} calls, ${fmtK(rewritten)} tokens written again).`,
59820
+ evidence: { runs, longestRun: longest, tokensRewritten: rewritten },
59821
+ estimatedSavingsUsd: round4(savings),
59822
+ recommendation: "Something early in the prompt changes every turn (system prompt, tool list, a file re-read with a timestamp); keep the stable prefix stable so later calls hit the cache."
59823
+ },
59824
+ sessions
59825
+ );
59826
+ }
59827
+ function ttlFor(list2) {
59828
+ let w5 = 0;
59829
+ let w1 = 0;
59830
+ for (const c of list2) {
59831
+ w5 += num3(c.cache_create_5m);
59832
+ w1 += num3(c.cache_create_1h);
59833
+ }
59834
+ return w1 > w5 ? TTL_1H_MS : TTL_5M_MS;
59835
+ }
59836
+ function detectCacheCold(ctx) {
59837
+ let restarts = 0;
59838
+ let rewritten = 0;
59839
+ let savings = 0;
59840
+ let longestGap = 0;
59841
+ const sessions = /* @__PURE__ */ new Set();
59842
+ for (const list2 of ctx.threads.values()) {
59843
+ const ttl = ttlFor(list2);
59844
+ for (let i = 1; i < list2.length; i++) {
59845
+ const c = list2[i];
59846
+ const prev = list2[i - 1];
59847
+ const gap = c.ts - prev.ts;
59848
+ if (gap <= ttl) continue;
59849
+ if (hasCompactionBetween(ctx, c.session_id, prev.ts, c.ts)) continue;
59850
+ const ctxTokens = contextOf(c);
59851
+ const w = writesOf(c);
59852
+ if (ctxTokens === 0 || w / ctxTokens < COLD_WRITE_SHARE) continue;
59853
+ restarts += 1;
59854
+ rewritten += w;
59855
+ longestGap = Math.max(longestGap, gap);
59856
+ sessions.add(c.session_id);
59857
+ const p = ctx.priceOf(c.model);
59858
+ if (p) savings += w * (writePrice(c, p) - p.cacheRead) / 1e6;
59859
+ }
59860
+ }
59861
+ if (restarts === 0) return void 0;
59862
+ const minutes = Math.round(longestGap / 6e4);
59863
+ return withScope(
59864
+ {
59865
+ id: "cache-cold",
59866
+ severity: restarts >= 2 ? "warn" : "info",
59867
+ title: `Cache went cold ${restarts === 1 ? "once" : `${restarts} times`}`,
59868
+ reason: `${restarts} pause${restarts === 1 ? "" : "s"} longer than the cache TTL (longest ${minutes} min) forced ${fmtK(rewritten)} tokens to be written to cache again (~${usd(savings)}).`,
59869
+ evidence: { restarts, tokensRewritten: rewritten, longestGapMinutes: minutes },
59870
+ estimatedSavingsUsd: round4(savings),
59871
+ recommendation: "Long pauses mid-session re-pay the cache write; for walk-away breaks start a new session, or keep the turn cadence inside the TTL."
59872
+ },
59873
+ sessions
59874
+ );
59875
+ }
59876
+ function detectContextBloat(ctx) {
59877
+ const bySession2 = groupBySession(ctx.calls);
59878
+ const modelFor = /* @__PURE__ */ new Map();
59879
+ for (const [sid, rows] of bySession2) modelFor.set(sid, dominantModel2(rows));
59880
+ let count = 0;
59881
+ let tokens = 0;
59882
+ let savings = 0;
59883
+ const byTool = /* @__PURE__ */ new Map();
59884
+ const sessions = /* @__PURE__ */ new Set();
59885
+ for (const u of ctx.toolUses) {
59886
+ if (!BLOAT_TOOLS.has(u.tool)) continue;
59887
+ const est = estTokensFromChars(num3(u.result_chars));
59888
+ if (est <= BLOAT_TOKENS) continue;
59889
+ count += 1;
59890
+ tokens += est;
59891
+ byTool.set(u.tool, (byTool.get(u.tool) ?? 0) + 1);
59892
+ sessions.add(u.session_id);
59893
+ const p = ctx.priceOf(modelFor.get(u.session_id) ?? FALLBACK_MODEL) ?? ctx.priceOf(FALLBACK_MODEL);
59894
+ if (p) {
59895
+ const later = ctx.laterCalls(u.session_id, u.ts);
59896
+ savings += (est * p.cacheWrite5m + est * p.cacheRead * later) / 1e6;
59897
+ }
59898
+ }
59899
+ if (count === 0) return void 0;
59900
+ const top2 = [...byTool.entries()].sort((a, b) => b[1] - a[1])[0];
59901
+ return withScope(
59902
+ {
59903
+ id: "context-bloat",
59904
+ severity: count >= 5 || tokens >= 1e5 ? "high" : "warn",
59905
+ title: "Oversized tool results in context",
59906
+ reason: `${count} ${[...byTool.keys()].join("/")} result${count === 1 ? "" : "s"} above ${fmtK(BLOAT_TOKENS)} tokens put ~${fmtK(tokens)} tokens into context (mostly ${top2[0]}, ${top2[1]}\xD7), re-read on every later call.`,
59907
+ evidence: { results: count, estTokens: tokens, topTool: top2[0], topToolCount: top2[1] },
59908
+ estimatedSavingsUsd: round4(savings),
59909
+ recommendation: "Route large outputs through em_execute / em_execute_file / em_search so only the matching sections come back."
59910
+ },
59911
+ sessions
59912
+ );
59913
+ }
59914
+ function detectCompactionChurn(ctx) {
59915
+ const windowFor = /* @__PURE__ */ new Map();
59916
+ for (const s of ctx.snapshots) {
59917
+ if (num3(s.ctx_size) > 0) {
59918
+ const prev = windowFor.get(s.session_id);
59919
+ if (prev === void 0) windowFor.set(s.session_id, num3(s.ctx_size));
59920
+ }
59921
+ }
59922
+ let worstSession;
59923
+ let worstCount = 0;
59924
+ let early = 0;
59925
+ const sessions = /* @__PURE__ */ new Set();
59926
+ for (const [sid, evs] of groupBySession(ctx.contextEvents)) {
59927
+ const compactions = evs.filter((e) => e.kind === "compact");
59928
+ const window2 = windowFor.get(sid) ?? DEFAULT_WINDOW;
59929
+ const earlyHere = compactions.filter(
59930
+ (e) => e.pre_tokens != null && e.pre_tokens > 0 && e.pre_tokens < window2 * CHURN_EARLY_SHARE
59931
+ ).length;
59932
+ if (compactions.length >= CHURN_COMPACTIONS || earlyHere > 0) {
59933
+ sessions.add(sid);
59934
+ early += earlyHere;
59935
+ if (compactions.length > worstCount) {
59936
+ worstCount = compactions.length;
59937
+ worstSession = sid;
59938
+ }
59939
+ }
59940
+ }
59941
+ if (sessions.size === 0) return void 0;
59942
+ return withScope(
59943
+ {
59944
+ id: "compaction-churn",
59945
+ severity: worstCount >= CHURN_COMPACTIONS * 2 ? "high" : "warn",
59946
+ title: "Frequent context compactions",
59947
+ reason: `${sessions.size} session${sessions.size === 1 ? "" : "s"} compacted repeatedly (up to ${worstCount} times in ${(worstSession ?? "").slice(0, 8)}; ${early} below ${pct(CHURN_EARLY_SHARE)} of the window).`,
59948
+ evidence: { sessions: sessions.size, maxCompactions: worstCount, earlyCompactions: early },
59949
+ recommendation: "Each compaction rewrites the whole cache and loses detail; keep raw output out of context (em_* tools) and split long tasks into sessions."
59950
+ },
59951
+ sessions
59952
+ );
59953
+ }
59954
+ function detectOpusForTrivial(ctx) {
59955
+ const toolRequests = /* @__PURE__ */ new Set();
59956
+ for (const u of ctx.toolUses) if (u.request_id) toolRequests.add(u.request_id);
59957
+ const premium = ctx.calls.filter((c) => isPremium(c.model));
59958
+ if (premium.length < MIN_PREMIUM_CALLS) return void 0;
59959
+ const trivial = premium.filter(
59960
+ (c) => num3(c.output_tokens) < TRIVIAL_OUTPUT && c.stop_reason !== "tool_use" && !toolRequests.has(c.request_id)
59961
+ );
59962
+ const share2 = trivial.length / premium.length;
59963
+ if (share2 <= TRIVIAL_SHARE) return void 0;
59964
+ const sonnet = resolvePrice(FALLBACK_MODEL, ctx.overrides);
59965
+ let spent = 0;
59966
+ let repriced = 0;
59967
+ for (const c of trivial) {
59968
+ spent += costOf(c);
59969
+ if (sonnet) repriced += costOfCall(c, sonnet).total;
59970
+ }
59971
+ const savings = Math.max(0, spent - repriced);
59972
+ return withScope(
59973
+ {
59974
+ id: "opus-for-trivial",
59975
+ severity: share2 >= 0.6 ? "warn" : "info",
59976
+ title: "Premium model on trivial turns",
59977
+ reason: `${trivial.length} of ${premium.length} premium-model calls (${pct(share2)}) produced under ${TRIVIAL_OUTPUT} output tokens with no tool use, costing ${usd(spent)} (~${usd(repriced)} at Sonnet).`,
59978
+ evidence: {
59979
+ trivialCalls: trivial.length,
59980
+ premiumCalls: premium.length,
59981
+ share: Math.round(share2 * 100),
59982
+ spentUsd: round4(spent)
59983
+ },
59984
+ estimatedSavingsUsd: round4(savings),
59985
+ recommendation: "Short acknowledgements and lookups do not need the premium model; delegate routine turns to a Sonnet/Haiku subagent or switch model for simple work."
59986
+ },
59987
+ trivial.map((c) => c.session_id)
59988
+ );
59989
+ }
59990
+ function detectFastModeShare(ctx) {
59991
+ let fast = 0;
59992
+ let fastCalls = 0;
59993
+ let total = 0;
59994
+ const sessions = /* @__PURE__ */ new Set();
59995
+ for (const c of ctx.calls) {
59996
+ const cost = costOf(c);
59997
+ total += cost;
59998
+ if (c.speed === "fast") {
59999
+ fast += cost;
60000
+ fastCalls += 1;
60001
+ sessions.add(c.session_id);
60002
+ }
60003
+ }
60004
+ if (total <= 0) return void 0;
60005
+ const share2 = fast / total;
60006
+ if (share2 <= FAST_COST_SHARE) return void 0;
60007
+ return withScope(
60008
+ {
60009
+ id: "fast-mode-share",
60010
+ severity: "info",
60011
+ title: "Fast mode is a large share of spend",
60012
+ reason: `${fastCalls} fast-mode calls account for ${usd(fast)} of ${usd(total)} (${pct(share2)}) of priced cost.`,
60013
+ evidence: {
60014
+ fastCalls,
60015
+ fastUsd: round4(fast),
60016
+ totalUsd: round4(total),
60017
+ share: Math.round(share2 * 100)
60018
+ },
60019
+ recommendation: "Fast mode is billed at a premium; reserve it for interactive turns where latency matters."
60020
+ },
60021
+ sessions
60022
+ );
60023
+ }
60024
+ function detectThinkingShare(ctx) {
60025
+ const t = totals(ctx.calls);
60026
+ if (t.output < MIN_OUTPUT_FOR_THINKING || t.thinking <= 0) return void 0;
60027
+ const share2 = t.thinking / t.output;
60028
+ if (share2 <= THINKING_SHARE) return void 0;
60029
+ return withScope(
60030
+ {
60031
+ id: "thinking-share",
60032
+ severity: "info",
60033
+ title: "Most output tokens are thinking",
60034
+ reason: `${fmtK(t.thinking)} of ${fmtK(t.output)} output tokens (${pct(share2)}) were thinking, billed at the output rate.`,
60035
+ evidence: {
60036
+ thinkingTokens: t.thinking,
60037
+ outputTokens: t.output,
60038
+ share: Math.round(share2 * 100)
60039
+ },
60040
+ recommendation: "Lower the effort level for routine edits and lookups; keep high effort for design and debugging."
60041
+ },
60042
+ ctx.calls.map((c) => c.session_id)
60043
+ );
60044
+ }
60045
+ function detectSubagentCost(ctx) {
60046
+ const a = byAgent(ctx.calls);
60047
+ if (a.delegationShare <= DELEGATION_SHARE) return void 0;
60048
+ const subCost = a.subagents.reduce((s, x) => s + x.totals.costUsd, 0);
60049
+ const subCalls = a.subagents.reduce((s, x) => s + x.totals.calls, 0);
60050
+ return withScope(
60051
+ {
60052
+ id: "subagent-cost",
60053
+ severity: "info",
60054
+ title: "Subagents dominate spend",
60055
+ reason: `${a.subagents.length} subagent${a.subagents.length === 1 ? "" : "s"} made ${subCalls} calls for ${usd(subCost)}, ${pct(a.delegationShare)} of priced cost (main thread ${usd(a.main.costUsd)}).`,
60056
+ evidence: {
60057
+ subagents: a.subagents.length,
60058
+ subagentCalls: subCalls,
60059
+ subagentUsd: round4(subCost),
60060
+ share: Math.round(a.delegationShare * 100)
60061
+ },
60062
+ recommendation: "Check that subagents run on the cheapest model that fits (model: sonnet/haiku) and return summaries, not transcripts."
60063
+ },
60064
+ ctx.calls.filter((c) => c.sidechain).map((c) => c.session_id)
60065
+ );
60066
+ }
60067
+ var DRIVER_ADVICE = {
60068
+ "cache-write": {
60069
+ what: "cache writes",
60070
+ recommendation: "Cache writes dominate: a long idle gap or a cache-busting change (edited system prompt, reordered tools) re-wrote the prefix. Keep the prefix stable, or use the 1h cache deliberately for long pauses."
60071
+ },
60072
+ output: {
60073
+ what: "output",
60074
+ recommendation: "Output dominates: a long answer or heavy thinking. Lower the effort level or ask for a shorter answer; keep high effort for design and debugging."
60075
+ },
60076
+ context: {
60077
+ what: "context",
60078
+ recommendation: "Context dominates: the call re-read a large conversation. Compact, start a fresh session, or move the bulk into a subagent."
60079
+ }
60080
+ };
60081
+ function detectExpensiveTurn(ctx) {
60082
+ const limit = ctx.budgets.turnUsd ?? DEFAULT_TURN_USD;
60083
+ const hits = ctx.calls.filter((c) => costOf(c) > limit).sort((a, b2) => costOf(b2) - costOf(a));
60084
+ if (hits.length === 0) return void 0;
60085
+ const top2 = hits[0];
60086
+ const price2 = ctx.priceOf(top2.model);
60087
+ const b = price2 ? costOfCall(top2, price2) : { input: 0, output: 0, cacheWrite: 0, cacheRead: 0, tools: 0, total: costOf(top2) };
60088
+ const contextUsd = b.input + b.cacheRead;
60089
+ const driver = b.cacheWrite > b.output && b.cacheWrite > contextUsd ? "cache-write" : b.output > contextUsd ? "output" : "context";
60090
+ const advice = DRIVER_ADVICE[driver];
60091
+ const n = hits.length;
60092
+ return withScope(
60093
+ {
60094
+ id: "expensive-turn",
60095
+ severity: costOf(top2) > limit * 3 ? "high" : "warn",
60096
+ title: `${n} call${n === 1 ? "" : "s"} over ${usd(limit)}`,
60097
+ reason: `${n} call${n === 1 ? "" : "s"} cost more than ${usd(limit)} each; the largest was ${usd(costOf(top2))} on ${top2.model} with ${fmtK(contextOf(top2))} context tokens, driven by ${advice.what} (context ${usd(contextUsd)}, output ${usd(b.output)}, cache writes ${usd(b.cacheWrite)}).`,
60098
+ evidence: {
60099
+ calls: n,
60100
+ maxUsd: round4(costOf(top2)),
60101
+ model: top2.model,
60102
+ contextTokens: contextOf(top2),
60103
+ limitUsd: limit,
60104
+ driver,
60105
+ contextUsd: round4(contextUsd),
60106
+ outputUsd: round4(b.output),
60107
+ cacheWriteUsd: round4(b.cacheWrite)
60108
+ },
60109
+ recommendation: advice.recommendation
60110
+ },
60111
+ hits.map((c) => c.session_id)
60112
+ );
60113
+ }
60114
+ function detectErrorLoop(ctx) {
60115
+ const loops = ctx.events.filter((e) => e.category === "iteration_loop");
60116
+ const sessions = new Set(loops.map((e) => e.session_id));
60117
+ let worstTool = "";
60118
+ let worstRun = 0;
60119
+ const byThread = /* @__PURE__ */ new Map();
60120
+ for (const u of ctx.toolUses) {
60121
+ const key = `${u.session_id}\0${u.agent_id ?? ""}`;
60122
+ const list2 = byThread.get(key);
60123
+ if (list2) list2.push(u);
60124
+ else byThread.set(key, [u]);
60125
+ }
60126
+ for (const list2 of byThread.values()) {
60127
+ list2.sort((a, b) => a.ts - b.ts);
60128
+ let run = 0;
60129
+ let tool = "";
60130
+ for (const u of list2) {
60131
+ if (u.is_error && u.tool === tool) run += 1;
60132
+ else {
60133
+ run = u.is_error ? 1 : 0;
60134
+ tool = u.is_error ? u.tool : "";
60135
+ }
60136
+ if (run >= ERROR_RUN) {
60137
+ sessions.add(u.session_id);
60138
+ if (run > worstRun) {
60139
+ worstRun = run;
60140
+ worstTool = tool;
60141
+ }
60142
+ }
60143
+ }
60144
+ }
60145
+ if (loops.length === 0 && worstRun === 0) return void 0;
60146
+ const parts = [];
60147
+ if (loops.length)
60148
+ parts.push(`${loops.length} repeated-call loop${loops.length === 1 ? "" : "s"} recorded`);
60149
+ if (worstRun) parts.push(`${worstRun} consecutive ${worstTool} failures`);
60150
+ return withScope(
60151
+ {
60152
+ id: "error-loop",
60153
+ severity: "warn",
60154
+ title: "Repeated failures or loops",
60155
+ reason: `${parts.join(" and ")}; every retry re-reads the whole context.`,
60156
+ evidence: { loops: loops.length, longestErrorRun: worstRun, tool: worstTool },
60157
+ recommendation: "After two identical failures stop and change approach (em_recall for an earlier fix, read the error, try a different tool)."
60158
+ },
60159
+ sessions
60160
+ );
60161
+ }
60162
+ function detectBudget(ctx) {
60163
+ const out = [];
60164
+ const { sessionUsd, dailyUsd } = ctx.budgets;
60165
+ if (sessionUsd) {
60166
+ for (const [sid, rows] of groupBySession(ctx.calls)) {
60167
+ const cost = totals(rows).costUsd;
60168
+ if (cost <= sessionUsd) continue;
60169
+ out.push({
60170
+ id: "budget",
60171
+ severity: "high",
60172
+ title: "Session budget exceeded",
60173
+ reason: `Session ${sid.slice(0, 8)} has cost ${usd(cost)}, over the ${usd(sessionUsd)} session budget (${pct(cost / sessionUsd)}).`,
60174
+ evidence: { sessionUsd: round4(cost), budgetUsd: sessionUsd },
60175
+ scope: "session",
60176
+ sessionId: sid
60177
+ });
60178
+ }
60179
+ }
60180
+ if (dailyUsd) {
60181
+ const today = dayKey(ctx.now);
60182
+ const cost = totals(ctx.calls.filter((c) => dayKey(c.ts) === today)).costUsd;
60183
+ if (cost > dailyUsd)
60184
+ out.push({
60185
+ id: "budget",
60186
+ severity: "high",
60187
+ title: "Daily budget exceeded",
60188
+ reason: `Today (${today}) has cost ${usd(cost)}, over the ${usd(dailyUsd)} daily budget (${pct(cost / dailyUsd)}).`,
60189
+ evidence: { day: today, todayUsd: round4(cost), budgetUsd: dailyUsd },
60190
+ scope: "project"
60191
+ });
60192
+ }
60193
+ return out;
60194
+ }
60195
+ function detectUnpricedModel(ctx) {
60196
+ const unpriced = ctx.calls.filter((c) => !c.priced);
60197
+ if (unpriced.length === 0) return void 0;
60198
+ const models = [...new Set(unpriced.map((c) => c.model || "unknown"))];
60199
+ return withScope(
60200
+ {
60201
+ id: "unpriced-model",
60202
+ severity: "info",
60203
+ title: "Some calls are unpriced",
60204
+ reason: `${unpriced.length} of ${ctx.calls.length} calls use ${models.length} model${models.length === 1 ? "" : "s"} without a price (${models.slice(0, 3).join(", ")}), so cost totals undercount.`,
60205
+ evidence: { calls: unpriced.length, models: models.join(", ") },
60206
+ recommendation: 'Add the model to config.usage.pricing, e.g. {"pricing": {"<model substring>": {"input": 3, "output": 15}}} (USD per MTok).'
60207
+ },
60208
+ unpriced.map((c) => c.session_id)
60209
+ );
60210
+ }
60211
+ function detectContextNearLimit(ctx) {
60212
+ let latest;
60213
+ for (const s of ctx.snapshots) if (!latest || s.ts > latest.ts) latest = s;
60214
+ if (!latest || latest.ctx_used_pct == null || latest.ctx_used_pct < NEAR_LIMIT_PCT)
60215
+ return void 0;
60216
+ const p = latest.ctx_used_pct;
60217
+ return {
60218
+ id: "context-near-limit",
60219
+ severity: p >= NEAR_LIMIT_HIGH ? "high" : "warn",
60220
+ title: "Context window nearly full",
60221
+ reason: `The latest statusline reports ${Math.round(p)}% of the ${fmtK(num3(latest.ctx_size) || DEFAULT_WINDOW)}-token window in use; the next compaction is close.`,
60222
+ evidence: { ctxUsedPct: Math.round(p), ctxSize: num3(latest.ctx_size) },
60223
+ recommendation: "Save durable findings with em_remember now, then compact or start a fresh session.",
60224
+ scope: "session",
60225
+ sessionId: latest.session_id
60226
+ };
60227
+ }
60228
+ function findInsight(insights, id) {
60229
+ return insights.find((i) => i.id === id);
60230
+ }
60231
+ var ev = (i, key) => {
60232
+ const v = i?.evidence?.[key];
60233
+ return typeof v === "number" ? v : 0;
60234
+ };
60235
+ function scoreContextHealth(ctx, insights) {
60236
+ const reasons = [];
60237
+ let v = 100;
60238
+ const bloat = ev(findInsight(insights, "context-bloat"), "results");
60239
+ if (bloat) {
60240
+ const d = Math.min(30, bloat * 5);
60241
+ v -= d;
60242
+ reasons.push(`${bloat} oversized tool result${bloat === 1 ? "" : "s"} in context (\u2212${d})`);
60243
+ }
60244
+ let extra = 0;
60245
+ for (const evs of groupBySession(ctx.contextEvents).values())
60246
+ extra += Math.max(0, evs.filter((e) => e.kind === "compact").length - 1);
60247
+ if (extra) {
60248
+ const d = Math.min(30, extra * 10);
60249
+ v -= d;
60250
+ reasons.push(
60251
+ `${extra} compaction${extra === 1 ? "" : "s"} beyond the first per session (\u2212${d})`
60252
+ );
60253
+ }
60254
+ const near = findInsight(insights, "context-near-limit");
60255
+ if (near) {
60256
+ v -= 20;
60257
+ reasons.push(`context ${ev(near, "ctxUsedPct")}% full (\u221220)`);
60258
+ }
60259
+ const loop = findInsight(insights, "error-loop");
60260
+ if (loop) {
60261
+ const n = ev(loop, "loops") + (ev(loop, "longestErrorRun") ? 1 : 0);
60262
+ const d = Math.min(20, n * 10);
60263
+ v -= d;
60264
+ reasons.push(`${n} error loop${n === 1 ? "" : "s"} (\u2212${d})`);
60265
+ }
60266
+ if (!reasons.length)
60267
+ reasons.push(
60268
+ ctx.calls.length ? "no oversized results, compaction churn or loops" : "no usage recorded yet"
60269
+ );
60270
+ return { id: "context-health", value: clamp(v), reasons };
60271
+ }
60272
+ function scoreCostEfficiency(ctx, insights) {
60273
+ const reasons = [];
60274
+ let v = 100;
60275
+ const trivial = findInsight(insights, "opus-for-trivial");
60276
+ if (trivial) {
60277
+ const d = Math.round(ev(trivial, "share") / 100 * 50);
60278
+ v -= d;
60279
+ reasons.push(`${ev(trivial, "share")}% of premium-model calls were trivial (\u2212${d})`);
60280
+ }
60281
+ const fast = findInsight(insights, "fast-mode-share");
60282
+ if (fast) {
60283
+ const d = Math.round(ev(fast, "share") / 100 * 40);
60284
+ v -= d;
60285
+ reasons.push(`fast mode is ${ev(fast, "share")}% of spend (\u2212${d})`);
60286
+ }
60287
+ const exp = findInsight(insights, "expensive-turn");
60288
+ if (exp) {
60289
+ const n = ev(exp, "calls");
60290
+ const d = Math.min(30, n * 10);
60291
+ v -= d;
60292
+ reasons.push(`${n} call${n === 1 ? "" : "s"} over ${usd(ev(exp, "limitUsd"))} (\u2212${d})`);
60293
+ }
60294
+ const unpriced = findInsight(insights, "unpriced-model");
60295
+ if (unpriced && ctx.calls.length) {
60296
+ const share2 = ev(unpriced, "calls") / ctx.calls.length;
60297
+ const d = Math.round(share2 * 20);
60298
+ v -= d;
60299
+ reasons.push(`${pct(share2)} of calls unpriced (\u2212${d})`);
60300
+ }
60301
+ const budgets = insights.filter((i) => i.id === "budget").length;
60302
+ if (budgets) {
60303
+ const d = Math.min(40, budgets * 20);
60304
+ v -= d;
60305
+ reasons.push(`${budgets} budget${budgets === 1 ? "" : "s"} exceeded (\u2212${d})`);
60306
+ }
60307
+ if (!reasons.length)
60308
+ reasons.push(
60309
+ ctx.calls.length ? "no premium waste, fast-mode or budget issues" : "no usage recorded yet"
60310
+ );
60311
+ return { id: "cost-efficiency", value: clamp(v), reasons };
60312
+ }
60313
+ function scoreCacheEfficiency(ctx, insights) {
60314
+ const reasons = [];
60315
+ const t = totals(ctx.calls);
60316
+ if (t.calls === 0)
60317
+ return { id: "cache-efficiency", value: 0, reasons: ["no usage recorded yet"] };
60318
+ let v = t.cacheHitRate * 100;
60319
+ reasons.push(`cache hit rate ${pct(t.cacheHitRate)} over ${t.calls} calls`);
60320
+ const cold = ev(findInsight(insights, "cache-cold"), "restarts");
60321
+ if (cold) {
60322
+ const d = Math.min(25, cold * 5);
60323
+ v -= d;
60324
+ reasons.push(`${cold} cold restart${cold === 1 ? "" : "s"} (\u2212${d})`);
60325
+ }
60326
+ const thrash = ev(findInsight(insights, "cache-thrash"), "runs");
60327
+ if (thrash) {
60328
+ const d = Math.min(30, thrash * 10);
60329
+ v -= d;
60330
+ reasons.push(`${thrash} cache-rewrite run${thrash === 1 ? "" : "s"} (\u2212${d})`);
60331
+ }
60332
+ return { id: "cache-efficiency", value: clamp(v), reasons };
60333
+ }
60334
+ function scoreDelegation(ctx) {
60335
+ const reasons = [];
60336
+ const a = byAgent(ctx.calls);
60337
+ if (ctx.calls.length === 0)
60338
+ return { id: "delegation", value: 0, reasons: ["no usage recorded yet"] };
60339
+ if (a.subagents.length === 0)
60340
+ return {
60341
+ id: "delegation",
60342
+ value: 60,
60343
+ reasons: ["no subagent calls: the main thread carries every tool result itself"]
60344
+ };
60345
+ let v;
60346
+ if (a.delegationShare <= DELEGATION_SHARE) {
60347
+ v = 100;
60348
+ reasons.push(
60349
+ `subagents are ${pct(a.delegationShare)} of cost across ${a.subagents.length} agent${a.subagents.length === 1 ? "" : "s"}`
60350
+ );
60351
+ } else {
60352
+ v = Math.max(20, 100 - 150 * (a.delegationShare - DELEGATION_SHARE));
60353
+ reasons.push(
60354
+ `subagents are ${pct(a.delegationShare)} of cost, above ${pct(DELEGATION_SHARE)} (\u2212${100 - Math.round(v)})`
60355
+ );
60356
+ }
60357
+ const sub = totals(ctx.calls.filter((c) => c.sidechain));
60358
+ if (sub.calls && sub.cacheHitRate < 0.5) {
60359
+ v -= 15;
60360
+ reasons.push(`subagent cache hit rate ${pct(sub.cacheHitRate)} (\u221215)`);
60361
+ }
60362
+ return { id: "delegation", value: clamp(v), reasons };
60363
+ }
60364
+ function detectInsights(input2) {
60365
+ const calls = input2.calls.filter(isRealCall);
60366
+ const compactionsBySession = /* @__PURE__ */ new Map();
60367
+ for (const e of input2.contextEvents) {
60368
+ const list2 = compactionsBySession.get(e.session_id);
60369
+ if (list2) list2.push(e.ts);
60370
+ else compactionsBySession.set(e.session_id, [e.ts]);
60371
+ }
60372
+ for (const list2 of compactionsBySession.values()) list2.sort((a, b) => a - b);
60373
+ const callTs = /* @__PURE__ */ new Map();
60374
+ for (const [sid, rows] of groupBySession(calls))
60375
+ callTs.set(
60376
+ sid,
60377
+ rows.map((c) => c.ts).sort((a, b) => a - b)
60378
+ );
60379
+ const priceCache = /* @__PURE__ */ new Map();
60380
+ const overrides = input2.overrides;
60381
+ const ctx = {
60382
+ calls,
60383
+ threads: threads(calls),
60384
+ toolUses: input2.toolUses,
60385
+ contextEvents: input2.contextEvents,
60386
+ stats: input2.stats,
60387
+ events: input2.events,
60388
+ snapshots: input2.snapshots,
60389
+ now: input2.now ?? Date.now(),
60390
+ budgets: input2.budgets ?? {},
60391
+ overrides,
60392
+ priceOf: (model) => {
60393
+ if (!priceCache.has(model)) priceCache.set(model, resolvePrice(model, overrides));
60394
+ return priceCache.get(model);
60395
+ },
60396
+ compactionsBySession,
60397
+ laterCalls: (sid, ts) => (callTs.get(sid) ?? []).filter((t) => t > ts).length
60398
+ };
60399
+ const insights = [];
60400
+ const push = (i) => {
60401
+ if (i) insights.push(i);
60402
+ };
60403
+ push(detectCacheThrash(ctx));
60404
+ push(detectCacheCold(ctx));
60405
+ push(detectContextBloat(ctx));
60406
+ push(detectCompactionChurn(ctx));
60407
+ push(detectOpusForTrivial(ctx));
60408
+ push(detectFastModeShare(ctx));
60409
+ push(detectThinkingShare(ctx));
60410
+ push(detectSubagentCost(ctx));
60411
+ push(detectExpensiveTurn(ctx));
60412
+ push(detectErrorLoop(ctx));
60413
+ for (const b of detectBudget(ctx)) push(b);
60414
+ push(detectUnpricedModel(ctx));
60415
+ push(detectContextNearLimit(ctx));
60416
+ insights.sort(
60417
+ (a, b) => SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity] || (b.estimatedSavingsUsd ?? 0) - (a.estimatedSavingsUsd ?? 0) || a.id.localeCompare(b.id)
60418
+ );
60419
+ const scores = [
60420
+ scoreContextHealth(ctx, insights),
60421
+ scoreCostEfficiency(ctx, insights),
60422
+ scoreCacheEfficiency(ctx, insights),
60423
+ scoreDelegation(ctx)
60424
+ ];
60425
+ return { insights, scores };
60426
+ }
60427
+ var INSIGHT_EVENT_CATEGORIES = ["iteration_loop", "error", "tool_call"];
60428
+ function loadInsightInput(db, ctx, opts = {}) {
60429
+ const filter = { sessionId: opts.sessionId, since: opts.since };
60430
+ const usage = ctx.config.usage;
60431
+ return {
60432
+ calls: db.apiCalls(filter),
60433
+ toolUses: db.toolUses(filter),
60434
+ contextEvents: db.contextEvents(filter),
60435
+ snapshots: db.statuslineSnapshots(filter),
60436
+ stats: opts.sessionId ? db.statsBySession(opts.sessionId) : db.statsAll(),
60437
+ events: opts.sessionId ? db.eventsBySession(opts.sessionId, { categories: INSIGHT_EVENT_CATEGORIES }) : db.eventsRecent(500, INSIGHT_EVENT_CATEGORIES).filter((e) => !opts.since || e.ts >= opts.since),
60438
+ now: opts.now ?? Date.now(),
60439
+ budgets: usage.budgets,
60440
+ overrides: usage.pricing
60441
+ };
60442
+ }
60443
+
60444
+ // src/usage/brief.ts
60445
+ var DAY_MS = 864e5;
60446
+ function todaysCalls(db, now) {
60447
+ const today = dayKey(now);
60448
+ return db.apiCalls({ since: now - 1.5 * DAY_MS }).filter((c) => dayKey(c.ts) === today);
60449
+ }
60450
+ function usageBrief(db, ctx, opts = {}) {
60451
+ const now = opts.now ?? Date.now();
60452
+ const today = totals(todaysCalls(db, now));
60453
+ const session = totals(opts.sessionId ? db.apiCalls({ sessionId: opts.sessionId }) : []);
60454
+ const cacheHitRate = today.calls ? today.cacheHitRate : session.cacheHitRate;
60455
+ const out = {
60456
+ line: "",
60457
+ sessionUsd: session.costUsd,
60458
+ todayUsd: today.costUsd,
60459
+ cacheHitRate
60460
+ };
60461
+ if (today.calls === 0 && session.calls === 0) return out;
60462
+ const parts = [
60463
+ `today ${formatUsd(today.costUsd)} (${today.calls} call${today.calls === 1 ? "" : "s"}, ${Math.round(cacheHitRate * 100)}% cache hits)`
60464
+ ];
60465
+ if (session.calls > 0) parts.push(`session ${formatUsd(session.costUsd)}`);
60466
+ try {
60467
+ const scope = session.calls > 0 ? { sessionId: opts.sessionId } : { since: now - 1.5 * DAY_MS };
60468
+ const { insights } = detectInsights(loadInsightInput(db, ctx, { ...scope, now }));
60469
+ const top2 = insights[0];
60470
+ if (top2) {
60471
+ out.topInsight = top2;
60472
+ const saving = top2.estimatedSavingsUsd && top2.estimatedSavingsUsd >= 5e-3 ? ` (~${formatUsd(top2.estimatedSavingsUsd)})` : "";
60473
+ parts.push(`top insight: ${top2.title.charAt(0).toLowerCase()}${top2.title.slice(1)}${saving}`);
60474
+ }
60475
+ } catch {
60476
+ }
60477
+ out.line = `usage: ${parts.join(" \xB7 ")}`;
60478
+ return out;
60479
+ }
60480
+
60481
+ // src/usage/ingest.ts
60482
+ init_esm_shims();
60483
+ import { createHash as createHash3 } from "crypto";
60484
+ import { closeSync, existsSync as existsSync18, openSync, readdirSync as readdirSync6, readSync, statSync as statSync11 } from "fs";
60485
+ import { homedir as homedir4 } from "os";
60486
+ import { basename as basename2, dirname as dirname11, join as join24, resolve as resolve8, sep as sep3 } from "path";
60487
+
60488
+ // src/usage/transcript.ts
60489
+ init_esm_shims();
60490
+ var NON_TEXT_PART_CHARS = 6400;
60491
+ var DENIAL_RE = /^\s*PreToolUse:|hook error|\bdenied\b/i;
60492
+ function isObj2(v) {
60493
+ return typeof v === "object" && v !== null && !Array.isArray(v);
60494
+ }
60495
+ function str2(v) {
60496
+ return typeof v === "string" && v.length ? v : null;
60497
+ }
60498
+ function num4(v) {
60499
+ return typeof v === "number" && Number.isFinite(v) ? v : 0;
60500
+ }
60501
+ function numOrNull(v) {
60502
+ return typeof v === "number" && Number.isFinite(v) ? v : null;
60503
+ }
60504
+ function tsOf(e) {
60505
+ const t = e.timestamp;
60506
+ if (typeof t === "string") {
60507
+ const ms = Date.parse(t);
60508
+ if (Number.isFinite(ms) && ms > 0) return ms;
60509
+ return void 0;
60510
+ }
60511
+ if (typeof t === "number" && Number.isFinite(t) && t > 0) return t < 1e12 ? t * 1e3 : t;
60512
+ return void 0;
60513
+ }
60514
+ function resultChars(content) {
60515
+ if (typeof content === "string") return content.length;
60516
+ if (Array.isArray(content)) {
60517
+ let n = 0;
60518
+ for (const part of content) {
60519
+ if (!isObj2(part)) {
60520
+ n += safeJsonLength(part);
60521
+ continue;
60522
+ }
60523
+ const kind = str2(part.type);
60524
+ if (kind === "text" || typeof part.text === "string") n += str2(part.text)?.length ?? 0;
60525
+ else if (kind === "image" || kind === "document" || kind === "tool_reference")
60526
+ n += NON_TEXT_PART_CHARS;
60527
+ else n += safeJsonLength(part);
60528
+ }
60529
+ return n;
60530
+ }
60531
+ if (content === void 0 || content === null) return 0;
60532
+ return safeJsonLength(content);
60533
+ }
60534
+ function resultText(content) {
60535
+ if (typeof content === "string") return content;
60536
+ if (Array.isArray(content)) {
60537
+ const parts = [];
60538
+ for (const part of content)
60539
+ if (isObj2(part) && typeof part.text === "string") parts.push(part.text);
60540
+ return parts.join("\n");
60541
+ }
60542
+ return "";
60543
+ }
60544
+ function safeJsonLength(v) {
60545
+ try {
60546
+ return JSON.stringify(v)?.length ?? 0;
60547
+ } catch {
60548
+ return 0;
60549
+ }
60550
+ }
60551
+ function parseTranscriptLines(lines, ctx) {
60552
+ const calls = /* @__PURE__ */ new Map();
60553
+ const toolUses = /* @__PURE__ */ new Map();
60554
+ const contextEvents = [];
60555
+ let lastTs;
60556
+ let prevTs;
60557
+ const fallbackTs = typeof ctx.fallbackTs === "number" && Number.isFinite(ctx.fallbackTs) && ctx.fallbackTs > 0 ? ctx.fallbackTs : Date.now();
60558
+ let denied = 0;
60559
+ for (const raw of lines) {
60560
+ const line = raw.trim();
60561
+ if (!line) continue;
60562
+ let e;
60563
+ try {
60564
+ e = JSON.parse(line);
60565
+ } catch {
60566
+ continue;
60567
+ }
60568
+ if (!isObj2(e)) continue;
60569
+ const type = str2(e.type);
60570
+ if (!type) continue;
60571
+ const sessionId = str2(e.sessionId) ?? ctx.sessionId;
60572
+ const agentId = str2(e.agentId);
60573
+ const sidechain = e.isSidechain === true ? 1 : 0;
60574
+ const real = tsOf(e);
60575
+ const ts = real ?? prevTs ?? fallbackTs;
60576
+ prevTs = ts;
60577
+ if (real !== void 0) lastTs = lastTs === void 0 ? real : Math.max(lastTs, real);
60578
+ if (type === "assistant") {
60579
+ const msg = isObj2(e.message) ? e.message : void 0;
60580
+ if (!msg) continue;
60581
+ const messageId = str2(msg.id);
60582
+ const requestId = str2(e.requestId) ?? messageId;
60583
+ if (Array.isArray(msg.content)) {
60584
+ for (const block of msg.content) {
60585
+ if (!isObj2(block) || block.type !== "tool_use") continue;
60586
+ const id = str2(block.id);
60587
+ if (!id) continue;
60588
+ const prev2 = toolUses.get(id);
60589
+ toolUses.set(id, {
60590
+ tool_use_id: id,
60591
+ session_id: prev2?.session_id ?? sessionId,
60592
+ agent_id: prev2?.agent_id ?? agentId,
60593
+ ts: prev2?.ts ?? ts,
60594
+ tool: str2(block.name) ?? prev2?.tool ?? "",
60595
+ input_chars: block.input === void 0 ? 0 : safeJsonLength(block.input),
60596
+ result_chars: prev2?.result_chars ?? null,
60597
+ is_error: prev2?.is_error ?? 0,
60598
+ request_id: requestId ?? prev2?.request_id ?? null
60599
+ });
60600
+ }
60601
+ }
60602
+ const usage = isObj2(msg.usage) ? msg.usage : void 0;
60603
+ if (!usage || !messageId || !requestId) continue;
60604
+ const model = str2(msg.model) ?? "unknown";
60605
+ const cc = isObj2(usage.cache_creation) ? usage.cache_creation : void 0;
60606
+ const create5m = cc ? num4(cc.ephemeral_5m_input_tokens) : num4(usage.cache_creation_input_tokens);
60607
+ const create1h = cc ? num4(cc.ephemeral_1h_input_tokens) : 0;
60608
+ const details = isObj2(usage.output_tokens_details) ? usage.output_tokens_details : void 0;
60609
+ const server2 = isObj2(usage.server_tool_use) ? usage.server_tool_use : void 0;
60610
+ const key = `${requestId}\0${messageId}`;
60611
+ const prev = calls.get(key);
60612
+ calls.set(key, {
60613
+ request_id: requestId,
60614
+ message_id: messageId,
60615
+ session_id: prev?.session_id ?? sessionId,
60616
+ agent_id: agentId ?? prev?.agent_id ?? null,
60617
+ sidechain: prev?.sidechain || sidechain,
60618
+ ts: prev?.ts ?? ts,
60619
+ model,
60620
+ input_tokens: num4(usage.input_tokens),
60621
+ output_tokens: num4(usage.output_tokens),
60622
+ cache_create_5m: create5m,
60623
+ cache_create_1h: create1h,
60624
+ cache_read: num4(usage.cache_read_input_tokens),
60625
+ thinking_tokens: details ? num4(details.thinking_tokens) : 0,
60626
+ web_searches: server2 ? num4(server2.web_search_requests) : 0,
60627
+ web_fetches: server2 ? num4(server2.web_fetch_requests) : 0,
60628
+ service_tier: str2(usage.service_tier),
60629
+ speed: str2(usage.speed),
60630
+ stop_reason: str2(msg.stop_reason) ?? prev?.stop_reason ?? null,
60631
+ prompt_id: str2(e.promptId) ?? prev?.prompt_id ?? null,
60632
+ git_branch: str2(e.gitBranch) ?? prev?.git_branch ?? null,
60633
+ cwd: str2(e.cwd) ?? prev?.cwd ?? null,
60634
+ effort: str2(e.effort) ?? prev?.effort ?? null,
60635
+ version: str2(e.version) ?? prev?.version ?? null,
60636
+ entrypoint: str2(e.entrypoint) ?? prev?.entrypoint ?? null
60637
+ });
60638
+ continue;
60639
+ }
60640
+ if (type === "user") {
60641
+ const msg = isObj2(e.message) ? e.message : void 0;
60642
+ if (!msg || !Array.isArray(msg.content)) continue;
60643
+ for (const block of msg.content) {
60644
+ if (!isObj2(block) || block.type !== "tool_result") continue;
60645
+ const id = str2(block.tool_use_id);
60646
+ if (!id) continue;
60647
+ const prev = toolUses.get(id);
60648
+ let isError = prev?.is_error ?? 0;
60649
+ if (block.is_error === true) {
60650
+ if (DENIAL_RE.test(resultText(block.content))) denied += 1;
60651
+ else isError = 1;
60652
+ }
60653
+ toolUses.set(id, {
60654
+ tool_use_id: id,
60655
+ session_id: prev?.session_id ?? sessionId,
60656
+ agent_id: prev?.agent_id ?? agentId,
60657
+ ts: prev?.ts ?? ts,
60658
+ tool: prev?.tool ?? "",
60659
+ input_chars: prev?.input_chars ?? 0,
60660
+ result_chars: resultChars(block.content),
60661
+ is_error: isError,
60662
+ request_id: prev?.request_id ?? null
60663
+ });
60664
+ }
60665
+ continue;
60666
+ }
60667
+ if (type === "system") {
60668
+ const subtype = str2(e.subtype);
60669
+ if (subtype === "compact_boundary") {
60670
+ const meta4 = isObj2(e.compactMetadata) ? e.compactMetadata : void 0;
60671
+ contextEvents.push({
60672
+ session_id: sessionId,
60673
+ ts,
60674
+ kind: "compact",
60675
+ trigger: meta4 ? str2(meta4.trigger) : null,
60676
+ pre_tokens: meta4 ? numOrNull(meta4.preTokens) : null,
60677
+ post_tokens: meta4 ? numOrNull(meta4.postTokens) : null
60678
+ });
60679
+ } else if (subtype === "local_command") {
60680
+ const run = isObj2(e.commandRun) ? e.commandRun : void 0;
60681
+ if (run && str2(run.command) === "clear") {
60682
+ contextEvents.push({
60683
+ session_id: sessionId,
60684
+ ts,
60685
+ kind: "clear",
60686
+ trigger: "manual",
60687
+ pre_tokens: null,
60688
+ post_tokens: null
60689
+ });
60690
+ }
60691
+ }
60692
+ }
60693
+ }
60694
+ const out = {
60695
+ calls: [...calls.values()],
60696
+ toolUses: [...toolUses.values()],
60697
+ contextEvents,
60698
+ denied
60699
+ };
60700
+ if (lastTs !== void 0) out.lastTs = lastTs;
60701
+ return out;
60702
+ }
60703
+
60704
+ // src/usage/ingest.ts
60705
+ var ZERO = { calls: 0, toolUses: 0, contextEvents: 0 };
60706
+ var HEAD_BYTES = 512;
60707
+ var SNAPSHOT_MAX_AGE_MS = 30 * 24 * 60 * 6e4;
60708
+ var SNAPSHOT_KEEP_PER_SESSION = 500;
60709
+ var SESSION_ID_RE = /^[A-Za-z0-9_-]{1,80}$/;
60710
+ function isValidSessionId(id) {
60711
+ return SESSION_ID_RE.test(id);
60712
+ }
60713
+ function claudeDir2() {
60714
+ return process.env.EMINIAR_CLAUDE_DIR || join24(homedir4(), ".claude");
60715
+ }
60716
+ function insideDir(path3, dir) {
60717
+ const d = resolve8(dir);
60718
+ const p = resolve8(path3);
60719
+ return p === d || p.startsWith(d.endsWith(sep3) ? d : d + sep3);
60720
+ }
60721
+ function isTranscriptFile(path3) {
60722
+ if (!path3.endsWith(".jsonl")) return false;
60723
+ try {
60724
+ return statSync11(path3).isFile();
60725
+ } catch {
60726
+ return false;
60727
+ }
60728
+ }
60729
+ function transcriptSlug(root) {
60730
+ return root.replace(/[^A-Za-z0-9]/g, "-");
60731
+ }
60732
+ function transcriptDirFor(root) {
60733
+ return join24(claudeDir2(), "projects", transcriptSlug(root));
60734
+ }
60735
+ function usageEnabled(ctx) {
60736
+ return ctx.config.usage.enabled !== false;
60737
+ }
60738
+ function priceCalls(calls, overrides) {
60739
+ return calls.map((c) => {
60740
+ const price2 = resolvePrice(c.model, overrides);
60741
+ if (!price2) return { ...c, cost_usd: null, priced: 0 };
60742
+ return { ...c, cost_usd: costOfCall(c, price2).total, priced: 1 };
60743
+ });
60744
+ }
60745
+ function readCompleteLines(path3, offset, size) {
60746
+ const length = size - offset;
60747
+ if (length <= 0) return { lines: [], nextOffset: offset };
60748
+ const buf = Buffer.alloc(length);
60749
+ const fd = openSync(path3, "r");
60750
+ let read2 = 0;
60751
+ try {
60752
+ while (read2 < length) {
60753
+ const n = readSync(fd, buf, read2, length - read2, offset + read2);
60754
+ if (n <= 0) break;
60755
+ read2 += n;
60756
+ }
60757
+ } finally {
60758
+ closeSync(fd);
60759
+ }
60760
+ const end = buf.lastIndexOf(10, read2 - 1);
60761
+ if (end < 0) return { lines: [], nextOffset: offset };
60762
+ const text2 = buf.toString("utf8", 0, end + 1);
60763
+ return { lines: text2.split("\n"), nextOffset: offset + end + 1 };
60764
+ }
60765
+ function headHash(path3, length) {
60766
+ if (length <= 0) return null;
60767
+ const buf = Buffer.alloc(length);
60768
+ let read2 = 0;
60769
+ const fd = openSync(path3, "r");
60770
+ try {
60771
+ while (read2 < length) {
60772
+ const n = readSync(fd, buf, read2, length - read2, read2);
60773
+ if (n <= 0) break;
60774
+ read2 += n;
60775
+ }
60776
+ } finally {
60777
+ closeSync(fd);
60778
+ }
60779
+ return createHash3("sha1").update(buf.subarray(0, read2)).digest("hex");
60780
+ }
60781
+ function byteAt(path3, pos) {
60782
+ if (pos < 0) return void 0;
60783
+ const buf = Buffer.alloc(1);
60784
+ const fd = openSync(path3, "r");
60785
+ try {
60786
+ return readSync(fd, buf, 0, 1, pos) === 1 ? buf[0] : void 0;
60787
+ } finally {
60788
+ closeSync(fd);
60789
+ }
60790
+ }
60791
+ function sessionIdFromName(path3) {
60792
+ const dir = dirname11(path3);
60793
+ if (basename2(dir) === "subagents") return basename2(dirname11(dir));
60794
+ const name = basename2(path3, ".jsonl");
60795
+ return name.startsWith("agent-") ? void 0 : name;
60796
+ }
60797
+ function contextEventExists(db, e) {
60798
+ return db.contextEvents({ sessionId: e.session_id, since: e.ts, until: e.ts + 1 }).some((x) => x.kind === e.kind);
60799
+ }
60800
+ function resumeOffset(path3, prev, size, mtime) {
60801
+ if (!prev || prev.offset <= 0) return 0;
60802
+ if (size < prev.offset) return 0;
60803
+ if (prev.head_hash) {
60804
+ const want = Math.min(HEAD_BYTES, prev.size);
60805
+ try {
60806
+ if (headHash(path3, want) !== prev.head_hash) return 0;
60807
+ } catch {
60808
+ return 0;
60809
+ }
60810
+ }
60811
+ if (prev.mtime !== mtime) {
60812
+ try {
60813
+ if (byteAt(path3, prev.offset - 1) !== 10) return 0;
60814
+ } catch {
60815
+ return 0;
60816
+ }
60817
+ }
60818
+ return prev.offset;
60819
+ }
60820
+ function ingestTranscript(db, path3, opts = {}) {
60821
+ if (!path3.endsWith(".jsonl")) return ZERO;
60822
+ let size;
60823
+ let mtime;
60824
+ try {
60825
+ const st = statSync11(path3);
60826
+ if (!st.isFile()) return ZERO;
60827
+ size = st.size;
60828
+ mtime = Math.floor(st.mtimeMs);
60829
+ } catch {
60830
+ return ZERO;
60831
+ }
60832
+ const prev = db.transcriptFileGet(path3);
60833
+ const offset = resumeOffset(path3, prev, size, mtime);
60834
+ const sessionHint = opts.sessionId ?? prev?.session_id ?? sessionIdFromName(path3);
60835
+ if (offset === size) {
60836
+ if (!prev || prev.size !== size || prev.mtime !== mtime || sessionHint && !prev.session_id)
60837
+ db.transcriptFileSet({
60838
+ path: path3,
60839
+ session_id: sessionHint ?? null,
60840
+ offset,
60841
+ size,
60842
+ mtime,
60843
+ head_hash: prev?.size === size ? prev.head_hash ?? null : fingerprint(path3, size),
60844
+ ingested_at: Date.now()
60845
+ });
60846
+ return ZERO;
60847
+ }
60848
+ const { lines, nextOffset } = readCompleteLines(path3, offset, size);
60849
+ const parsed = parseTranscriptLines(lines, {
60850
+ sessionId: sessionHint ?? "unknown",
60851
+ path: path3,
60852
+ fallbackTs: mtime
60853
+ });
60854
+ const calls = priceCalls(parsed.calls, opts.overrides);
60855
+ const counts = { calls: 0, toolUses: 0, contextEvents: 0 };
60856
+ const sessionSeen = parsed.calls[0]?.session_id ?? parsed.toolUses[0]?.session_id;
60857
+ const head = fingerprint(path3, size);
60858
+ db.transaction(() => {
60859
+ counts.calls = db.apiCallsUpsert(calls);
60860
+ counts.toolUses = db.toolUsesUpsert(parsed.toolUses);
60861
+ for (const e of parsed.contextEvents) {
60862
+ if (contextEventExists(db, e)) continue;
60863
+ db.contextEventInsert(e);
60864
+ counts.contextEvents++;
60865
+ }
60866
+ db.transcriptFileSet({
60867
+ path: path3,
60868
+ session_id: sessionHint ?? sessionSeen ?? null,
60869
+ offset: nextOffset,
60870
+ size,
60871
+ mtime,
60872
+ head_hash: head,
60873
+ ingested_at: Date.now()
60874
+ });
60875
+ });
60876
+ return counts;
60877
+ }
60878
+ function fingerprint(path3, size) {
60879
+ try {
60880
+ return headHash(path3, Math.min(HEAD_BYTES, size));
60881
+ } catch {
60882
+ return null;
60883
+ }
60884
+ }
60885
+ function pruneSnapshots(db) {
60886
+ try {
60887
+ db.statuslineSnapshotsPrune({
60888
+ olderThan: Date.now() - SNAPSHOT_MAX_AGE_MS,
60889
+ keepPerSession: SNAPSHOT_KEEP_PER_SESSION
60890
+ });
60891
+ } catch {
60892
+ }
60893
+ }
60894
+ function add(a, b) {
60895
+ return {
60896
+ calls: a.calls + b.calls,
60897
+ toolUses: a.toolUses + b.toolUses,
60898
+ contextEvents: a.contextEvents + b.contextEvents
60899
+ };
60900
+ }
60901
+ function listJsonl(dir) {
60902
+ try {
60903
+ return readdirSync6(dir).filter((f) => f.endsWith(".jsonl")).map((f) => join24(dir, f));
60904
+ } catch {
60905
+ return [];
60906
+ }
60907
+ }
60908
+ function firstEntrySessionId(path3) {
60909
+ try {
60910
+ const { lines } = readCompleteLines(path3, 0, Math.min(statSync11(path3).size, 64 * 1024));
60911
+ for (const line of lines) {
60912
+ if (!line.trim()) continue;
60913
+ const e = JSON.parse(line);
60914
+ return typeof e.sessionId === "string" ? e.sessionId : void 0;
60915
+ }
60916
+ } catch {
60917
+ }
60918
+ return void 0;
60919
+ }
60920
+ function sidechainFilesFor(db, dir, sessionId) {
60921
+ if (!isValidSessionId(sessionId)) return [];
60922
+ const inside = (p) => insideDir(p, dir) && p.endsWith(".jsonl");
60923
+ const out = listJsonl(join24(dir, sessionId, "subagents")).filter(inside);
60924
+ for (const path3 of listJsonl(dir)) {
60925
+ if (!inside(path3) || !basename2(path3).startsWith("agent-")) continue;
60926
+ const known = db.transcriptFileGet(path3)?.session_id;
60927
+ let owner = known ?? void 0;
60928
+ if (!owner) {
60929
+ owner = firstEntrySessionId(path3);
60930
+ if (owner) {
60931
+ db.transcriptFileSet({
60932
+ path: path3,
60933
+ session_id: owner,
60934
+ offset: 0,
60935
+ size: 0,
60936
+ mtime: 0,
60937
+ ingested_at: Date.now()
60938
+ });
60939
+ }
60940
+ }
60941
+ if (owner === sessionId) out.push(path3);
60942
+ }
60943
+ return out;
60944
+ }
60945
+ function ingestSession(db, root, sessionId, transcriptPath, opts = {}) {
60946
+ if (!isValidSessionId(sessionId)) return ZERO;
60947
+ let main;
60948
+ const trusted = transcriptPath ?? db.sessionGet(sessionId)?.transcript_path ?? void 0;
60949
+ if (trusted) {
60950
+ if (!isTranscriptFile(trusted)) return ZERO;
60951
+ main = resolve8(trusted);
60952
+ } else {
60953
+ const built = resolve8(transcriptDirFor(root), `${sessionId}.jsonl`);
60954
+ if (!insideDir(built, claudeDir2())) return ZERO;
60955
+ main = built;
60956
+ }
60957
+ let total = ingestTranscript(db, main, { sessionId, overrides: opts.overrides });
60958
+ for (const path3 of sidechainFilesFor(db, dirname11(main), sessionId)) {
60959
+ total = add(total, ingestTranscript(db, path3, { sessionId, overrides: opts.overrides }));
60960
+ }
60961
+ pruneSnapshots(db);
60962
+ return total;
60963
+ }
60964
+ function ingestProject(db, root, opts = {}) {
60965
+ const dir = transcriptDirFor(root);
60966
+ const files = new Set(listJsonl(dir));
60967
+ if (existsSync18(dir)) {
60968
+ try {
60969
+ for (const entry of readdirSync6(dir, { withFileTypes: true })) {
60970
+ if (entry.isDirectory())
60971
+ for (const sub of listJsonl(join24(dir, entry.name, "subagents"))) files.add(sub);
60972
+ }
60973
+ } catch {
60974
+ }
60975
+ }
60976
+ for (const s of db.sessionsRecent(1e3)) if (s.transcript_path) files.add(s.transcript_path);
60977
+ let total = ZERO;
60978
+ let touched = 0;
60979
+ for (const path3 of files) {
60980
+ let st;
60981
+ try {
60982
+ const s = statSync11(path3);
60983
+ if (!s.isFile()) continue;
60984
+ st = { size: s.size, mtime: Math.floor(s.mtimeMs) };
60985
+ } catch {
60986
+ continue;
60987
+ }
60988
+ const prev = db.transcriptFileGet(path3);
60989
+ if (prev && prev.size === st.size && prev.mtime === st.mtime && prev.offset === st.size)
60990
+ continue;
60991
+ touched++;
60992
+ const sessionId = prev?.session_id ?? sessionIdFromName(path3);
60993
+ total = add(total, ingestTranscript(db, path3, { sessionId, overrides: opts.overrides }));
60994
+ }
60995
+ pruneSnapshots(db);
60996
+ return { ...total, files: touched };
60997
+ }
60998
+
58652
60999
  // src/tools/em_stats.ts
58653
61000
  var emStats = defineTool({
58654
61001
  name: "em_stats",
@@ -58704,9 +61051,28 @@ var emStats = defineTool({
58704
61051
  parts.push(
58705
61052
  `index: ${db.countDocuments()} documents (${db.countDocuments("exec")} exec, ${db.countDocuments("batch")} batch, ${db.countDocuments("file")} file, ${db.countDocuments("url")} url, ${db.countDocuments("code")} code), ${db.vecCount()} vectors`
58706
61053
  );
61054
+ const cost = usageLine(ctx, sessionId);
61055
+ if (cost) parts.push(cost);
58707
61056
  return text(parts.join("\n\n"));
58708
61057
  }
58709
61058
  });
61059
+ function usageLine(ctx, sessionId) {
61060
+ if (!usageEnabled(ctx)) return void 0;
61061
+ try {
61062
+ const db = getStorage(ctx);
61063
+ let failed;
61064
+ try {
61065
+ ingestSession(db, ctx.root, sessionId, void 0, { overrides: ctx.config.usage.pricing });
61066
+ } catch (err) {
61067
+ failed = `usage: ingest failed: ${err.message}`;
61068
+ }
61069
+ const b = usageBrief(db, ctx, { sessionId });
61070
+ const line = b.todayUsd || b.sessionUsd ? `usage: today ${formatUsd(b.todayUsd)} \xB7 session ${formatUsd(b.sessionUsd)} \xB7 see em_usage` : void 0;
61071
+ return [line, failed].filter((s) => s).join("\n") || void 0;
61072
+ } catch (err) {
61073
+ return `usage: unavailable: ${err.message}`;
61074
+ }
61075
+ }
58710
61076
  function mergeByTool(rows) {
58711
61077
  const m = /* @__PURE__ */ new Map();
58712
61078
  for (const r of rows) {
@@ -58860,6 +61226,536 @@ function groupBySource(edges) {
58860
61226
  return out;
58861
61227
  }
58862
61228
 
61229
+ // src/tools/em_usage.ts
61230
+ init_esm_shims();
61231
+ var USAGE_VIEWS = [
61232
+ "summary",
61233
+ "daily",
61234
+ "weekly",
61235
+ "monthly",
61236
+ "models",
61237
+ "sessions",
61238
+ "blocks",
61239
+ "turns",
61240
+ "tools",
61241
+ "insights",
61242
+ "savings"
61243
+ ];
61244
+ var DAY4 = 864e5;
61245
+ var UNITS = { s: 1e3, m: 6e4, h: 36e5, d: DAY4, w: 7 * DAY4 };
61246
+ var SINCE_MIN = Date.UTC(2e3, 0, 1);
61247
+ function parseSince(value, now = Date.now()) {
61248
+ const s = value?.trim();
61249
+ if (!s) return void 0;
61250
+ const bad = () => new Error(
61251
+ `invalid since "${value}": use an ISO date (2026-10-01) or a relative value like 30d, 12h, 2w`
61252
+ );
61253
+ let ms;
61254
+ const rel = /^(\d{1,6})([smhdw])$/i.exec(s);
61255
+ if (rel) ms = now - Number(rel[1]) * (UNITS[rel[2].toLowerCase()] ?? DAY4);
61256
+ else if (/^\d{4}-\d{2}-\d{2}(T.*)?$/.test(s)) ms = Date.parse(s);
61257
+ else throw bad();
61258
+ if (!Number.isFinite(ms) || ms < SINCE_MIN || ms > now + DAY4) throw bad();
61259
+ return ms;
61260
+ }
61261
+ var LIMIT_MAX = 1e3;
61262
+ var LIMIT_DEFAULT = 20;
61263
+ function parseLimit(value) {
61264
+ if (value === void 0) return void 0;
61265
+ const n = typeof value === "number" ? value : /^\d{1,6}$/.test(value.trim()) ? Number(value.trim()) : Number.NaN;
61266
+ if (!Number.isInteger(n) || n < 1 || n > LIMIT_MAX)
61267
+ return `limit must be a whole number between 1 and ${LIMIT_MAX}`;
61268
+ return n;
61269
+ }
61270
+ var clampLimit = (n) => n === void 0 || !Number.isFinite(n) ? LIMIT_DEFAULT : Math.min(LIMIT_MAX, Math.max(1, Math.floor(n)));
61271
+ var INGEST_GUARD_MS = 1e4;
61272
+ var lastIngest = /* @__PURE__ */ new Map();
61273
+ var sessionIngestKey = (root, sessionId) => `${root}\0${sessionId}`;
61274
+ function recentlyIngested(root, sessionId, now = Date.now()) {
61275
+ const within = (key) => {
61276
+ const t = lastIngest.get(key);
61277
+ return t !== void 0 && now - t < INGEST_GUARD_MS;
61278
+ };
61279
+ return within(root) || sessionId !== void 0 && within(sessionIngestKey(root, sessionId));
61280
+ }
61281
+ function ingestFor(db, ctx, scope, sessionId, now) {
61282
+ const overrides = ctx.config.usage.pricing;
61283
+ const project = scope === "project";
61284
+ if (recentlyIngested(ctx.root, project ? void 0 : sessionId, now)) return;
61285
+ try {
61286
+ if (project) ingestProject(db, ctx.root, { overrides });
61287
+ else ingestSession(db, ctx.root, sessionId, void 0, { overrides });
61288
+ } catch {
61289
+ } finally {
61290
+ lastIngest.set(project ? ctx.root : sessionIngestKey(ctx.root, sessionId), now);
61291
+ }
61292
+ }
61293
+ function loadUsage(ctx, opts = {}) {
61294
+ const db = getStorage(ctx);
61295
+ const scope = opts.scope ?? "project";
61296
+ const sessionId = opts.sessionId ?? getCurrentSession(ctx);
61297
+ const now = opts.now ?? Date.now();
61298
+ if (opts.ingest !== false) ingestFor(db, ctx, scope, sessionId, now);
61299
+ const filter = scope === "session" ? { sessionId, since: opts.since } : { since: opts.since, sessionId: void 0 };
61300
+ return {
61301
+ scope,
61302
+ sessionId,
61303
+ since: opts.since,
61304
+ now,
61305
+ calls: db.apiCalls(filter).filter(isRealCall),
61306
+ toolUses: db.toolUses(filter),
61307
+ contextEvents: db.contextEvents(scope === "session" ? { sessionId } : {}),
61308
+ stats: scope === "session" ? db.statsBySession(sessionId) : db.statsAll(),
61309
+ snapshot: db.statuslineLatest(scope === "session" ? sessionId : void 0)
61310
+ };
61311
+ }
61312
+ function modelSlices(rows) {
61313
+ return byModel(rows).map((g) => ({
61314
+ model: g.key,
61315
+ calls: g.totals.calls,
61316
+ costUsd: g.totals.costUsd,
61317
+ unpricedCalls: g.totals.unpricedCalls
61318
+ })).sort((a, b) => b.costUsd - a.costUsd || b.calls - a.calls);
61319
+ }
61320
+ function periods(calls, keyFn) {
61321
+ const buckets = /* @__PURE__ */ new Map();
61322
+ for (const c of calls) {
61323
+ const k = keyFn(c.ts);
61324
+ const list2 = buckets.get(k);
61325
+ if (list2) list2.push(c);
61326
+ else buckets.set(k, [c]);
61327
+ }
61328
+ return [...buckets.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, rows]) => ({ key, totals: totals(rows), models: modelSlices(rows) }));
61329
+ }
61330
+ function modelRows(calls) {
61331
+ const all = totals(calls);
61332
+ return byModel(calls).map((g) => ({
61333
+ model: g.key,
61334
+ calls: g.totals.calls,
61335
+ costUsd: g.totals.costUsd,
61336
+ unpricedCalls: g.totals.unpricedCalls,
61337
+ input: g.totals.input,
61338
+ output: g.totals.output,
61339
+ cacheRead: g.totals.cacheRead,
61340
+ cacheWrite: g.totals.cacheCreate5m + g.totals.cacheCreate1h,
61341
+ thinking: g.totals.thinking,
61342
+ share: all.costUsd > 0 ? g.totals.costUsd / all.costUsd : 0
61343
+ })).sort((a, b) => b.costUsd - a.costUsd || b.calls - a.calls);
61344
+ }
61345
+ function insightsFor(ctx, data) {
61346
+ const db = getStorage(ctx);
61347
+ const input2 = loadInsightInput(db, ctx, {
61348
+ ...data.scope === "session" ? { sessionId: data.sessionId } : {},
61349
+ ...data.since !== void 0 ? { since: data.since } : {},
61350
+ now: data.now
61351
+ });
61352
+ const r = detectInsights(input2);
61353
+ return { insights: r.insights, scores: r.scores };
61354
+ }
61355
+ var SEVERITY_RANK2 = { high: 0, warn: 1, info: 2 };
61356
+ function sortInsights(list2) {
61357
+ return list2.slice().sort(
61358
+ (a, b) => SEVERITY_RANK2[a.severity] - SEVERITY_RANK2[b.severity] || (b.estimatedSavingsUsd ?? 0) - (a.estimatedSavingsUsd ?? 0)
61359
+ );
61360
+ }
61361
+ function summaryPayload(ctx, data) {
61362
+ const { calls, now } = data;
61363
+ const today = dayKey(now);
61364
+ const blocks = billingBlocks(calls, now);
61365
+ const ins = insightsFor(ctx, data);
61366
+ return {
61367
+ scope: data.scope,
61368
+ sessionId: data.sessionId,
61369
+ since: data.since,
61370
+ now,
61371
+ totals: totals(calls),
61372
+ today: totals(calls.filter((c) => dayKey(c.ts) === today)),
61373
+ last7d: totals(calls.filter((c) => c.ts >= now - 7 * DAY4)),
61374
+ last30d: totals(calls.filter((c) => c.ts >= now - 30 * DAY4)),
61375
+ cache: cacheStats(calls),
61376
+ models: modelRows(calls),
61377
+ burn: burnRate(calls, now),
61378
+ block: activeBlock(blocks, now) ?? null,
61379
+ insights: sortInsights(ins.insights).slice(0, 3),
61380
+ scores: ins.scores,
61381
+ savings: savingsFor(ctx, data),
61382
+ snapshot: data.snapshot ?? null
61383
+ };
61384
+ }
61385
+ function savingsFor(ctx, data) {
61386
+ return pricedSavings(data.stats, data.calls, ctx.config.usage.pricing, { since: data.since });
61387
+ }
61388
+ function sessionsPayload(ctx, data, limit) {
61389
+ const db = getStorage(ctx);
61390
+ const span = /* @__PURE__ */ new Map();
61391
+ for (const c of data.calls) {
61392
+ const s = span.get(c.session_id);
61393
+ if (!s) span.set(c.session_id, { first: c.ts, last: c.ts, branch: c.git_branch });
61394
+ else {
61395
+ s.first = Math.min(s.first, c.ts);
61396
+ if (c.ts >= s.last) {
61397
+ s.last = c.ts;
61398
+ s.branch = c.git_branch ?? s.branch;
61399
+ }
61400
+ }
61401
+ }
61402
+ const rows = bySession(data.calls).map((g) => {
61403
+ const s = span.get(g.key);
61404
+ const row = db.sessionGet(g.key);
61405
+ return {
61406
+ session: g.key,
61407
+ startedAt: row?.started_at ?? null,
61408
+ firstCall: s?.first ?? 0,
61409
+ lastCall: s?.last ?? 0,
61410
+ branch: s?.branch ?? null,
61411
+ lastPrompt: row?.last_prompt ?? null,
61412
+ totals: g.totals
61413
+ };
61414
+ }).sort((a, b) => b.lastCall - a.lastCall);
61415
+ return { rows: rows.slice(0, limit) };
61416
+ }
61417
+ function turnsPayload(ctx, data, limit) {
61418
+ const db = getStorage(ctx);
61419
+ const sessionId = data.sessionId;
61420
+ const calls = data.scope === "session" ? data.calls : data.calls.filter((c) => c.session_id === sessionId);
61421
+ const events = data.scope === "session" ? data.contextEvents : db.contextEvents({ sessionId });
61422
+ const timeline = contextTimeline(calls, events);
61423
+ return {
61424
+ sessionId,
61425
+ timeline: timeline.length > limit ? timeline.slice(-limit) : timeline,
61426
+ compactions: events.length,
61427
+ totals: totals(calls)
61428
+ };
61429
+ }
61430
+ function usageView(ctx, view, data, limitArg = LIMIT_DEFAULT) {
61431
+ const { calls, now } = data;
61432
+ const limit = clampLimit(limitArg);
61433
+ switch (view) {
61434
+ case "summary":
61435
+ return summaryPayload(ctx, data);
61436
+ case "daily":
61437
+ return { rows: periods(calls, (ts) => dayKey(ts)), totals: totals(calls) };
61438
+ case "weekly":
61439
+ return { rows: periods(calls, (ts) => weekKey(ts)), totals: totals(calls) };
61440
+ case "monthly":
61441
+ return { rows: periods(calls, (ts) => monthKey(ts)), totals: totals(calls) };
61442
+ case "models":
61443
+ return {
61444
+ rows: modelRows(calls),
61445
+ byEffort: byEffort(calls),
61446
+ bySpeed: bySpeed(calls),
61447
+ agents: byAgent(calls)
61448
+ };
61449
+ case "sessions":
61450
+ return sessionsPayload(ctx, data, limit);
61451
+ case "blocks": {
61452
+ const blocks = billingBlocks(calls, now).reverse();
61453
+ return { rows: blocks.slice(0, limit), burn: burnRate(calls, now) };
61454
+ }
61455
+ case "turns":
61456
+ return turnsPayload(ctx, data, limit);
61457
+ case "tools": {
61458
+ const rows = topToolConsumers(data.toolUses, limit);
61459
+ return {
61460
+ rows,
61461
+ totalChars: data.toolUses.reduce((n, u) => n + (u.result_chars ?? 0), 0)
61462
+ };
61463
+ }
61464
+ case "insights": {
61465
+ const r = insightsFor(ctx, data);
61466
+ return { insights: sortInsights(r.insights), scores: r.scores };
61467
+ }
61468
+ case "savings":
61469
+ return savingsFor(ctx, data);
61470
+ }
61471
+ }
61472
+ function textTable(headers, rows, aligns) {
61473
+ const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length)));
61474
+ const line = (cells) => ` ${cells.map((c, i) => {
61475
+ const w = widths[i] ?? c.length;
61476
+ return (aligns?.[i] ?? (i === 0 ? "l" : "r")) === "l" ? c.padEnd(w) : c.padStart(w);
61477
+ }).join(" ").trimEnd()}`;
61478
+ return [line(headers), ...rows.map(line)].join("\n");
61479
+ }
61480
+ var usd2 = (n) => formatUsd(n);
61481
+ var pct2 = (x) => `${Math.round(x * 100)}%`;
61482
+ var shortId = (id) => id.slice(0, 8);
61483
+ var hhmm = (ts) => new Date(ts).toISOString().slice(11, 19);
61484
+ var dateTime = (ts) => new Date(ts).toISOString().slice(0, 16).replace("T", " ");
61485
+ var shortModel = (m) => m.replace(/^claude-/, "").replace(/-\d{8}(\[1m\])?$/, "$1");
61486
+ function fmtGap(ms) {
61487
+ if (!(ms >= 36e5)) return "<1h";
61488
+ const h = Math.floor(ms / 36e5);
61489
+ const m = Math.floor(ms % 36e5 / 6e4);
61490
+ return `${h}:${String(m).padStart(2, "0")}`;
61491
+ }
61492
+ var burnLine = (b) => b.reliable ? `${usd2(b.usdPerHour)}/h \xB7 ${fmtTokens(Math.round(b.tokensPerMin))} tok/min \xB7 projected ${usd2(b.projectedBlockUsd ?? 0)}` : `burn n/a (needs ${BURN_MIN_CALLS}+ calls over ${BURN_MIN_MINUTES}+ min)`;
61493
+ var EST_NOTE = "est. at list price";
61494
+ function totalsLine(t) {
61495
+ const unpriced = t.unpricedCalls ? ` \xB7 ${t.unpricedCalls} unpriced` : "";
61496
+ return `${t.calls} calls \xB7 ${usd2(t.costUsd)}${unpriced} \xB7 in ${fmtTokens(t.input)} \xB7 out ${fmtTokens(t.output)} \xB7 cache read ${fmtTokens(t.cacheRead)} \xB7 cache write ${fmtTokens(t.cacheCreate5m + t.cacheCreate1h)} \xB7 ${pct2(t.cacheHitRate)} cache hits`;
61497
+ }
61498
+ function scopeTitle(p) {
61499
+ const s = p.scope === "session" ? `session ${shortId(p.sessionId)}` : "project";
61500
+ return p.since ? `${s} since ${dateTime(p.since)} UTC` : s;
61501
+ }
61502
+ function renderSummary(p) {
61503
+ const out = [`usage (${scopeTitle(p)}, ${EST_NOTE}): ${totalsLine(p.totals)}`];
61504
+ out.push(
61505
+ ` cost: today ${usd2(p.today.costUsd)} (${p.today.calls} calls) \xB7 7d ${usd2(p.last7d.costUsd)} \xB7 30d ${usd2(p.last30d.costUsd)}`
61506
+ );
61507
+ out.push(
61508
+ ` cache: ${pct2(p.cache.hitRate)} hit rate \xB7 ${fmtTokens(p.cache.cacheReads)} read \xB7 ${fmtTokens(p.cache.cacheWrites)} written \xB7 ${p.cache.coldCalls} cold call(s)${p.cache.share1h > 0 ? ` \xB7 ${pct2(p.cache.share1h)} 1h writes` : ""}`
61509
+ );
61510
+ if (p.models.length)
61511
+ out.push(
61512
+ ` models: ${p.models.map(
61513
+ (m) => `${shortModel(m.model)} ${m.calls} calls ${usd2(m.costUsd)}${m.unpricedCalls ? ` (${m.unpricedCalls} unpriced)` : ""}`
61514
+ ).join(" \xB7 ")}`
61515
+ );
61516
+ if (p.block)
61517
+ out.push(
61518
+ ` active block: started ${hhmm(p.block.start)} UTC \xB7 ${p.block.calls} calls \xB7 ${usd2(p.block.costUsd)} \xB7 ${burnLine(p.burn)}`
61519
+ );
61520
+ else out.push(" active block: none (no calls in the current 5-hour window)");
61521
+ if (p.snapshot && (p.snapshot.five_hour_pct != null || p.snapshot.seven_day_pct != null)) {
61522
+ const plan = [
61523
+ p.snapshot.five_hour_pct != null ? `5h ${p.snapshot.five_hour_pct}%` : "",
61524
+ p.snapshot.seven_day_pct != null ? `7d ${p.snapshot.seven_day_pct}%` : "",
61525
+ p.snapshot.ctx_used_pct != null ? `ctx ${p.snapshot.ctx_used_pct}%` : ""
61526
+ ].filter(Boolean);
61527
+ out.push(` plan: ${plan.join(" \xB7 ")}`);
61528
+ }
61529
+ if (p.insights.length) {
61530
+ out.push(" insights:");
61531
+ for (const i of p.insights) out.push(` [${i.severity}] ${i.title}: ${i.reason}`);
61532
+ } else out.push(" insights: none");
61533
+ out.push(
61534
+ ` eminiar savings: ${fmtTokens(p.savings.savedTokens)} tokens kept out of context \u2248 ${usd2(p.savings.directUsd)} direct + ${usd2(p.savings.compoundingUsd)} compounding (see em_usage view:"savings")`
61535
+ );
61536
+ if (p.totals.unpricedCalls)
61537
+ out.push(
61538
+ ` note: ${p.totals.unpricedCalls} call(s) use a model without a bundled price; add config.usage.pricing to price them`
61539
+ );
61540
+ return out.join("\n");
61541
+ }
61542
+ function renderPeriods(label, p) {
61543
+ if (!p.rows.length) return `${label}: no API calls recorded`;
61544
+ const rows = p.rows.map((r) => [
61545
+ r.key,
61546
+ String(r.totals.calls),
61547
+ usd2(r.totals.costUsd),
61548
+ fmtTokens(r.totals.input),
61549
+ fmtTokens(r.totals.output),
61550
+ fmtTokens(r.totals.cacheRead),
61551
+ fmtTokens(r.totals.cacheCreate5m + r.totals.cacheCreate1h),
61552
+ pct2(r.totals.cacheHitRate),
61553
+ r.models.map((m) => `${shortModel(m.model)} ${usd2(m.costUsd)}`).join(", ") + (r.totals.unpricedCalls ? ` (${r.totals.unpricedCalls} unpriced)` : "")
61554
+ ]);
61555
+ return `${label}: ${totalsLine(p.totals)}
61556
+ ${textTable(
61557
+ ["period", "calls", "cost", "in", "out", "cache rd", "cache wr", "hits", "models"],
61558
+ rows,
61559
+ ["l", "r", "r", "r", "r", "r", "r", "r", "l"]
61560
+ )}
61561
+ periods are local calendar days; cost ${EST_NOTE}`;
61562
+ }
61563
+ function renderModels(p) {
61564
+ if (!p.rows.length) return "models: no API calls recorded";
61565
+ const out = [
61566
+ "models:",
61567
+ textTable(
61568
+ ["model", "calls", "cost", "share", "in", "out", "cache rd", "cache wr", "thinking"],
61569
+ p.rows.map((m) => [
61570
+ shortModel(m.model) + (m.unpricedCalls ? " (unpriced)" : ""),
61571
+ String(m.calls),
61572
+ usd2(m.costUsd),
61573
+ pct2(m.share),
61574
+ fmtTokens(m.input),
61575
+ fmtTokens(m.output),
61576
+ fmtTokens(m.cacheRead),
61577
+ fmtTokens(m.cacheWrite),
61578
+ fmtTokens(m.thinking)
61579
+ ])
61580
+ )
61581
+ ];
61582
+ const line = (title, groups) => ` ${title}: ${groups.map((g) => `${g.key} ${g.totals.calls} calls ${usd2(g.totals.costUsd)}`).join(" \xB7 ")}`;
61583
+ if (p.byEffort.length > 1) out.push(line("effort", p.byEffort));
61584
+ if (p.bySpeed.some((g) => g.key === "fast")) out.push(line("speed", p.bySpeed));
61585
+ if (p.agents.subagents.length)
61586
+ out.push(
61587
+ ` subagents: ${p.agents.subagents.length} \xB7 ${usd2(p.agents.subagents.reduce((s, a) => s + a.totals.costUsd, 0))} (${pct2(p.agents.delegationShare)} of cost)`
61588
+ );
61589
+ out.push(` cost ${EST_NOTE}`);
61590
+ return out.join("\n");
61591
+ }
61592
+ function renderSessions(p) {
61593
+ if (!p.rows.length) return "sessions: no API calls recorded";
61594
+ return `sessions:
61595
+ ${textTable(
61596
+ ["session", "last call (UTC)", "branch", "calls", "cost", "in", "out", "hits", "prompt"],
61597
+ p.rows.map((s) => [
61598
+ shortId(s.session),
61599
+ dateTime(s.lastCall),
61600
+ s.branch ?? "",
61601
+ String(s.totals.calls),
61602
+ usd2(s.totals.costUsd),
61603
+ fmtTokens(s.totals.input),
61604
+ fmtTokens(s.totals.output),
61605
+ pct2(s.totals.cacheHitRate),
61606
+ (s.lastPrompt ?? "").replace(/\s+/g, " ").slice(0, 50)
61607
+ ]),
61608
+ ["l", "l", "l", "r", "r", "r", "r", "r", "l"]
61609
+ )}`;
61610
+ }
61611
+ function renderBlocks(p) {
61612
+ if (!p.rows.length) return "blocks: no API calls recorded";
61613
+ const rows = p.rows.map((b) => [
61614
+ `${dateTime(b.start)}${b.active ? " *" : ""}`,
61615
+ `${hhmm(b.firstCall)}\u2013${hhmm(b.lastCall)}`,
61616
+ String(b.calls),
61617
+ usd2(b.costUsd),
61618
+ fmtTokens(b.tokens.input + b.tokens.output + b.tokens.cacheCreate + b.tokens.cacheRead),
61619
+ b.gapBefore != null ? fmtGap(b.gapBefore) : "",
61620
+ b.models.map(shortModel).join(", ") + (b.unpricedCalls ? ` (${b.unpricedCalls} unpriced)` : "")
61621
+ ]);
61622
+ const out = [
61623
+ "5-hour billing blocks (times in UTC, * = active):",
61624
+ textTable(["block (UTC)", "calls at (UTC)", "calls", "cost", "tokens", "gap", "models"], rows, [
61625
+ "l",
61626
+ "l",
61627
+ "r",
61628
+ "r",
61629
+ "r",
61630
+ "r",
61631
+ "l"
61632
+ ])
61633
+ ];
61634
+ if (p.rows.some((b) => b.active))
61635
+ out.push(
61636
+ p.burn.reliable ? ` burn: ${usd2(p.burn.usdPerHour)}/h \xB7 ${fmtTokens(Math.round(p.burn.tokensPerMin))} tok/min over ${Math.round(p.burn.windowMinutes)} min \xB7 projected ${usd2(p.burn.projectedBlockUsd ?? 0)} for the block` : ` ${burnLine(p.burn)}`
61637
+ );
61638
+ return out.join("\n");
61639
+ }
61640
+ function renderTurns(p) {
61641
+ if (!p.timeline.length) return `turns (session ${shortId(p.sessionId)}): no API calls recorded`;
61642
+ const rows = p.timeline.map((t) => [
61643
+ hhmm(t.ts),
61644
+ shortModel(t.model),
61645
+ fmtTokens(t.contextTokens),
61646
+ fmtTokens(t.output),
61647
+ usd2(t.costUsd),
61648
+ t.compactBefore ? "compacted before this call" : ""
61649
+ ]);
61650
+ const shown = p.timeline.length;
61651
+ const count = shown < p.totals.calls ? `last ${shown} of ${p.totals.calls} calls` : `${shown} calls`;
61652
+ return `turns (session ${shortId(p.sessionId)}): ${count} \xB7 ${p.compactions} compaction(s) \xB7 ${usd2(p.totals.costUsd)}
61653
+ ${textTable(
61654
+ ["time (UTC)", "model", "context", "output", "cost", ""],
61655
+ rows,
61656
+ ["l", "l", "r", "r", "r", "l"]
61657
+ )}`;
61658
+ }
61659
+ function renderTools(p) {
61660
+ if (!p.rows.length) return "tools: no tool results recorded";
61661
+ return `tool results in context (${fmtTokens(Math.ceil(p.totalChars / 4))} est. tokens total):
61662
+ ${textTable(
61663
+ ["tool", "calls", "chars", "est tokens", "avg chars", "errors"],
61664
+ p.rows.map((r) => [
61665
+ r.tool,
61666
+ String(r.calls),
61667
+ fmtTokens(r.chars),
61668
+ fmtTokens(r.estTokens),
61669
+ fmtTokens(r.avgChars),
61670
+ String(r.errors)
61671
+ ])
61672
+ )}`;
61673
+ }
61674
+ function renderInsights(p) {
61675
+ const out = [];
61676
+ if (p.insights.length) {
61677
+ out.push(`insights (${p.insights.length}):`);
61678
+ for (const i of p.insights) {
61679
+ out.push(` [${i.severity}] ${i.title} (${i.id}, ${i.scope})`);
61680
+ out.push(` ${i.reason}`);
61681
+ if (i.recommendation) out.push(` \u2192 ${i.recommendation}`);
61682
+ if (i.estimatedSavingsUsd) out.push(` est. savings ${usd2(i.estimatedSavingsUsd)}`);
61683
+ }
61684
+ } else out.push("insights: nothing to flag");
61685
+ if (p.scores.length) {
61686
+ out.push("scores (0\u2013100, higher is better):");
61687
+ for (const s of p.scores) {
61688
+ out.push(` ${s.id.padEnd(16)} ${String(Math.round(s.value)).padStart(3)}`);
61689
+ for (const r of s.reasons) out.push(` - ${r}`);
61690
+ }
61691
+ }
61692
+ return out.join("\n");
61693
+ }
61694
+ function renderSavings(p) {
61695
+ const out = [
61696
+ `eminiar savings: ${fmtTokens(p.savedTokens)} tokens kept out of context`,
61697
+ ` direct ${usd2(p.directUsd)} (never written to cache: cache-write price of ${shortModel(p.model)})`,
61698
+ ` compounding ${usd2(p.compoundingUsd)} (never re-read on later turns: cache-read price \xD7 ${p.laterCallsPerSession.toFixed(1)} later calls per session)`,
61699
+ " assumptions:",
61700
+ ...p.assumptions.map((a) => ` - ${a}`)
61701
+ ];
61702
+ return out.join("\n");
61703
+ }
61704
+ function renderUsage(view, payload) {
61705
+ switch (view) {
61706
+ case "summary":
61707
+ return renderSummary(payload);
61708
+ case "daily":
61709
+ return renderPeriods("daily", payload);
61710
+ case "weekly":
61711
+ return renderPeriods("weekly (Monday)", payload);
61712
+ case "monthly":
61713
+ return renderPeriods("monthly", payload);
61714
+ case "models":
61715
+ return renderModels(payload);
61716
+ case "sessions":
61717
+ return renderSessions(payload);
61718
+ case "blocks":
61719
+ return renderBlocks(payload);
61720
+ case "turns":
61721
+ return renderTurns(payload);
61722
+ case "tools":
61723
+ return renderTools(payload);
61724
+ case "insights":
61725
+ return renderInsights(payload);
61726
+ case "savings":
61727
+ return renderSavings(payload);
61728
+ }
61729
+ }
61730
+ var emUsage = defineTool({
61731
+ name: "em_usage",
61732
+ description: "Token, cache and cost insights for Claude Code sessions, from the transcripts on this machine: cost today / 7d / 30d, cache hit rate, per-model and per-session breakdowns, 5-hour billing blocks with burn rate, the context timeline of a session with compactions, which tools put the most into context, detected insights with scores, and what eminiar's own tools saved. Cost is an estimate at list prices; everything stays local.",
61733
+ schema: {
61734
+ scope: external_exports.enum(["session", "project"]).optional().describe("default project"),
61735
+ view: external_exports.enum(USAGE_VIEWS).optional().describe("default summary"),
61736
+ since: external_exports.string().optional().describe("ISO date/time or relative: 30d, 12h, 2w (default: everything)"),
61737
+ limit: external_exports.number().int().positive().max(LIMIT_MAX).optional().describe(`rows for list views (${LIMIT_DEFAULT})`),
61738
+ json: external_exports.boolean().optional().describe("return the aggregate object instead of text")
61739
+ },
61740
+ handler: async (args, ctx) => {
61741
+ if (!usageEnabled(ctx))
61742
+ return text("usage insights are off (config.usage.enabled = false); nothing is ingested");
61743
+ const view = args.view ?? "summary";
61744
+ let since;
61745
+ try {
61746
+ since = parseSince(args.since);
61747
+ } catch (err) {
61748
+ return errorResult(err.message);
61749
+ }
61750
+ const limit = parseLimit(args.limit);
61751
+ if (typeof limit === "string") return errorResult(limit);
61752
+ const data = loadUsage(ctx, { scope: args.scope ?? "project", since });
61753
+ const payload = usageView(ctx, view, data, limit ?? LIMIT_DEFAULT);
61754
+ if (args.json) return text(JSON.stringify({ view, scope: data.scope, ...payload }));
61755
+ return text(renderUsage(view, payload));
61756
+ }
61757
+ });
61758
+
58863
61759
  // src/tools/index.ts
58864
61760
  var tools = [
58865
61761
  emExecute,
@@ -58882,6 +61778,7 @@ var tools = [
58882
61778
  emReindex,
58883
61779
  emIndexStatus,
58884
61780
  emStats,
61781
+ emUsage,
58885
61782
  emPurge,
58886
61783
  emDoctor
58887
61784
  ];