repoburg 1.3.273 → 1.3.275

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/agent-pane/backend/dist/index.js +424 -79
  2. package/agent-pane/backend/dist/server.js +424 -79
  3. package/agent-pane/frontend/dist/assets/{index-DlwZnsU3.css → index-BKDM-AC5.css} +1 -1
  4. package/agent-pane/frontend/dist/assets/index-BNyA-dQb.js +617 -0
  5. package/agent-pane/frontend/dist/index.html +2 -2
  6. package/backend/dist/src/core-entities/llm-call-log.entity.d.ts +2 -0
  7. package/backend/dist/src/core-entities/llm-call-log.entity.js +11 -0
  8. package/backend/dist/src/core-entities/llm-call-log.entity.js.map +1 -1
  9. package/backend/dist/src/llm-call-logs/dto/llm-call-log.dto.d.ts +2 -0
  10. package/backend/dist/src/llm-call-logs/dto/llm-call-log.dto.js +6 -0
  11. package/backend/dist/src/llm-call-logs/dto/llm-call-log.dto.js.map +1 -1
  12. package/backend/dist/src/llm-call-logs/llm-call-logs.module.js +2 -1
  13. package/backend/dist/src/llm-call-logs/llm-call-logs.module.js.map +1 -1
  14. package/backend/dist/src/llm-call-logs/llm-call-logs.service.d.ts +3 -1
  15. package/backend/dist/src/llm-call-logs/llm-call-logs.service.js +8 -2
  16. package/backend/dist/src/llm-call-logs/llm-call-logs.service.js.map +1 -1
  17. package/backend/dist/src/llm-provider/alibaba-llm.provider.js +1 -0
  18. package/backend/dist/src/llm-provider/alibaba-llm.provider.js.map +1 -1
  19. package/backend/dist/src/llm-provider/ollama-llm.provider.js +1 -0
  20. package/backend/dist/src/llm-provider/ollama-llm.provider.js.map +1 -1
  21. package/backend/dist/src/llm-provider/openai-llm.provider.js +1 -0
  22. package/backend/dist/src/llm-provider/openai-llm.provider.js.map +1 -1
  23. package/backend/dist/src/llm-provider/openrouter-llm.provider.js +1 -0
  24. package/backend/dist/src/llm-provider/openrouter-llm.provider.js.map +1 -1
  25. package/backend/dist/src/llm-provider/zai-llm.provider.js +1 -0
  26. package/backend/dist/src/llm-provider/zai-llm.provider.js.map +1 -1
  27. package/backend/dist/src/usage-export/usage-export.module.d.ts +2 -0
  28. package/backend/dist/src/usage-export/usage-export.module.js +21 -0
  29. package/backend/dist/src/usage-export/usage-export.module.js.map +1 -0
  30. package/backend/dist/src/usage-export/usage-export.service.d.ts +6 -0
  31. package/backend/dist/src/usage-export/usage-export.service.js +54 -0
  32. package/backend/dist/src/usage-export/usage-export.service.js.map +1 -0
  33. package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
  34. package/daemon/dist/workspace-validation.js +11 -2
  35. package/daemon/dist/workspace-validation.spec.js +8 -2
  36. package/package.json +1 -1
  37. package/tabtape/extension/dist/build-info.json +3 -3
  38. package/tabtape/extension/dist/manifest.json +1 -1
  39. package/tabtape/ui/dist/build-info.json +3 -3
  40. package/agent-pane/frontend/dist/assets/index-WRsdlJfD.js +0 -606
@@ -372,26 +372,26 @@ var require_permessage_deflate = __commonJS({
372
372
  value = value[0];
373
373
  if (key === "client_max_window_bits") {
374
374
  if (value !== true) {
375
- const num = +value;
376
- if (!Number.isInteger(num) || num < 8 || num > 15) {
375
+ const num2 = +value;
376
+ if (!Number.isInteger(num2) || num2 < 8 || num2 > 15) {
377
377
  throw new TypeError(
378
378
  `Invalid value for parameter "${key}": ${value}`
379
379
  );
380
380
  }
381
- value = num;
381
+ value = num2;
382
382
  } else if (!this._isServer) {
383
383
  throw new TypeError(
384
384
  `Invalid value for parameter "${key}": ${value}`
385
385
  );
386
386
  }
387
387
  } else if (key === "server_max_window_bits") {
388
- const num = +value;
389
- if (!Number.isInteger(num) || num < 8 || num > 15) {
388
+ const num2 = +value;
389
+ if (!Number.isInteger(num2) || num2 < 8 || num2 > 15) {
390
390
  throw new TypeError(
391
391
  `Invalid value for parameter "${key}": ${value}`
392
392
  );
393
393
  }
394
- value = num;
394
+ value = num2;
395
395
  } else if (key === "client_no_context_takeover" || key === "server_no_context_takeover") {
396
396
  if (value !== true) {
397
397
  throw new TypeError(
@@ -1105,8 +1105,8 @@ var require_receiver = __commonJS({
1105
1105
  return;
1106
1106
  }
1107
1107
  const buf = this.consume(8);
1108
- const num = buf.readUInt32BE(0);
1109
- if (num > Math.pow(2, 53 - 32) - 1) {
1108
+ const num2 = buf.readUInt32BE(0);
1109
+ if (num2 > Math.pow(2, 53 - 32) - 1) {
1110
1110
  const error = this.createError(
1111
1111
  RangeError,
1112
1112
  "Unsupported WebSocket frame: payload length > 2^53 - 1",
@@ -1117,7 +1117,7 @@ var require_receiver = __commonJS({
1117
1117
  cb(error);
1118
1118
  return;
1119
1119
  }
1120
- this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4);
1120
+ this._payloadLength = num2 * Math.pow(2, 32) + buf.readUInt32BE(4);
1121
1121
  this.haveLength(cb);
1122
1122
  }
1123
1123
  /**
@@ -11520,7 +11520,7 @@ var require_thread_stream = __commonJS({
11520
11520
  var { version } = require_package();
11521
11521
  var { EventEmitter: EventEmitter2 } = __require("events");
11522
11522
  var { Worker } = __require("worker_threads");
11523
- var { join: join4 } = __require("path");
11523
+ var { join: join5 } = __require("path");
11524
11524
  var { pathToFileURL } = __require("url");
11525
11525
  var { wait } = require_wait();
11526
11526
  var {
@@ -11571,7 +11571,7 @@ var require_thread_stream = __commonJS({
11571
11571
  function createWorker(stream, opts) {
11572
11572
  const { filename, workerData } = opts;
11573
11573
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
11574
- const toExecute = bundlerOverrides["thread-stream-worker"] || join4(__dirname, "lib", "worker.js");
11574
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join5(__dirname, "lib", "worker.js");
11575
11575
  const worker = new Worker(toExecute, {
11576
11576
  ...opts.workerOpts,
11577
11577
  name: opts.workerOpts?.name || "thread-stream",
@@ -12039,7 +12039,7 @@ var require_transport = __commonJS({
12039
12039
  var { createRequire } = __require("module");
12040
12040
  var { existsSync: existsSync3 } = __require("node:fs");
12041
12041
  var getCallers = require_caller();
12042
- var { join: join4, isAbsolute, sep } = __require("node:path");
12042
+ var { join: join5, isAbsolute, sep } = __require("node:path");
12043
12043
  var { fileURLToPath: fileURLToPath3 } = __require("node:url");
12044
12044
  var sleep = require_atomic_sleep();
12045
12045
  var onExit = require_on_exit_leak_free();
@@ -12192,7 +12192,7 @@ var require_transport = __commonJS({
12192
12192
  throw new Error("only one of target or targets can be specified");
12193
12193
  }
12194
12194
  if (targets) {
12195
- target = bundlerOverrides["pino-worker"] || join4(__dirname, "worker.js");
12195
+ target = bundlerOverrides["pino-worker"] || join5(__dirname, "worker.js");
12196
12196
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
12197
12197
  return {
12198
12198
  ...dest,
@@ -12210,7 +12210,7 @@ var require_transport = __commonJS({
12210
12210
  });
12211
12211
  });
12212
12212
  } else if (pipeline) {
12213
- target = bundlerOverrides["pino-worker"] || join4(__dirname, "worker.js");
12213
+ target = bundlerOverrides["pino-worker"] || join5(__dirname, "worker.js");
12214
12214
  options.pipelines = [pipeline.map((dest) => {
12215
12215
  return {
12216
12216
  ...dest,
@@ -12233,7 +12233,7 @@ var require_transport = __commonJS({
12233
12233
  return origin;
12234
12234
  }
12235
12235
  if (origin === "pino/file") {
12236
- return join4(__dirname, "..", "file.js");
12236
+ return join5(__dirname, "..", "file.js");
12237
12237
  }
12238
12238
  let fixTarget2;
12239
12239
  for (const filePath of callers) {
@@ -12348,14 +12348,14 @@ var require_tools = __commonJS({
12348
12348
  }
12349
12349
  return point < 32 ? JSON.stringify(str) : '"' + result + '"';
12350
12350
  }
12351
- function asJson(obj, msg, num, time) {
12351
+ function asJson(obj, msg, num2, time) {
12352
12352
  if (asJsonChan.hasSubscribers === false) {
12353
- return _asJson.call(this, obj, msg, num, time);
12353
+ return _asJson.call(this, obj, msg, num2, time);
12354
12354
  }
12355
12355
  const store = { instance: this, arguments };
12356
- return asJsonChan.traceSync(_asJson, store, this, obj, msg, num, time);
12356
+ return asJsonChan.traceSync(_asJson, store, this, obj, msg, num2, time);
12357
12357
  }
12358
- function _asJson(obj, msg, num, time) {
12358
+ function _asJson(obj, msg, num2, time) {
12359
12359
  const stringify2 = this[stringifySym];
12360
12360
  const stringifySafe = this[stringifySafeSym];
12361
12361
  const stringifiers = this[stringifiersSym];
@@ -12365,7 +12365,7 @@ var require_tools = __commonJS({
12365
12365
  const formatters = this[formattersSym];
12366
12366
  const messageKey = this[messageKeySym];
12367
12367
  const errorKey = this[errorKeySym];
12368
- let data = this[lsCacheSym][num] + time;
12368
+ let data = this[lsCacheSym][num2] + time;
12369
12369
  data = data + chindings;
12370
12370
  let value;
12371
12371
  if (formatters.log) {
@@ -12990,7 +12990,7 @@ var require_proto = __commonJS({
12990
12990
  function defaultMixinMergeStrategy(mergeObject, mixinObject) {
12991
12991
  return Object.assign(mixinObject, mergeObject);
12992
12992
  }
12993
- function write(_obj, msg, num) {
12993
+ function write(_obj, msg, num2) {
12994
12994
  const t = this[timeSym]();
12995
12995
  const mixin = this[mixinSym];
12996
12996
  const errorKey = this[errorKeySym];
@@ -13012,12 +13012,12 @@ var require_proto = __commonJS({
13012
13012
  }
13013
13013
  }
13014
13014
  if (mixin) {
13015
- obj = mixinMergeStrategy(obj, mixin(obj, num, this));
13015
+ obj = mixinMergeStrategy(obj, mixin(obj, num2, this));
13016
13016
  }
13017
- const s = this[asJsonSym](obj, msg, num, t);
13017
+ const s = this[asJsonSym](obj, msg, num2, t);
13018
13018
  const stream = this[streamSym];
13019
13019
  if (stream[needsMetadataGsym] === true) {
13020
- stream.lastLevel = num;
13020
+ stream.lastLevel = num2;
13021
13021
  stream.lastObj = obj;
13022
13022
  stream.lastMsg = msg;
13023
13023
  stream.lastTime = t.slice(this[timeSliceIndexSym]);
@@ -13213,7 +13213,7 @@ var require_safe_stable_stringify = __commonJS({
13213
13213
  return circularValue;
13214
13214
  }
13215
13215
  let res = "";
13216
- let join4 = ",";
13216
+ let join5 = ",";
13217
13217
  const originalIndentation = indentation;
13218
13218
  if (Array.isArray(value)) {
13219
13219
  if (value.length === 0) {
@@ -13227,7 +13227,7 @@ var require_safe_stable_stringify = __commonJS({
13227
13227
  indentation += spacer;
13228
13228
  res += `
13229
13229
  ${indentation}`;
13230
- join4 = `,
13230
+ join5 = `,
13231
13231
  ${indentation}`;
13232
13232
  }
13233
13233
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -13235,13 +13235,13 @@ ${indentation}`;
13235
13235
  for (; i < maximumValuesToStringify - 1; i++) {
13236
13236
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
13237
13237
  res += tmp2 !== void 0 ? tmp2 : "null";
13238
- res += join4;
13238
+ res += join5;
13239
13239
  }
13240
13240
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
13241
13241
  res += tmp !== void 0 ? tmp : "null";
13242
13242
  if (value.length - 1 > maximumBreadth) {
13243
13243
  const removedKeys = value.length - maximumBreadth - 1;
13244
- res += `${join4}"... ${getItemCount(removedKeys)} not stringified"`;
13244
+ res += `${join5}"... ${getItemCount(removedKeys)} not stringified"`;
13245
13245
  }
13246
13246
  if (spacer !== "") {
13247
13247
  res += `
@@ -13262,7 +13262,7 @@ ${originalIndentation}`;
13262
13262
  let separator = "";
13263
13263
  if (spacer !== "") {
13264
13264
  indentation += spacer;
13265
- join4 = `,
13265
+ join5 = `,
13266
13266
  ${indentation}`;
13267
13267
  whitespace = " ";
13268
13268
  }
@@ -13276,13 +13276,13 @@ ${indentation}`;
13276
13276
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
13277
13277
  if (tmp !== void 0) {
13278
13278
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
13279
- separator = join4;
13279
+ separator = join5;
13280
13280
  }
13281
13281
  }
13282
13282
  if (keyLength > maximumBreadth) {
13283
13283
  const removedKeys = keyLength - maximumBreadth;
13284
13284
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
13285
- separator = join4;
13285
+ separator = join5;
13286
13286
  }
13287
13287
  if (spacer !== "" && separator.length > 1) {
13288
13288
  res = `
@@ -13323,7 +13323,7 @@ ${originalIndentation}`;
13323
13323
  }
13324
13324
  const originalIndentation = indentation;
13325
13325
  let res = "";
13326
- let join4 = ",";
13326
+ let join5 = ",";
13327
13327
  if (Array.isArray(value)) {
13328
13328
  if (value.length === 0) {
13329
13329
  return "[]";
@@ -13336,7 +13336,7 @@ ${originalIndentation}`;
13336
13336
  indentation += spacer;
13337
13337
  res += `
13338
13338
  ${indentation}`;
13339
- join4 = `,
13339
+ join5 = `,
13340
13340
  ${indentation}`;
13341
13341
  }
13342
13342
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -13344,13 +13344,13 @@ ${indentation}`;
13344
13344
  for (; i < maximumValuesToStringify - 1; i++) {
13345
13345
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
13346
13346
  res += tmp2 !== void 0 ? tmp2 : "null";
13347
- res += join4;
13347
+ res += join5;
13348
13348
  }
13349
13349
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
13350
13350
  res += tmp !== void 0 ? tmp : "null";
13351
13351
  if (value.length - 1 > maximumBreadth) {
13352
13352
  const removedKeys = value.length - maximumBreadth - 1;
13353
- res += `${join4}"... ${getItemCount(removedKeys)} not stringified"`;
13353
+ res += `${join5}"... ${getItemCount(removedKeys)} not stringified"`;
13354
13354
  }
13355
13355
  if (spacer !== "") {
13356
13356
  res += `
@@ -13363,7 +13363,7 @@ ${originalIndentation}`;
13363
13363
  let whitespace = "";
13364
13364
  if (spacer !== "") {
13365
13365
  indentation += spacer;
13366
- join4 = `,
13366
+ join5 = `,
13367
13367
  ${indentation}`;
13368
13368
  whitespace = " ";
13369
13369
  }
@@ -13372,7 +13372,7 @@ ${indentation}`;
13372
13372
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
13373
13373
  if (tmp !== void 0) {
13374
13374
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
13375
- separator = join4;
13375
+ separator = join5;
13376
13376
  }
13377
13377
  }
13378
13378
  if (spacer !== "" && separator.length > 1) {
@@ -13430,20 +13430,20 @@ ${originalIndentation}`;
13430
13430
  indentation += spacer;
13431
13431
  let res2 = `
13432
13432
  ${indentation}`;
13433
- const join5 = `,
13433
+ const join6 = `,
13434
13434
  ${indentation}`;
13435
13435
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
13436
13436
  let i = 0;
13437
13437
  for (; i < maximumValuesToStringify - 1; i++) {
13438
13438
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
13439
13439
  res2 += tmp2 !== void 0 ? tmp2 : "null";
13440
- res2 += join5;
13440
+ res2 += join6;
13441
13441
  }
13442
13442
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
13443
13443
  res2 += tmp !== void 0 ? tmp : "null";
13444
13444
  if (value.length - 1 > maximumBreadth) {
13445
13445
  const removedKeys = value.length - maximumBreadth - 1;
13446
- res2 += `${join5}"... ${getItemCount(removedKeys)} not stringified"`;
13446
+ res2 += `${join6}"... ${getItemCount(removedKeys)} not stringified"`;
13447
13447
  }
13448
13448
  res2 += `
13449
13449
  ${originalIndentation}`;
@@ -13459,16 +13459,16 @@ ${originalIndentation}`;
13459
13459
  return '"[Object]"';
13460
13460
  }
13461
13461
  indentation += spacer;
13462
- const join4 = `,
13462
+ const join5 = `,
13463
13463
  ${indentation}`;
13464
13464
  let res = "";
13465
13465
  let separator = "";
13466
13466
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
13467
13467
  if (isTypedArrayWithEntries(value)) {
13468
- res += stringifyTypedArray(value, join4, maximumBreadth);
13468
+ res += stringifyTypedArray(value, join5, maximumBreadth);
13469
13469
  keys = keys.slice(value.length);
13470
13470
  maximumPropertiesToStringify -= value.length;
13471
- separator = join4;
13471
+ separator = join5;
13472
13472
  }
13473
13473
  if (deterministic) {
13474
13474
  keys = sort(keys, comparator);
@@ -13479,13 +13479,13 @@ ${indentation}`;
13479
13479
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
13480
13480
  if (tmp !== void 0) {
13481
13481
  res += `${separator}${strEscape(key2)}: ${tmp}`;
13482
- separator = join4;
13482
+ separator = join5;
13483
13483
  }
13484
13484
  }
13485
13485
  if (keyLength > maximumBreadth) {
13486
13486
  const removedKeys = keyLength - maximumBreadth;
13487
13487
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
13488
- separator = join4;
13488
+ separator = join5;
13489
13489
  }
13490
13490
  if (separator !== "") {
13491
13491
  res = `
@@ -14379,13 +14379,13 @@ var require_serializer = __commonJS({
14379
14379
  return "" + integer;
14380
14380
  }
14381
14381
  asNumber(i) {
14382
- const num = Number(i);
14383
- if (num !== num) {
14382
+ const num2 = Number(i);
14383
+ if (num2 !== num2) {
14384
14384
  throw new Error(`The value "${i}" cannot be converted to a number.`);
14385
- } else if (num === Infinity || num === -Infinity) {
14385
+ } else if (num2 === Infinity || num2 === -Infinity) {
14386
14386
  return "null";
14387
14387
  } else {
14388
- return "" + num;
14388
+ return "" + num2;
14389
14389
  }
14390
14390
  }
14391
14391
  asBoolean(bool) {
@@ -31796,9 +31796,9 @@ var require_semver = __commonJS({
31796
31796
  } else {
31797
31797
  this.prerelease = m[4].split(".").map((id) => {
31798
31798
  if (/^[0-9]+$/.test(id)) {
31799
- const num = +id;
31800
- if (num >= 0 && num < MAX_SAFE_INTEGER) {
31801
- return num;
31799
+ const num2 = +id;
31800
+ if (num2 >= 0 && num2 < MAX_SAFE_INTEGER) {
31801
+ return num2;
31802
31802
  }
31803
31803
  }
31804
31804
  return id;
@@ -41789,6 +41789,25 @@ var import_cors = __toESM(require_cors(), 1);
41789
41789
  // src/routes/sessions.ts
41790
41790
  import path from "node:path";
41791
41791
 
41792
+ // src/db/item-trim.ts
41793
+ var TRIMMABLE_KEYS = ["aggregated_output", "output"];
41794
+ var MAX_ITEM_TEXT_BYTES = 64e3;
41795
+ function prunedMarker(maxBytes) {
41796
+ return `\u2026 [pruned: kept newest ${maxBytes} bytes]
41797
+ `;
41798
+ }
41799
+ function trimItemData(data, maxBytes = MAX_ITEM_TEXT_BYTES) {
41800
+ let trimmed = null;
41801
+ for (const key of TRIMMABLE_KEYS) {
41802
+ const value = data[key];
41803
+ if (typeof value === "string" && value.length > maxBytes) {
41804
+ if (!trimmed) trimmed = { ...data };
41805
+ trimmed[key] = prunedMarker(maxBytes) + value.slice(-maxBytes);
41806
+ }
41807
+ }
41808
+ return trimmed ?? data;
41809
+ }
41810
+
41792
41811
  // src/routes/session-touch.ts
41793
41812
  function markSessionTouched(db2, sessionId) {
41794
41813
  const { changes } = db2.prepare("UPDATE sessions SET user_touched = 1 WHERE id = ? AND user_touched = 0").run(sessionId);
@@ -41819,7 +41838,7 @@ async function registerSessionRoutes(fastify) {
41819
41838
  for (const row of itemRows) {
41820
41839
  const item = {
41821
41840
  ...row,
41822
- data: row.data ? JSON.parse(row.data) : null
41841
+ data: row.data ? trimItemData(JSON.parse(row.data)) : null
41823
41842
  };
41824
41843
  const list = itemsByTurn.get(item.turn_id) ?? [];
41825
41844
  list.push(item);
@@ -43402,7 +43421,6 @@ async function registerPerfLogRoutes(fastify) {
43402
43421
  }
43403
43422
 
43404
43423
  // src/routes/maintenance.ts
43405
- var TRIMMABLE_KEYS = ["aggregated_output", "output"];
43406
43424
  async function registerMaintenanceRoutes(fastify) {
43407
43425
  fastify.post(
43408
43426
  "/api/maintenance/prune-items",
@@ -43435,17 +43453,9 @@ async function registerMaintenanceRoutes(fastify) {
43435
43453
  } catch {
43436
43454
  continue;
43437
43455
  }
43438
- let changed = false;
43439
- for (const key of TRIMMABLE_KEYS) {
43440
- const v = data[key];
43441
- if (typeof v === "string" && v.length > maxBytes) {
43442
- data[key] = `\u2026 [pruned: kept newest ${maxBytes} bytes]
43443
- ` + v.slice(-maxBytes);
43444
- changed = true;
43445
- }
43446
- }
43447
- if (!changed) continue;
43448
- const next = JSON.stringify(data);
43456
+ const trimmed = trimItemData(data, maxBytes);
43457
+ if (trimmed === data) continue;
43458
+ const next = JSON.stringify(trimmed);
43449
43459
  bytesBefore += row.data.length;
43450
43460
  bytesAfter += next.length;
43451
43461
  pruned++;
@@ -98293,6 +98303,7 @@ Icons come from ONE library: Lucide (https://lucide.dev/icons, ISC).
98293
98303
  - createSession({ cwd, runtime_type, model, model_reasoning_effort, title }) - agent session owned by this app; model_reasoning_effort is the effort for its turns ("low" | "medium" | "high" | "xhigh" | "max" | "ultra", the levels the model supports), omitted = the runtime's default
98294
98304
  - submitTurn(sessionId, prompt, { attachments }?) - send a prompt to a session; attachments (optional) are stored files (id, url or the uploadFile result) or inline Blobs / { name, type, data } sent with that turn only; the agent sees attached images (Claude and Codex)
98295
98305
  - onTurnCompleted(callback) - callback(event, { turnId, response }) when a turn ends
98306
+ - onBackgroundTasks(callback) - callback({ sessionId, tasks }) when the live background-task set of one of the app's sessions changes (Monitor shells, backgrounded subagents); tasks is the CURRENT set, empty when none are left
98296
98307
  - onToolCall(callback) - callback({ callId, name, tool, args }) when the AGENT calls one of the manifest tools (name and tool carry the same tool name); answer with resolveToolCall(callId, result)
98297
98308
  - openSession(sessionId) - show that session in the pane (the view switches to its conversation); works only while the app is on screen, so call it from a click handler
98298
98309
  - openUrl(url) - open an absolute http(s) URL in a new browser tab; call it from a click handler (popup blockers allow tabs opened by a click)
@@ -98577,6 +98588,10 @@ interface AgentPaneCreateSessionOptions {
98577
98588
  interface AgentPaneTurnPayload { turnId?: string; response?: string | null; error?: string }
98578
98589
  interface AgentPaneDeltaPayload { sessionId?: string; turnId?: string; itemId?: string; delta?: string }
98579
98590
  interface AgentPaneToolCallPayload { callId: string; tool: string; name: string; args: Record<string, unknown> }
98591
+ /** Live background tasks of a session (Monitor shells, backgrounded subagents):
98592
+ * the CURRENT set, REPLACE semantics \u2014 an empty array means none are left. */
98593
+ interface AgentPaneBackgroundTasksPayload { sessionId?: string; tasks?: AgentPaneBackgroundTask[] }
98594
+ interface AgentPaneBackgroundTask { task_id: string; task_type: string; description: string }
98580
98595
  /** A file the app stored with uploadFile (PNG, JPEG, GIF or WebP, max 5 MB). */
98581
98596
  interface AgentPaneFile {
98582
98597
  /** Content id "<sha256>.<ext>": the same bytes always get the same id. */
@@ -98614,6 +98629,9 @@ declare const AgentPane: {
98614
98629
  onTurnDelta(cb: (payload: AgentPaneDeltaPayload) => void): void;
98615
98630
  onToolCall(cb: (payload: AgentPaneToolCallPayload) => void): void;
98616
98631
  onStateChanged(cb: (payload: unknown) => void): void;
98632
+ /** Fires when the live background-task set of one of the app's sessions
98633
+ * changes (also empty sets, when the last task ends). */
98634
+ onBackgroundTasks(cb: (payload: AgentPaneBackgroundTasksPayload | null) => void): void;
98617
98635
  resolveToolCall(callId: string, result?: unknown): Promise<{ ok: boolean }>;
98618
98636
  /** Show a session in the pane (switches the view to its conversation).
98619
98637
  * Only works while the app is on screen: call it from a click handler. */
@@ -99062,6 +99080,8 @@ function getHeadlessToolHandler(appId, workspace = "") {
99062
99080
  onTurnCompleted: () => {
99063
99081
  },
99064
99082
  onTurnDelta: () => {
99083
+ },
99084
+ onBackgroundTasks: () => {
99065
99085
  }
99066
99086
  };
99067
99087
  const sandbox = {
@@ -99572,7 +99592,8 @@ async function registerAppRoutes(fastify, opts) {
99572
99592
  if (!APP_ID_RE.test(id)) return reply.code(400).send({ error: "Invalid app id" });
99573
99593
  if (!readManifest(id)) return reply.code(404).send({ error: "App not found" });
99574
99594
  const rows = getDb().prepare("SELECT * FROM sessions WHERE origin_app_id = ? ORDER BY created_at DESC").all(id);
99575
- return rows;
99595
+ const tasks = listAllBackgroundTasks();
99596
+ return rows.map((row) => ({ ...row, background_tasks: tasks[row.id] ?? [] }));
99576
99597
  });
99577
99598
  fastify.post(
99578
99599
  "/api/apps/:id/edit-session",
@@ -99748,6 +99769,329 @@ async function registerSidecarRoutes(fastify) {
99748
99769
  );
99749
99770
  }
99750
99771
 
99772
+ // src/usage/usage-service.ts
99773
+ import { readdirSync as readdirSync2, readFileSync as readFileSync2 } from "node:fs";
99774
+ import { homedir as homedir3 } from "node:os";
99775
+ import { join as join3 } from "node:path";
99776
+
99777
+ // src/usage/usage-pricing.ts
99778
+ var PRICES = {
99779
+ // --- Z.AI GLM (cache writes are currently listed free) ---
99780
+ "glm-5.3-flashx": { input: 0.37, cachedInput: 0.075, cacheWrite: 0, output: 1.25 },
99781
+ "glm-5.3-flash": { input: 0.15, cachedInput: 0.03, cacheWrite: 0, output: 0.5 },
99782
+ "glm-5.3": { input: 1.4, cachedInput: 0.26, cacheWrite: 0, output: 4.4 },
99783
+ "glm-5.2": { input: 1.4, cachedInput: 0.26, cacheWrite: 0, output: 4.4 },
99784
+ "glm-5.1": { input: 1.4, cachedInput: 0.26, cacheWrite: 0, output: 4.4 },
99785
+ "glm-5": { input: 1, cachedInput: 0.2, cacheWrite: 0, output: 3.2 },
99786
+ "glm-4.7-flashx": { input: 0.07, cachedInput: 0.01, cacheWrite: 0, output: 0.4 },
99787
+ "glm-4.7": { input: 0.6, cachedInput: 0.11, cacheWrite: 0, output: 2.2 },
99788
+ "glm-4.6": { input: 0.6, cachedInput: 0.11, cacheWrite: 0, output: 2.2 },
99789
+ "glm-4.5-air": { input: 0.2, cachedInput: 0.03, cacheWrite: 0, output: 1.1 },
99790
+ "glm-4.5": { input: 0.6, cachedInput: 0.11, cacheWrite: 0, output: 2.2 },
99791
+ // --- OpenAI (no separate cache-write price; cached input only) ---
99792
+ "gpt-5.2-codex": { input: 1.75, cachedInput: 0.175, cacheWrite: 1.75, output: 14 },
99793
+ "gpt-5.3-codex": { input: 1.75, cachedInput: 0.175, cacheWrite: 1.75, output: 14 },
99794
+ "gpt-5.1-codex": { input: 1.25, cachedInput: 0.125, cacheWrite: 1.25, output: 10 },
99795
+ "gpt-5.2": { input: 1.75, cachedInput: 0.175, cacheWrite: 1.75, output: 14 },
99796
+ "gpt-5.1": { input: 1.25, cachedInput: 0.125, cacheWrite: 1.25, output: 10 },
99797
+ "gpt-5-mini": { input: 0.25, cachedInput: 0.025, cacheWrite: 0.25, output: 2 },
99798
+ "gpt-5-nano": { input: 0.05, cachedInput: 5e-3, cacheWrite: 0.05, output: 0.4 },
99799
+ "gpt-5": { input: 1.25, cachedInput: 0.125, cacheWrite: 1.25, output: 10 },
99800
+ "gpt-4.1-mini": { input: 0.4, cachedInput: 0.1, cacheWrite: 0.4, output: 1.6 },
99801
+ "gpt-4.1": { input: 2, cachedInput: 0.5, cacheWrite: 2, output: 8 },
99802
+ "gpt-4o-mini": { input: 0.15, cachedInput: 0.075, cacheWrite: 0.15, output: 0.6 },
99803
+ "gpt-4o": { input: 2.5, cachedInput: 1.25, cacheWrite: 2.5, output: 10 },
99804
+ // --- Anthropic Claude (5m cache writes; reads at 0.1x base) ---
99805
+ "claude-opus-5-5": { input: 4, cachedInput: 0.2, cacheWrite: 5, output: 20 },
99806
+ "claude-opus-4-5": { input: 5, cachedInput: 0.5, cacheWrite: 6.25, output: 25 },
99807
+ "claude-opus-4": { input: 15, cachedInput: 1.5, cacheWrite: 18.75, output: 75 },
99808
+ "claude-sonnet-5-5": { input: 2, cachedInput: 0.1, cacheWrite: 2.5, output: 10 },
99809
+ "claude-sonnet-5": { input: 2, cachedInput: 0.2, cacheWrite: 2.5, output: 10 },
99810
+ "claude-sonnet-4-5": { input: 3, cachedInput: 0.3, cacheWrite: 3.75, output: 15 },
99811
+ "claude-sonnet-4": { input: 3, cachedInput: 0.3, cacheWrite: 3.75, output: 15 },
99812
+ "claude-haiku-5-5": { input: 0.1, cachedInput: 0.01, cacheWrite: 0.125, output: 0.5 },
99813
+ "claude-haiku-4-5": { input: 1, cachedInput: 0.1, cacheWrite: 1.25, output: 5 }
99814
+ };
99815
+ var SORTED_KEYS = Object.keys(PRICES).sort((a, b) => b.length - a.length);
99816
+ function normalizeModelId(modelId) {
99817
+ const lastSegment = modelId.toLowerCase().split("/").pop() ?? "";
99818
+ const withoutTag = lastSegment.split(":")[0];
99819
+ return withoutTag.replace(/-(\d{8}|\d{4}-\d{2}-\d{2})$/, "");
99820
+ }
99821
+ function findModelPricing(modelId) {
99822
+ const normalized = normalizeModelId(modelId);
99823
+ for (const key of SORTED_KEYS) {
99824
+ if (normalized === key || normalized.startsWith(`${key}-`)) return PRICES[key];
99825
+ }
99826
+ return null;
99827
+ }
99828
+ function computeCost(modelId, usage, inputIncludesCached) {
99829
+ const pricing = findModelPricing(modelId);
99830
+ if (!pricing) return null;
99831
+ const m = 1e6;
99832
+ const { inputTokens, cachedInputTokens, cacheWriteInputTokens, outputTokens } = usage;
99833
+ const uncached = inputIncludesCached ? Math.max(0, inputTokens - cachedInputTokens - cacheWriteInputTokens) : inputTokens;
99834
+ return (uncached * pricing.input + cachedInputTokens * pricing.cachedInput + cacheWriteInputTokens * pricing.cacheWrite + outputTokens * pricing.output) / m;
99835
+ }
99836
+
99837
+ // src/usage/usage-service.ts
99838
+ function usageFilesDir() {
99839
+ return process.env.AGENT_PANE_USAGE_DIR ?? join3(homedir3(), ".repoburg", "usage");
99840
+ }
99841
+ function emptyBucket() {
99842
+ return { calls: 0, errors: 0, input: 0, cached: 0, cacheWrite: 0, output: 0, cost: 0 };
99843
+ }
99844
+ function newAggregator() {
99845
+ return {
99846
+ totals: emptyBucket(),
99847
+ unpricedModels: /* @__PURE__ */ new Set(),
99848
+ byRuntime: /* @__PURE__ */ new Map(),
99849
+ byModel: /* @__PURE__ */ new Map(),
99850
+ byDay: /* @__PURE__ */ new Map()
99851
+ };
99852
+ }
99853
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
99854
+ function addEvent(agg, event) {
99855
+ const { runtimeType, provider, model, effort, day, isError, usage, inputIncludesCached } = event;
99856
+ const cost = usage ? computeCost(model, usage, inputIncludesCached) : null;
99857
+ if (usage && cost === null) agg.unpricedModels.add(model);
99858
+ const bump = (b) => {
99859
+ b.calls += 1;
99860
+ if (isError) b.errors += 1;
99861
+ if (usage) {
99862
+ b.input += usage.inputTokens;
99863
+ b.cached += usage.cachedInputTokens;
99864
+ b.cacheWrite += usage.cacheWriteInputTokens;
99865
+ b.output += usage.outputTokens;
99866
+ b.cost += cost ?? 0;
99867
+ }
99868
+ };
99869
+ bump(agg.totals);
99870
+ const runtimeBucket = agg.byRuntime.get(runtimeType) ?? emptyBucket();
99871
+ bump(runtimeBucket);
99872
+ agg.byRuntime.set(runtimeType, runtimeBucket);
99873
+ const dayBucket = agg.byDay.get(day) ?? emptyBucket();
99874
+ bump(dayBucket);
99875
+ agg.byDay.set(day, dayBucket);
99876
+ const key = `${runtimeType}|${provider ?? ""}|${model}|${effort ?? ""}`;
99877
+ const row = agg.byModel.get(key);
99878
+ if (row) {
99879
+ row.calls += 1;
99880
+ if (isError) row.errors += 1;
99881
+ if (usage) {
99882
+ row.input_tokens += usage.inputTokens;
99883
+ row.cached_input_tokens += usage.cachedInputTokens;
99884
+ row.cache_write_input_tokens += usage.cacheWriteInputTokens;
99885
+ row.output_tokens += usage.outputTokens;
99886
+ if (cost !== null) row.cost_usd = (row.cost_usd ?? 0) + cost;
99887
+ }
99888
+ } else {
99889
+ agg.byModel.set(key, {
99890
+ runtime_type: runtimeType,
99891
+ provider,
99892
+ model,
99893
+ reasoning_effort: effort,
99894
+ calls: 1,
99895
+ errors: isError ? 1 : 0,
99896
+ input_tokens: usage?.inputTokens ?? 0,
99897
+ cached_input_tokens: usage?.cachedInputTokens ?? 0,
99898
+ cache_write_input_tokens: usage?.cacheWriteInputTokens ?? 0,
99899
+ output_tokens: usage?.outputTokens ?? 0,
99900
+ cost_usd: cost,
99901
+ priced: cost !== null
99902
+ });
99903
+ }
99904
+ }
99905
+ function parseTokenUsage(raw) {
99906
+ if (!raw) return null;
99907
+ try {
99908
+ const parsed = JSON.parse(raw);
99909
+ if (!parsed || typeof parsed !== "object") return null;
99910
+ return {
99911
+ inputTokens: num(parsed.input_tokens),
99912
+ cachedInputTokens: num(parsed.cached_input_tokens),
99913
+ cacheWriteInputTokens: num(parsed.cache_write_input_tokens),
99914
+ outputTokens: num(parsed.output_tokens)
99915
+ };
99916
+ } catch {
99917
+ return null;
99918
+ }
99919
+ }
99920
+ function addPaneTurns(agg, sinceIso, untilIso) {
99921
+ const params = [];
99922
+ let where = "s.runtime_type IN ('claude','codex') AND t.status IN ('completed','failed','aborted')";
99923
+ if (sinceIso) {
99924
+ params.push(sinceIso);
99925
+ where += " AND t.created_at >= ?";
99926
+ }
99927
+ if (untilIso) {
99928
+ params.push(untilIso);
99929
+ where += " AND t.created_at < ?";
99930
+ }
99931
+ const rows = getDb().prepare(
99932
+ `SELECT s.runtime_type, s.model, s.model_reasoning_effort, t.token_usage, t.status, t.created_at
99933
+ FROM turns t JOIN sessions s ON s.id = t.session_id WHERE ${where}`
99934
+ ).all(...params);
99935
+ for (const row of rows) {
99936
+ const day = row.created_at.slice(0, 10);
99937
+ addEvent(agg, {
99938
+ runtimeType: row.runtime_type,
99939
+ provider: null,
99940
+ model: row.model ?? "(default)",
99941
+ effort: row.model_reasoning_effort ?? null,
99942
+ day,
99943
+ isError: row.status === "failed",
99944
+ usage: parseTokenUsage(row.token_usage),
99945
+ // Anthropic reports input/cache-read/cache-write as disjoint buckets.
99946
+ inputIncludesCached: row.runtime_type !== "claude"
99947
+ });
99948
+ }
99949
+ }
99950
+ function addRepoburgExport(agg, sinceIso, untilIso) {
99951
+ let files;
99952
+ try {
99953
+ files = readdirSync2(usageFilesDir()).filter((f) => /^usage-\d{4}-\d{2}-\d{2}\.jsonl$/.test(f)).sort();
99954
+ } catch {
99955
+ return;
99956
+ }
99957
+ const sinceDay = sinceIso?.slice(0, 10) ?? null;
99958
+ const untilDay = untilIso ? untilIso.slice(0, 10) : null;
99959
+ for (const file of files) {
99960
+ const day = file.slice("usage-".length, file.length - ".jsonl".length);
99961
+ if (sinceDay && day < sinceDay) continue;
99962
+ if (untilDay && day > untilDay) continue;
99963
+ let content;
99964
+ try {
99965
+ content = readFileSync2(join3(usageFilesDir(), file), "utf-8");
99966
+ } catch {
99967
+ continue;
99968
+ }
99969
+ for (const line of content.split("\n")) {
99970
+ const trimmed = line.trim();
99971
+ if (!trimmed) continue;
99972
+ let record;
99973
+ try {
99974
+ record = JSON.parse(trimmed);
99975
+ } catch {
99976
+ continue;
99977
+ }
99978
+ if (sinceIso && record.ts < sinceIso) continue;
99979
+ if (untilIso && record.ts >= untilIso) continue;
99980
+ const status = record.status_code;
99981
+ addEvent(agg, {
99982
+ runtimeType: "repoburg",
99983
+ provider: record.provider ?? null,
99984
+ model: record.model_id ?? "(unknown)",
99985
+ effort: record.reasoning_effort ?? null,
99986
+ day: record.ts.slice(0, 10),
99987
+ isError: status === null || status < 200 || status >= 300,
99988
+ usage: {
99989
+ inputTokens: num(record.input_tokens),
99990
+ cachedInputTokens: num(record.cached_tokens),
99991
+ cacheWriteInputTokens: 0,
99992
+ outputTokens: num(record.output_tokens)
99993
+ },
99994
+ // OpenAI-compatible providers report cached tokens inside input.
99995
+ inputIncludesCached: true
99996
+ });
99997
+ }
99998
+ }
99999
+ }
100000
+ function getUsageSummary(sinceIso, untilIso) {
100001
+ const agg = newAggregator();
100002
+ addPaneTurns(agg, sinceIso, untilIso);
100003
+ addRepoburgExport(agg, sinceIso, untilIso);
100004
+ const byRuntime = [...agg.byRuntime.entries()].map(([runtimeType, b]) => ({
100005
+ runtime_type: runtimeType,
100006
+ calls: b.calls,
100007
+ errors: b.errors,
100008
+ input_tokens: b.input,
100009
+ cached_input_tokens: b.cached,
100010
+ cache_write_input_tokens: b.cacheWrite,
100011
+ output_tokens: b.output,
100012
+ cost_usd: b.cost
100013
+ })).sort((a, b) => a.runtime_type.localeCompare(b.runtime_type));
100014
+ const byModel = [...agg.byModel.values()].map((row) => ({
100015
+ runtime_type: row.runtime_type,
100016
+ provider: row.provider,
100017
+ model: row.model,
100018
+ reasoning_effort: row.reasoning_effort,
100019
+ calls: row.calls,
100020
+ errors: row.errors,
100021
+ input_tokens: row.input_tokens,
100022
+ cached_input_tokens: row.cached_input_tokens,
100023
+ cache_write_input_tokens: row.cache_write_input_tokens,
100024
+ output_tokens: row.output_tokens,
100025
+ // Unpriced models stay null; a priced row that also saw zero-token
100026
+ // error events keeps its exact sum.
100027
+ cost_usd: row.priced ? row.cost_usd ?? 0 : null
100028
+ })).sort((a, b) => (b.cost_usd ?? 0) - (a.cost_usd ?? 0) || b.calls - a.calls);
100029
+ const daily = [...agg.byDay.entries()].map(([date, b]) => ({
100030
+ date,
100031
+ calls: b.calls,
100032
+ input_tokens: b.input,
100033
+ cached_input_tokens: b.cached,
100034
+ cache_write_input_tokens: b.cacheWrite,
100035
+ output_tokens: b.output,
100036
+ cost_usd: b.cost
100037
+ })).sort((a, b) => a.date.localeCompare(b.date));
100038
+ return {
100039
+ window: { since: sinceIso, until: untilIso },
100040
+ totals: {
100041
+ calls: agg.totals.calls,
100042
+ errors: agg.totals.errors,
100043
+ input_tokens: agg.totals.input,
100044
+ cached_input_tokens: agg.totals.cached,
100045
+ cache_write_input_tokens: agg.totals.cacheWrite,
100046
+ output_tokens: agg.totals.output,
100047
+ total_tokens: agg.totals.input + agg.totals.cached + agg.totals.cacheWrite + agg.totals.output,
100048
+ cost_usd: agg.totals.cost,
100049
+ unpriced_models: [...agg.unpricedModels].sort()
100050
+ },
100051
+ by_runtime: byRuntime,
100052
+ by_model: byModel,
100053
+ daily
100054
+ };
100055
+ }
100056
+
100057
+ // src/routes/usage.ts
100058
+ function parseTimeParam(value) {
100059
+ if (typeof value !== "string" || value === "") return null;
100060
+ if (Number.isNaN(Date.parse(value))) {
100061
+ throw Object.assign(new Error(`Invalid timestamp: ${value}`), { statusCode: 400 });
100062
+ }
100063
+ return value;
100064
+ }
100065
+ async function registerUsageRoutes(fastify) {
100066
+ fastify.get(
100067
+ "/api/usage/summary",
100068
+ {
100069
+ schema: {
100070
+ querystring: {
100071
+ type: "object",
100072
+ properties: {
100073
+ since: { type: "string" },
100074
+ until: { type: "string" }
100075
+ }
100076
+ }
100077
+ }
100078
+ },
100079
+ async (request, reply) => {
100080
+ try {
100081
+ const since = parseTimeParam(request.query?.since);
100082
+ const until = parseTimeParam(request.query?.until);
100083
+ return getUsageSummary(since, until);
100084
+ } catch (err) {
100085
+ const e = err;
100086
+ if (e.statusCode === 400) {
100087
+ return reply.code(400).send({ error: e.message });
100088
+ }
100089
+ throw err;
100090
+ }
100091
+ }
100092
+ );
100093
+ }
100094
+
99751
100095
  // src/codex/item-mapper.ts
99752
100096
  function mapItemData(item) {
99753
100097
  switch (item.type) {
@@ -100512,9 +100856,9 @@ var codexProvider = {
100512
100856
 
100513
100857
  // src/claude/claude-provider.ts
100514
100858
  import { query, forkSession as sdkForkSession } from "@anthropic-ai/claude-agent-sdk";
100515
- import { readFileSync as readFileSync2 } from "fs";
100859
+ import { readFileSync as readFileSync3 } from "fs";
100516
100860
  import { tmpdir } from "os";
100517
- import { join as join3 } from "path";
100861
+ import { join as join4 } from "path";
100518
100862
 
100519
100863
  // src/claude/claude-message-mapper.ts
100520
100864
  function mapAssistantContent(content, messageId, thinkingTexts) {
@@ -101034,7 +101378,8 @@ function handleLoopError(entry, sessionId, errMsg, db2) {
101034
101378
  turn_id: row.turn_id,
101035
101379
  external_item_id: row.external_item_id,
101036
101380
  type: row.type,
101037
- data: row.data ? JSON.parse(row.data) : {},
101381
+ // MUH-25 — re-broadcasts ship trimmed payloads too.
101382
+ data: row.data ? trimItemData(JSON.parse(row.data)) : {},
101038
101383
  status: "failed",
101039
101384
  created_at: row.created_at,
101040
101385
  updated_at: now
@@ -101455,7 +101800,7 @@ function constructMonitorOutputPath(entry, taskId) {
101455
101800
  if (!entry.sessionId) return "";
101456
101801
  const uid = typeof process.getuid === "function" ? process.getuid() : 0;
101457
101802
  const encodedCwd = entry.cwd.replace(/\//g, "-");
101458
- return join3(tmpdir(), `claude-${uid}`, encodedCwd, entry.sessionId, "tasks", `${taskId}.output`);
101803
+ return join4(tmpdir(), `claude-${uid}`, encodedCwd, entry.sessionId, "tasks", `${taskId}.output`);
101459
101804
  }
101460
101805
  function handleTaskStarted(message, sessionId, turnId, entry, db2) {
101461
101806
  const m = message;
@@ -101498,7 +101843,7 @@ function handleTaskProgress(message, sessionId, turnId, entry, db2) {
101498
101843
  const outputPath = existing.output_file || constructMonitorOutputPath(entry, m.task_id);
101499
101844
  if (outputPath) {
101500
101845
  try {
101501
- output = readFileSync2(outputPath, "utf-8");
101846
+ output = readFileSync3(outputPath, "utf-8");
101502
101847
  } catch {
101503
101848
  }
101504
101849
  }
@@ -101547,13 +101892,11 @@ function handleTaskNotification(message, sessionId, turnId, entry, db2) {
101547
101892
  if (!row) return;
101548
101893
  const existing = row.data ? JSON.parse(row.data) : {};
101549
101894
  const taskType = existing.task_type ?? "unknown";
101550
- const sdkOutputFile = m.output_file || null;
101551
- const constructedPath = taskType === "local_bash" ? constructMonitorOutputPath(entry, m.task_id) : "";
101552
- const outputPath = sdkOutputFile || constructedPath;
101895
+ const outputPath = taskType === "local_bash" ? m.output_file || constructMonitorOutputPath(entry, m.task_id) : null;
101553
101896
  let output = "";
101554
101897
  if (outputPath) {
101555
101898
  try {
101556
- output = readFileSync2(outputPath, "utf-8");
101899
+ output = readFileSync3(outputPath, "utf-8");
101557
101900
  } catch {
101558
101901
  }
101559
101902
  }
@@ -101561,7 +101904,7 @@ function handleTaskNotification(message, sessionId, turnId, entry, db2) {
101561
101904
  ...existing,
101562
101905
  status: m.status,
101563
101906
  summary: m.summary,
101564
- output_file: outputPath,
101907
+ output_file: m.output_file || outputPath || existing.output_file || null,
101565
101908
  output,
101566
101909
  token_count: m.usage?.total_tokens ?? existing.token_count ?? null
101567
101910
  };
@@ -101632,7 +101975,8 @@ function completeTurn(db2, sessionId, turnId, usage) {
101632
101975
  }
101633
101976
  function upsertItem(db2, desc, sessionId, turnId, status, broadcastEvent) {
101634
101977
  const now = (/* @__PURE__ */ new Date()).toISOString();
101635
- const result = db2.prepare("UPDATE items SET data = ?, status = ?, updated_at = ? WHERE external_item_id = ? AND turn_id = ?").run(JSON.stringify(desc.data), status, now, desc.externalId, turnId);
101978
+ const data = trimItemData(desc.data);
101979
+ const result = db2.prepare("UPDATE items SET data = ?, status = ?, updated_at = ? WHERE external_item_id = ? AND turn_id = ?").run(JSON.stringify(data), status, now, desc.externalId, turnId);
101636
101980
  let itemId;
101637
101981
  let createdAt;
101638
101982
  if (result.changes === 0) {
@@ -101640,7 +101984,7 @@ function upsertItem(db2, desc, sessionId, turnId, status, broadcastEvent) {
101640
101984
  createdAt = now;
101641
101985
  db2.prepare(
101642
101986
  "INSERT INTO items (id, turn_id, external_item_id, type, data, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
101643
- ).run(itemId, turnId, desc.externalId, desc.type, JSON.stringify(desc.data), status, now, now);
101987
+ ).run(itemId, turnId, desc.externalId, desc.type, JSON.stringify(data), status, now, now);
101644
101988
  } else {
101645
101989
  const row = db2.prepare("SELECT id, created_at FROM items WHERE external_item_id = ? AND turn_id = ?").get(desc.externalId, turnId);
101646
101990
  itemId = row.id;
@@ -101651,7 +101995,7 @@ function upsertItem(db2, desc, sessionId, turnId, status, broadcastEvent) {
101651
101995
  turn_id: turnId,
101652
101996
  external_item_id: desc.externalId,
101653
101997
  type: desc.type,
101654
- data: desc.data,
101998
+ data,
101655
101999
  status,
101656
102000
  created_at: createdAt,
101657
102001
  updated_at: now
@@ -103638,6 +103982,7 @@ async function createAgentPaneApp(dbPath) {
103638
103982
  await registerVscodeWebRoutes(fastify);
103639
103983
  await registerAppRoutes(fastify);
103640
103984
  await registerSidecarRoutes(fastify);
103985
+ await registerUsageRoutes(fastify);
103641
103986
  const wss2 = initWs();
103642
103987
  wss2.on("connection", (ws) => {
103643
103988
  ws.send(JSON.stringify({ event: "background_tasks:snapshot", data: { sessions: listAllBackgroundTasks() } }));