modelstat 0.7.1 → 0.8.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/cli.mjs CHANGED
@@ -85,10 +85,15 @@ function parseRemote(url) {
85
85
  return { host: null, slug: null };
86
86
  }
87
87
  }
88
+ function mainRepoPath(cwd) {
89
+ const i = cwd.indexOf("/.claude/");
90
+ return i === -1 ? cwd : cwd.slice(0, i);
91
+ }
88
92
  async function resolveGitContext(cwd) {
89
93
  if (!cwd) return null;
90
- if (cache.has(cwd)) return cache.get(cwd) ?? null;
91
- const root = findRepoRoot(cwd);
94
+ const target = mainRepoPath(cwd);
95
+ if (cache.has(target)) return cache.get(target) ?? null;
96
+ const root = findRepoRoot(target);
92
97
  if (!root) {
93
98
  const empty = {
94
99
  remote_url: null,
@@ -97,7 +102,7 @@ async function resolveGitContext(cwd) {
97
102
  branch: null,
98
103
  commit_sha: null
99
104
  };
100
- cache.set(cwd, empty);
105
+ cache.set(target, empty);
101
106
  return empty;
102
107
  }
103
108
  const ran = async (args) => {
@@ -119,7 +124,7 @@ async function resolveGitContext(cwd) {
119
124
  branch,
120
125
  commit_sha: sha
121
126
  };
122
- cache.set(cwd, ctx);
127
+ cache.set(target, ctx);
123
128
  return ctx;
124
129
  }
125
130
  function guessRepoSlugFromPath(cwd) {
@@ -16734,7 +16739,7 @@ var require_client_h2 = __commonJS({
16734
16739
  "../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/dispatcher/client-h2.js"(exports, module) {
16735
16740
  "use strict";
16736
16741
  var assert = __require("assert");
16737
- var { pipeline } = __require("stream");
16742
+ var { pipeline: pipeline2 } = __require("stream");
16738
16743
  var util2 = require_util();
16739
16744
  var {
16740
16745
  RequestContentLengthMismatchError,
@@ -17367,7 +17372,7 @@ var require_client_h2 = __commonJS({
17367
17372
  }
17368
17373
  function writeStream(abort, socket, expectsPayload, h2stream, body, client, request3, contentLength) {
17369
17374
  assert(contentLength !== 0 || client[kRunning] === 0, "stream body cannot be pipelined");
17370
- const pipe = pipeline(
17375
+ const pipe = pipeline2(
17371
17376
  body,
17372
17377
  h2stream,
17373
17378
  (err) => {
@@ -18074,10 +18079,10 @@ var require_pool_base = __commonJS({
18074
18079
  [kClients] = [];
18075
18080
  [kNeedDrain] = false;
18076
18081
  [kOnDrain](client, origin, targets) {
18077
- const queue = this[kQueue];
18082
+ const queue2 = this[kQueue];
18078
18083
  let needDrain = false;
18079
18084
  while (!needDrain) {
18080
- const item = queue.shift();
18085
+ const item = queue2.shift();
18081
18086
  if (!item) {
18082
18087
  break;
18083
18088
  }
@@ -18089,7 +18094,7 @@ var require_pool_base = __commonJS({
18089
18094
  this[kNeedDrain] = false;
18090
18095
  this.emit("drain", origin, [this, ...targets]);
18091
18096
  }
18092
- if (this[kClosedResolve] && queue.isEmpty()) {
18097
+ if (this[kClosedResolve] && queue2.isEmpty()) {
18093
18098
  const closeAll = [];
18094
18099
  for (let i = 0; i < this[kClients].length; i++) {
18095
18100
  const client2 = this[kClients][i];
@@ -21133,7 +21138,7 @@ var require_api_pipeline = __commonJS({
21133
21138
  util2.destroy(ret, err);
21134
21139
  }
21135
21140
  };
21136
- function pipeline(opts, handler) {
21141
+ function pipeline2(opts, handler) {
21137
21142
  try {
21138
21143
  const pipelineHandler = new PipelineHandler(opts, handler);
21139
21144
  this.dispatch({ ...opts, body: pipelineHandler.req }, pipelineHandler);
@@ -21142,7 +21147,7 @@ var require_api_pipeline = __commonJS({
21142
21147
  return new PassThrough().destroy(err);
21143
21148
  }
21144
21149
  }
21145
- module.exports = pipeline;
21150
+ module.exports = pipeline2;
21146
21151
  }
21147
21152
  });
21148
21153
 
@@ -22014,13 +22019,13 @@ var require_mock_call_history = __commonJS({
22014
22019
  "use strict";
22015
22020
  var { kMockCallHistoryAddLog } = require_mock_symbols();
22016
22021
  var { InvalidArgumentError } = require_errors();
22017
- function handleFilterCallsWithOptions(criteria, options, handler, store) {
22022
+ function handleFilterCallsWithOptions(criteria, options, handler, store2) {
22018
22023
  switch (options.operator) {
22019
22024
  case "OR":
22020
- store.push(...handler(criteria));
22021
- return store;
22025
+ store2.push(...handler(criteria));
22026
+ return store2;
22022
22027
  case "AND":
22023
- return handler.call({ logs: store }, criteria);
22028
+ return handler.call({ logs: store2 }, criteria);
22024
22029
  default:
22025
22030
  throw new InvalidArgumentError("options.operator must to be a case insensitive string equal to 'OR' or 'AND'");
22026
22031
  }
@@ -24377,12 +24382,12 @@ var require_cache = __commonJS({
24377
24382
  }
24378
24383
  return false;
24379
24384
  }
24380
- function assertCacheStore(store, name = "CacheStore") {
24381
- if (typeof store !== "object" || store === null) {
24382
- throw new TypeError(`expected type of ${name} to be a CacheStore, got ${store === null ? "null" : typeof store}`);
24385
+ function assertCacheStore(store2, name = "CacheStore") {
24386
+ if (typeof store2 !== "object" || store2 === null) {
24387
+ throw new TypeError(`expected type of ${name} to be a CacheStore, got ${store2 === null ? "null" : typeof store2}`);
24383
24388
  }
24384
24389
  for (const fn of ["get", "createWriteStream", "delete"]) {
24385
- if (typeof store[fn] !== "function") {
24390
+ if (typeof store2[fn] !== "function") {
24386
24391
  throw new TypeError(`${name} needs to have a \`${fn}()\` function`);
24387
24392
  }
24388
24393
  }
@@ -24986,8 +24991,8 @@ var require_cache_handler = __commonJS({
24986
24991
  * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey
24987
24992
  * @param {import('../../types/dispatcher.d.ts').default.DispatchHandler} handler
24988
24993
  */
24989
- constructor({ store, type, cacheByDefault }, cacheKey, handler) {
24990
- this.#store = store;
24994
+ constructor({ store: store2, type, cacheByDefault }, cacheKey, handler) {
24995
+ this.#store = store2;
24991
24996
  this.#cacheType = type;
24992
24997
  this.#cacheByDefault = cacheByDefault;
24993
24998
  this.#cacheKey = cacheKey;
@@ -25396,7 +25401,7 @@ var require_memory_cache_store = __commonJS({
25396
25401
  assertCacheKey(key);
25397
25402
  assertCacheValue(val);
25398
25403
  const topLevelKey = `${key.origin}:${key.path}`;
25399
- const store = this;
25404
+ const store2 = this;
25400
25405
  const entry = { ...key, ...val, body: [], size: 0 };
25401
25406
  return new Writable({
25402
25407
  write(chunk, encoding, callback) {
@@ -25404,7 +25409,7 @@ var require_memory_cache_store = __commonJS({
25404
25409
  chunk = Buffer.from(chunk, encoding);
25405
25410
  }
25406
25411
  entry.size += chunk.byteLength;
25407
- if (entry.size >= store.#maxEntrySize) {
25412
+ if (entry.size >= store2.#maxEntrySize) {
25408
25413
  this.destroy();
25409
25414
  } else {
25410
25415
  entry.body.push(chunk);
@@ -25412,42 +25417,42 @@ var require_memory_cache_store = __commonJS({
25412
25417
  callback(null);
25413
25418
  },
25414
25419
  final(callback) {
25415
- let entries = store.#entries.get(topLevelKey);
25420
+ let entries = store2.#entries.get(topLevelKey);
25416
25421
  if (!entries) {
25417
25422
  entries = [];
25418
- store.#entries.set(topLevelKey, entries);
25423
+ store2.#entries.set(topLevelKey, entries);
25419
25424
  }
25420
25425
  const previousEntry = findEntry(key, entries, Date.now());
25421
25426
  if (previousEntry) {
25422
25427
  const index = entries.indexOf(previousEntry);
25423
25428
  entries.splice(index, 1, entry);
25424
- store.#size -= previousEntry.size;
25429
+ store2.#size -= previousEntry.size;
25425
25430
  } else {
25426
25431
  entries.push(entry);
25427
- store.#count += 1;
25428
- }
25429
- store.#size += entry.size;
25430
- if (store.#size > store.#maxSize || store.#count > store.#maxCount) {
25431
- if (!store.#hasEmittedMaxSizeEvent) {
25432
- store.emit("maxSizeExceeded", {
25433
- size: store.#size,
25434
- maxSize: store.#maxSize,
25435
- count: store.#count,
25436
- maxCount: store.#maxCount
25432
+ store2.#count += 1;
25433
+ }
25434
+ store2.#size += entry.size;
25435
+ if (store2.#size > store2.#maxSize || store2.#count > store2.#maxCount) {
25436
+ if (!store2.#hasEmittedMaxSizeEvent) {
25437
+ store2.emit("maxSizeExceeded", {
25438
+ size: store2.#size,
25439
+ maxSize: store2.#maxSize,
25440
+ count: store2.#count,
25441
+ maxCount: store2.#maxCount
25437
25442
  });
25438
- store.#hasEmittedMaxSizeEvent = true;
25443
+ store2.#hasEmittedMaxSizeEvent = true;
25439
25444
  }
25440
- for (const [key2, entries2] of store.#entries) {
25445
+ for (const [key2, entries2] of store2.#entries) {
25441
25446
  for (const entry2 of entries2.splice(0, entries2.length / 2)) {
25442
- store.#size -= entry2.size;
25443
- store.#count -= 1;
25447
+ store2.#size -= entry2.size;
25448
+ store2.#count -= 1;
25444
25449
  }
25445
25450
  if (entries2.length === 0) {
25446
- store.#entries.delete(key2);
25451
+ store2.#entries.delete(key2);
25447
25452
  }
25448
25453
  }
25449
- if (store.#size < store.#maxSize && store.#count < store.#maxCount) {
25450
- store.#hasEmittedMaxSizeEvent = false;
25454
+ if (store2.#size < store2.#maxSize && store2.#count < store2.#maxCount) {
25455
+ store2.#hasEmittedMaxSizeEvent = false;
25451
25456
  }
25452
25457
  }
25453
25458
  callback(null);
@@ -25818,7 +25823,7 @@ var require_cache2 = __commonJS({
25818
25823
  }
25819
25824
  module.exports = (opts = {}) => {
25820
25825
  const {
25821
- store = new MemoryCacheStore(),
25826
+ store: store2 = new MemoryCacheStore(),
25822
25827
  methods = ["GET"],
25823
25828
  cacheByDefault = void 0,
25824
25829
  type = "shared",
@@ -25827,7 +25832,7 @@ var require_cache2 = __commonJS({
25827
25832
  if (typeof opts !== "object" || opts === null) {
25828
25833
  throw new TypeError(`expected type of opts to be an Object, got ${opts === null ? "null" : typeof opts}`);
25829
25834
  }
25830
- assertCacheStore(store, "opts.store");
25835
+ assertCacheStore(store2, "opts.store");
25831
25836
  assertCacheMethods(methods, "opts.methods");
25832
25837
  assertCacheOrigins(origins, "opts.origins");
25833
25838
  if (typeof cacheByDefault !== "undefined" && typeof cacheByDefault !== "number") {
@@ -25837,7 +25842,7 @@ var require_cache2 = __commonJS({
25837
25842
  throw new TypeError(`expected opts.type to be shared, private, or undefined, got ${typeof type}`);
25838
25843
  }
25839
25844
  const globalOpts = {
25840
- store,
25845
+ store: store2,
25841
25846
  methods,
25842
25847
  cacheByDefault,
25843
25848
  type
@@ -25876,7 +25881,7 @@ var require_cache2 = __commonJS({
25876
25881
  return dispatch(opts2, handler);
25877
25882
  }
25878
25883
  const cacheKey = makeCacheKey(opts2);
25879
- const result2 = store.get(cacheKey);
25884
+ const result2 = store2.get(cacheKey);
25880
25885
  if (result2 && typeof result2.then === "function") {
25881
25886
  return result2.then((result3) => handleResult2(
25882
25887
  dispatch,
@@ -25909,7 +25914,7 @@ var require_decompress = __commonJS({
25909
25914
  "../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/interceptor/decompress.js"(exports, module) {
25910
25915
  "use strict";
25911
25916
  var { createInflate, createGunzip, createBrotliDecompress, createZstdDecompress } = __require("zlib");
25912
- var { pipeline } = __require("stream");
25917
+ var { pipeline: pipeline2 } = __require("stream");
25913
25918
  var DecoratorHandler = require_decorator_handler();
25914
25919
  var { runtimeFeatures } = require_runtime_features();
25915
25920
  var supportedEncodings = {
@@ -26018,7 +26023,7 @@ var require_decompress = __commonJS({
26018
26023
  #setupMultipleDecompressors(controller) {
26019
26024
  const lastDecompressor = this.#decompressors[this.#decompressors.length - 1];
26020
26025
  this.#setupDecompressorEvents(lastDecompressor, controller);
26021
- pipeline(this.#decompressors, (err) => {
26026
+ pipeline2(this.#decompressors, (err) => {
26022
26027
  if (err) {
26023
26028
  super.onResponseError(controller, err);
26024
26029
  return;
@@ -26809,12 +26814,12 @@ var require_sqlite_cache_store = __commonJS({
26809
26814
  assertCacheValue(value);
26810
26815
  let size = 0;
26811
26816
  const body = [];
26812
- const store = this;
26817
+ const store2 = this;
26813
26818
  return new Writable({
26814
26819
  decodeStrings: true,
26815
26820
  write(chunk, encoding, callback) {
26816
26821
  size += chunk.byteLength;
26817
- if (size < store.#maxEntrySize) {
26822
+ if (size < store2.#maxEntrySize) {
26818
26823
  body.push(chunk);
26819
26824
  } else {
26820
26825
  this.destroy();
@@ -26822,7 +26827,7 @@ var require_sqlite_cache_store = __commonJS({
26822
26827
  callback();
26823
26828
  },
26824
26829
  final(callback) {
26825
- store.set(key, { ...value, body });
26830
+ store2.set(key, { ...value, body });
26826
26831
  callback();
26827
26832
  }
26828
26833
  });
@@ -28758,7 +28763,7 @@ var require_fetch = __commonJS({
28758
28763
  subresourceSet
28759
28764
  } = require_constants3();
28760
28765
  var EE = __require("events");
28761
- var { Readable: Readable2, pipeline, finished, isErrored, isReadable } = __require("stream");
28766
+ var { Readable: Readable2, pipeline: pipeline2, finished, isErrored, isReadable } = __require("stream");
28762
28767
  var { addAbortListener, bufferToLowerCasedHeaderName } = require_util();
28763
28768
  var { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = require_data_url();
28764
28769
  var { getGlobalDispatcher } = require_global2();
@@ -29749,7 +29754,7 @@ var require_fetch = __commonJS({
29749
29754
  status: status2,
29750
29755
  statusText,
29751
29756
  headersList,
29752
- body: decoders.length ? pipeline(this.body, ...decoders, (err) => {
29757
+ body: decoders.length ? pipeline2(this.body, ...decoders, (err) => {
29753
29758
  if (err) {
29754
29759
  this.onError(err);
29755
29760
  }
@@ -32222,9 +32227,9 @@ var require_sender = __commonJS({
32222
32227
  }
32223
32228
  async #run() {
32224
32229
  this.#running = true;
32225
- const queue = this.#queue;
32226
- while (!queue.isEmpty()) {
32227
- const node = queue.shift();
32230
+ const queue2 = this.#queue;
32231
+ while (!queue2.isEmpty()) {
32232
+ const node = queue2.shift();
32228
32233
  if (node.promise !== null) {
32229
32234
  await node.promise;
32230
32235
  }
@@ -33383,7 +33388,7 @@ ${value}`;
33383
33388
  var require_eventsource = __commonJS({
33384
33389
  "../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/web/eventsource/eventsource.js"(exports, module) {
33385
33390
  "use strict";
33386
- var { pipeline } = __require("stream");
33391
+ var { pipeline: pipeline2 } = __require("stream");
33387
33392
  var { fetching } = require_fetch();
33388
33393
  var { makeRequest } = require_request2();
33389
33394
  var { webidl } = require_webidl();
@@ -33541,7 +33546,7 @@ var require_eventsource = __commonJS({
33541
33546
  ));
33542
33547
  }
33543
33548
  });
33544
- pipeline(
33549
+ pipeline2(
33545
33550
  response.body.stream,
33546
33551
  eventSourceStream,
33547
33552
  (error) => {
@@ -33896,11 +33901,13 @@ function expBackoff(attempt) {
33896
33901
  const i = Math.min(Math.max(attempt, 0), BACKOFF_MS.length - 1);
33897
33902
  return BACKOFF_MS[i];
33898
33903
  }
33899
- var INGEST_BATCH_MAX_EVENTS, BACKOFF_MS, BACKSTOP_SCAN_MS;
33904
+ var INGEST_BATCH_MAX_EVENTS, SESSION_DEBOUNCE_MS, FORCE_SHIP_THRESHOLD, BACKOFF_MS, BACKSTOP_SCAN_MS;
33900
33905
  var init_config = __esm({
33901
33906
  "../../packages/daemon-core/src/config/index.ts"() {
33902
33907
  "use strict";
33903
33908
  INGEST_BATCH_MAX_EVENTS = 1e3;
33909
+ SESSION_DEBOUNCE_MS = 7e3;
33910
+ FORCE_SHIP_THRESHOLD = 200;
33904
33911
  BACKOFF_MS = [1e3, 2500, 5e3, 1e4, 2e4, 6e4];
33905
33912
  BACKSTOP_SCAN_MS = 5 * 6e4;
33906
33913
  }
@@ -34890,7 +34897,7 @@ async function fetchDeviceViewLedgerByClaim(claimCode) {
34890
34897
  }
34891
34898
  function ingestClient() {
34892
34899
  if (_ingest) return _ingest;
34893
- const logger = createLogger("daemon.ingest");
34900
+ const logger2 = createLogger("daemon.ingest");
34894
34901
  _ingest = new IngestClient({
34895
34902
  apiUrl: state.apiUrl,
34896
34903
  auth: {
@@ -34906,7 +34913,7 @@ function ingestClient() {
34906
34913
  }
34907
34914
  }
34908
34915
  },
34909
- logger
34916
+ logger: logger2
34910
34917
  });
34911
34918
  return _ingest;
34912
34919
  }
@@ -35087,17 +35094,101 @@ var init_ids2 = __esm({
35087
35094
  });
35088
35095
 
35089
35096
  // ../../packages/daemon-core/src/queue/index.ts
35097
+ async function buildBatches(opts) {
35098
+ const debounceMs = opts.debounceMs ?? SESSION_DEBOUNCE_MS;
35099
+ const maxEvents = opts.maxEvents ?? INGEST_BATCH_MAX_EVENTS;
35100
+ const forceThreshold = opts.forceShipThreshold ?? FORCE_SHIP_THRESHOLD;
35101
+ const bySession = await opts.store.listUnsentBySession();
35102
+ const batches = [];
35103
+ let eventsInCurrentBatch = [];
35104
+ let sessionIdsInCurrentBatch = [];
35105
+ let toolCallsInCurrentBatch = [];
35106
+ for (const [, items] of bySession) {
35107
+ if (items.length === 0) continue;
35108
+ const lastTs = Math.max(...items.map((i) => i.last_event_ts_ms));
35109
+ const quiet = opts.nowMs - lastTs >= debounceMs;
35110
+ if (!quiet && items.length < forceThreshold) continue;
35111
+ for (const item of items) {
35112
+ const itemCalls = item.tool_calls ?? [];
35113
+ if (eventsInCurrentBatch.length >= maxEvents || eventsInCurrentBatch.length > 0 && toolCallsInCurrentBatch.length + itemCalls.length > INGEST_BATCH_MAX_TOOL_CALLS) {
35114
+ batches.push(
35115
+ await finalise({
35116
+ events: eventsInCurrentBatch,
35117
+ sessionIds: sessionIdsInCurrentBatch,
35118
+ toolCalls: toolCallsInCurrentBatch,
35119
+ pipeline: opts.pipeline,
35120
+ deviceId: opts.deviceId,
35121
+ daemonVersion: opts.daemonVersion
35122
+ })
35123
+ );
35124
+ eventsInCurrentBatch = [];
35125
+ sessionIdsInCurrentBatch = [];
35126
+ toolCallsInCurrentBatch = [];
35127
+ }
35128
+ eventsInCurrentBatch.push(item.event);
35129
+ sessionIdsInCurrentBatch.push(item.session_id);
35130
+ toolCallsInCurrentBatch.push(...itemCalls);
35131
+ }
35132
+ }
35133
+ if (eventsInCurrentBatch.length > 0) {
35134
+ batches.push(
35135
+ await finalise({
35136
+ events: eventsInCurrentBatch,
35137
+ sessionIds: sessionIdsInCurrentBatch,
35138
+ toolCalls: toolCallsInCurrentBatch,
35139
+ pipeline: opts.pipeline,
35140
+ deviceId: opts.deviceId,
35141
+ daemonVersion: opts.daemonVersion
35142
+ })
35143
+ );
35144
+ }
35145
+ return batches;
35146
+ }
35147
+ function attachSegmentIds(calls, segments) {
35148
+ const segmentByEvent = /* @__PURE__ */ new Map();
35149
+ for (const seg of segments) {
35150
+ for (const id of seg.source_event_ids) segmentByEvent.set(id, seg.segment_id);
35151
+ }
35152
+ return attachSegmentIdsByMap(calls, segmentByEvent);
35153
+ }
35090
35154
  function attachSegmentIdsByMap(calls, segmentByEvent) {
35091
35155
  return calls.map((c) => ({
35092
35156
  ...c,
35093
35157
  segment_id: segmentByEvent.get(c.source_event_id) ?? null
35094
35158
  }));
35095
35159
  }
35160
+ async function finalise(args) {
35161
+ const segments = [];
35162
+ const bySession = /* @__PURE__ */ new Map();
35163
+ for (let i = 0; i < args.events.length; i++) {
35164
+ const sid = args.sessionIds[i] ?? args.events[i]?.session_id ?? "unknown";
35165
+ const arr = bySession.get(sid) ?? [];
35166
+ const ev = args.events[i];
35167
+ if (ev) arr.push(ev);
35168
+ bySession.set(sid, arr);
35169
+ }
35170
+ for (const [, sessionEvents] of bySession) {
35171
+ const segs = await args.pipeline.run(sessionEvents);
35172
+ segments.push(...segs);
35173
+ }
35174
+ return {
35175
+ batch_id: batchId(),
35176
+ device_id: args.deviceId,
35177
+ daemon_version: args.daemonVersion,
35178
+ events: args.events,
35179
+ segments,
35180
+ // Segments are in hand here, so segment attribution happens at the
35181
+ // last responsible moment before the wire.
35182
+ tool_calls: attachSegmentIds(args.toolCalls, segments)
35183
+ };
35184
+ }
35185
+ var INGEST_BATCH_MAX_TOOL_CALLS;
35096
35186
  var init_queue = __esm({
35097
35187
  "../../packages/daemon-core/src/queue/index.ts"() {
35098
35188
  "use strict";
35099
35189
  init_config();
35100
35190
  init_ids2();
35191
+ INGEST_BATCH_MAX_TOOL_CALLS = 2e4;
35101
35192
  }
35102
35193
  });
35103
35194
 
@@ -37090,7 +37181,7 @@ var init_scan = __esm({
37090
37181
  init_api();
37091
37182
  init_config2();
37092
37183
  init_pipeline2();
37093
- DAEMON_VERSION = true ? "daemon-0.7.1" : "daemon-dev";
37184
+ DAEMON_VERSION = true ? "daemon-0.8.1" : "daemon-dev";
37094
37185
  BATCH_MAX_EVENTS = INGEST_BATCH_MAX_EVENTS;
37095
37186
  BATCH_MAX_TOOL_CALLS = 2e4;
37096
37187
  BATCH_BUFFER_HARD_CAP = BATCH_MAX_EVENTS * 2;
@@ -37327,6 +37418,165 @@ var init_update = __esm({
37327
37418
  }
37328
37419
  });
37329
37420
 
37421
+ // src/receiver.ts
37422
+ import { createServer } from "http";
37423
+ function queue() {
37424
+ if (!store) store = new FileQueueStore(homePath("sdk-ingest-queue.json"));
37425
+ return store;
37426
+ }
37427
+ function parseBatch(json) {
37428
+ if (typeof json !== "object" || json === null) return { error: "body must be a JSON object" };
37429
+ const b = json;
37430
+ if (!Array.isArray(b.events) || b.events.length === 0)
37431
+ return { error: "events must be a non-empty array" };
37432
+ if (b.events.length > 1e4) return { error: "too many events (max 10000)" };
37433
+ for (const e of b.events) {
37434
+ if (typeof e !== "object" || e === null) return { error: "each event must be an object" };
37435
+ const ev = e;
37436
+ for (const k of ["source_event_id", "session_id", "agent", "ts"]) {
37437
+ if (typeof ev[k] !== "string" || ev[k].length === 0)
37438
+ return { error: `event.${k} is required` };
37439
+ }
37440
+ }
37441
+ const toolCalls = Array.isArray(b.tool_calls) ? b.tool_calls : [];
37442
+ return { events: b.events, toolCalls };
37443
+ }
37444
+ async function enqueue(batch) {
37445
+ const q = queue();
37446
+ for (const event of batch.events) {
37447
+ const calls = batch.toolCalls.filter((tc) => tc.source_event_id === event.source_event_id).map(({ segment_id: _segmentId, ...draft }) => draft);
37448
+ await q.put({
37449
+ source_event_id: event.source_event_id,
37450
+ session_id: event.session_id,
37451
+ agent: event.agent,
37452
+ event,
37453
+ last_event_ts_ms: Date.parse(event.ts) || Date.now(),
37454
+ synced: false,
37455
+ sent_batch_id: null,
37456
+ tool_calls: calls.length > 0 ? calls : void 0
37457
+ });
37458
+ }
37459
+ return batch.events.length;
37460
+ }
37461
+ async function drainLocalQueue(opts) {
37462
+ if (draining) return { batches: 0, events: 0 };
37463
+ draining = true;
37464
+ try {
37465
+ const q = queue();
37466
+ if (await q.countUnsent() === 0) return { batches: 0, events: 0 };
37467
+ const batches = await buildBatches({
37468
+ store: q,
37469
+ pipeline,
37470
+ deviceId: opts.deviceId,
37471
+ daemonVersion: opts.daemonVersion,
37472
+ nowMs: Date.now()
37473
+ });
37474
+ let events = 0;
37475
+ for (const batch of batches) {
37476
+ const shipped = {
37477
+ ...batch,
37478
+ events: batch.events.map(({ content_excerpt: _excerpt, ...rest }) => rest)
37479
+ };
37480
+ await uploadBatch(shipped);
37481
+ await q.markSent(
37482
+ batch.events.map((e) => e.source_event_id),
37483
+ batch.batch_id
37484
+ );
37485
+ events += batch.events.length;
37486
+ }
37487
+ return { batches: batches.length, events };
37488
+ } finally {
37489
+ draining = false;
37490
+ }
37491
+ }
37492
+ function localQueueDepth() {
37493
+ return queue().countUnsent();
37494
+ }
37495
+ function startLocalIngestReceiver(opts = {}) {
37496
+ const port = opts.port ?? (Number(process.env.MODELSTAT_LOCAL_INGEST_PORT) || DEFAULT_LOCAL_INGEST_PORT);
37497
+ return new Promise((resolve6) => {
37498
+ const server = createServer((req, res) => void handle(req, res));
37499
+ let settled = false;
37500
+ server.on("error", (err) => {
37501
+ if (settled) return;
37502
+ settled = true;
37503
+ logger.warn(
37504
+ `local ingest receiver disabled \u2014 SDK local_daemon mode unavailable: ${err.code ?? err.message} on 127.0.0.1:${port}`
37505
+ );
37506
+ resolve6(null);
37507
+ });
37508
+ server.listen(port, "127.0.0.1", () => {
37509
+ settled = true;
37510
+ const addr = server.address();
37511
+ const boundPort = typeof addr === "object" && addr ? addr.port : port;
37512
+ logger.info(`local ingest receiver on http://127.0.0.1:${boundPort}/v1/ingest`);
37513
+ resolve6({
37514
+ port: boundPort,
37515
+ close: () => new Promise((r) => server.close(() => r()))
37516
+ });
37517
+ });
37518
+ });
37519
+ }
37520
+ async function handle(req, res) {
37521
+ const send = (code, body) => {
37522
+ res.writeHead(code, { "content-type": "application/json" });
37523
+ res.end(JSON.stringify(body));
37524
+ };
37525
+ const path = (req.url ?? "").split("?")[0];
37526
+ if (req.method === "GET" && path === "/healthz") return send(200, { ok: true });
37527
+ if (req.method !== "POST" || path !== "/v1/ingest" && path !== "/v1/ingest/raw") {
37528
+ return send(404, { error: "not found" });
37529
+ }
37530
+ let size = 0;
37531
+ const chunks = [];
37532
+ try {
37533
+ for await (const chunk of req) {
37534
+ size += chunk.length;
37535
+ if (size > MAX_BODY_BYTES) {
37536
+ send(413, { error: "batch too large" });
37537
+ req.destroy();
37538
+ return;
37539
+ }
37540
+ chunks.push(chunk);
37541
+ }
37542
+ } catch {
37543
+ return send(400, { error: "read error" });
37544
+ }
37545
+ let json;
37546
+ try {
37547
+ json = JSON.parse(Buffer.concat(chunks).toString("utf8"));
37548
+ } catch {
37549
+ return send(400, { error: "invalid json" });
37550
+ }
37551
+ const parsed = parseBatch(json);
37552
+ if ("error" in parsed) return send(400, parsed);
37553
+ try {
37554
+ const accepted = await enqueue(parsed);
37555
+ return send(200, { accepted, queued: true });
37556
+ } catch (e) {
37557
+ logger.warn(`local ingest enqueue failed: ${e.message}`);
37558
+ return send(500, { error: "enqueue failed" });
37559
+ }
37560
+ }
37561
+ var logger, DEFAULT_LOCAL_INGEST_PORT, MAX_BODY_BYTES, store, pipeline, draining;
37562
+ var init_receiver = __esm({
37563
+ "src/receiver.ts"() {
37564
+ "use strict";
37565
+ init_logger();
37566
+ init_node();
37567
+ init_queue();
37568
+ init_api();
37569
+ init_paths();
37570
+ init_pipeline2();
37571
+ logger = createLogger("daemon.local-ingest");
37572
+ DEFAULT_LOCAL_INGEST_PORT = 4319;
37573
+ MAX_BODY_BYTES = 16 * 1024 * 1024;
37574
+ store = null;
37575
+ pipeline = { run: (events) => buildSegments(events) };
37576
+ draining = false;
37577
+ }
37578
+ });
37579
+
37330
37580
  // src/reconcile.ts
37331
37581
  import { stat as stat3 } from "fs/promises";
37332
37582
  function utcDay(ts) {
@@ -37704,19 +37954,19 @@ function createPolicyRefresher(opts) {
37704
37954
  ...opts.fetch ? { fetch: opts.fetch } : {},
37705
37955
  ...opts.logger ? { logger: opts.logger } : {}
37706
37956
  };
37707
- const store = new RemoteConfigStore(env, [policiesKind]);
37957
+ const store2 = new RemoteConfigStore(env, [policiesKind]);
37708
37958
  let timer = null;
37709
37959
  const apply = () => {
37710
- const bundle = store.get("policies");
37960
+ const bundle = store2.get("policies");
37711
37961
  setRemoteRedactionPatterns(compilePolicyPatterns(bundle));
37712
37962
  };
37713
37963
  const refresh = async () => {
37714
- await store.refresh("policies");
37964
+ await store2.refresh("policies");
37715
37965
  apply();
37716
37966
  };
37717
37967
  return {
37718
37968
  async start() {
37719
- await store.initFromCache();
37969
+ await store2.initFromCache();
37720
37970
  apply();
37721
37971
  void refresh().catch(() => {
37722
37972
  });
@@ -39743,6 +39993,25 @@ async function runDaemon(opts = {}) {
39743
39993
  setPhase("error", `summariser preflight failed: ${err.message}`);
39744
39994
  throw err;
39745
39995
  }
39996
+ const localIngest = await startLocalIngestReceiver();
39997
+ const LOCAL_DRAIN_INTERVAL_MS = 5e3;
39998
+ let localDrainTimer = null;
39999
+ if (localIngest) {
40000
+ const drainTick = async () => {
40001
+ try {
40002
+ const { events } = await drainLocalQueue({
40003
+ deviceId: state.deviceId,
40004
+ daemonVersion: DAEMON_VERSION2
40005
+ });
40006
+ if (events > 0) bumpStat("sdk_events_uploaded", events);
40007
+ setStat("sdk_queue", await localQueueDepth());
40008
+ } catch (e) {
40009
+ setMessage(`SDK ingest upload deferred: ${describeErrorWithCause(e)}`);
40010
+ }
40011
+ };
40012
+ localDrainTimer = setInterval(() => void drainTick(), LOCAL_DRAIN_INTERVAL_MS);
40013
+ localDrainTimer.unref();
40014
+ }
39746
40015
  const { reconcileProcessingVersion: reconcileProcessingVersion2 } = await Promise.resolve().then(() => (init_processing_version(), processing_version_exports));
39747
40016
  const pv = reconcileProcessingVersion2(state);
39748
40017
  if (pv.changed) {
@@ -39809,6 +40078,8 @@ async function runDaemon(opts = {}) {
39809
40078
  setPhase("offline", "Shutting down");
39810
40079
  await sendHeartbeat();
39811
40080
  await watcher.close();
40081
+ if (localDrainTimer) clearInterval(localDrainTimer);
40082
+ await localIngest?.close();
39812
40083
  try {
39813
40084
  const { disposeLlama: disposeLlama2 } = await Promise.resolve().then(() => (init_node(), node_exports));
39814
40085
  await disposeLlama2();
@@ -39832,11 +40103,12 @@ var init_daemon = __esm({
39832
40103
  init_config2();
39833
40104
  init_lock();
39834
40105
  init_machine_key();
40106
+ init_receiver();
39835
40107
  init_reconcile();
39836
40108
  init_scan();
39837
40109
  init_single_flight();
39838
40110
  init_update();
39839
- DAEMON_VERSION2 = true ? "daemon-0.7.1" : "daemon-dev";
40111
+ DAEMON_VERSION2 = true ? "daemon-0.8.1" : "daemon-dev";
39840
40112
  HEARTBEAT_INTERVAL_MS = 1e4;
39841
40113
  SCAN_INTERVAL_MS = 5 * 60 * 1e3;
39842
40114
  DISCOVERY_INTERVAL_MS = 6e4;
@@ -40440,7 +40712,7 @@ function tryOpenBrowser(url) {
40440
40712
  return false;
40441
40713
  }
40442
40714
  }
40443
- var DAEMON_VERSION3 = true ? "daemon-0.7.1" : "daemon-dev";
40715
+ var DAEMON_VERSION3 = true ? "daemon-0.8.1" : "daemon-dev";
40444
40716
  function osFamily() {
40445
40717
  const p = platform6();
40446
40718
  if (p === "darwin") return "macos";