@dexto/tui 1.6.21 → 1.6.24

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.
@@ -48,6 +48,15 @@ function buildErrorContent(error, prefix) {
48
48
  }
49
49
  return errorContent;
50
50
  }
51
+ function hasMeaningfulTokenUsageForAnalytics(tokenUsage, estimatedCost) {
52
+ if (estimatedCost !== void 0) {
53
+ return true;
54
+ }
55
+ if (!tokenUsage) {
56
+ return false;
57
+ }
58
+ return (tokenUsage.inputTokens ?? 0) > 0 || (tokenUsage.outputTokens ?? 0) > 0 || (tokenUsage.reasoningTokens ?? 0) > 0 || (tokenUsage.cacheReadTokens ?? 0) > 0 || (tokenUsage.cacheWriteTokens ?? 0) > 0 || (tokenUsage.totalTokens ?? 0) > 0;
59
+ }
51
60
  async function processStream(iterator, setters, options) {
52
61
  const {
53
62
  setMessages,
@@ -323,13 +332,12 @@ async function processStream(iterator, setters, options) {
323
332
  state.cumulativeOutputTokens += event.tokenUsage.outputTokens;
324
333
  }
325
334
  }
326
- if (event.tokenUsage && (event.tokenUsage.inputTokens || event.tokenUsage.outputTokens)) {
335
+ if (hasMeaningfulTokenUsageForAnalytics(event.tokenUsage, event.estimatedCost)) {
327
336
  let estimateAccuracyPercent;
328
- if (event.estimatedInputTokens !== void 0 && event.tokenUsage.inputTokens) {
329
- const diff = event.estimatedInputTokens - event.tokenUsage.inputTokens;
330
- estimateAccuracyPercent = Math.round(
331
- diff / event.tokenUsage.inputTokens * 100
332
- );
337
+ const actualInputTokens = event.tokenUsage?.inputTokens;
338
+ if (event.estimatedInputTokens !== void 0 && actualInputTokens) {
339
+ const diff = event.estimatedInputTokens - actualInputTokens;
340
+ estimateAccuracyPercent = Math.round(diff / actualInputTokens * 100);
333
341
  }
334
342
  (0, import_host.captureAnalytics)("dexto_llm_tokens_consumed", {
335
343
  source: "cli",
@@ -338,12 +346,18 @@ async function processStream(iterator, setters, options) {
338
346
  model: event.model,
339
347
  reasoningVariant: event.reasoningVariant ?? void 0,
340
348
  reasoningBudgetTokens: event.reasoningBudgetTokens ?? void 0,
341
- inputTokens: event.tokenUsage.inputTokens,
342
- outputTokens: event.tokenUsage.outputTokens,
343
- reasoningTokens: event.tokenUsage.reasoningTokens,
344
- totalTokens: event.tokenUsage.totalTokens,
345
- cacheReadTokens: event.tokenUsage.cacheReadTokens,
346
- cacheWriteTokens: event.tokenUsage.cacheWriteTokens,
349
+ inputTokens: event.tokenUsage?.inputTokens,
350
+ outputTokens: event.tokenUsage?.outputTokens,
351
+ reasoningTokens: event.tokenUsage?.reasoningTokens,
352
+ totalTokens: event.tokenUsage?.totalTokens,
353
+ cacheReadTokens: event.tokenUsage?.cacheReadTokens,
354
+ cacheWriteTokens: event.tokenUsage?.cacheWriteTokens,
355
+ estimatedCostUsd: event.estimatedCost,
356
+ inputCostUsd: event.costBreakdown?.inputUsd,
357
+ outputCostUsd: event.costBreakdown?.outputUsd,
358
+ reasoningCostUsd: event.costBreakdown?.reasoningUsd,
359
+ cacheReadCostUsd: event.costBreakdown?.cacheReadUsd,
360
+ cacheWriteCostUsd: event.costBreakdown?.cacheWriteUsd,
347
361
  estimatedInputTokens: event.estimatedInputTokens,
348
362
  estimateAccuracyPercent
349
363
  });
@@ -1 +1 @@
1
- {"version":3,"file":"processStream.d.ts","sourceRoot":"","sources":["../../src/services/processStream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAC;AAGvE,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAc,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAwBvE;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,2DAA2D;IAC3D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,oFAAoF;IACpF,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACpE,iFAAiF;IACjF,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACnE,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,kEAAkE;IAClE,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,mBAAmB,EAAE,YAAY,CAAC,CAAC,CAAC;IAC3F,yDAAyD;IACzD,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,aAAa,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;IAC/F,4DAA4D;IAC5D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,uDAAuD;IACvD,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;CAC7E;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,2FAA2F;IAC3F,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2FAA2F;IAC3F,mBAAmB,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAC1C,wFAAwF;IACxF,oBAAoB,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3C,yDAAyD;IACzD,QAAQ,EAAE,IAAI,CAAC,OAAO,aAAa,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;IAC5D,8DAA8D;IAC9D,YAAY,CAAC,EAAE,OAAO,+BAA+B,EAAE,wBAAwB,CAAC;IAChF,kEAAkE;IAClE,QAAQ,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,mBAAmB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;CAC3F;AA8BD;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAC/B,QAAQ,EAAE,qBAAqB,CAAC,cAAc,CAAC,EAC/C,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,oBAAoB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA6qCf"}
1
+ {"version":3,"file":"processStream.d.ts","sourceRoot":"","sources":["../../src/services/processStream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAC;AAGvE,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAc,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAwBvE;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,2DAA2D;IAC3D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,oFAAoF;IACpF,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACpE,iFAAiF;IACjF,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACnE,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,kEAAkE;IAClE,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,mBAAmB,EAAE,YAAY,CAAC,CAAC,CAAC;IAC3F,yDAAyD;IACzD,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,aAAa,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;IAC/F,4DAA4D;IAC5D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,uDAAuD;IACvD,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;CAC7E;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,2FAA2F;IAC3F,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2FAA2F;IAC3F,mBAAmB,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAC1C,wFAAwF;IACxF,oBAAoB,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3C,yDAAyD;IACzD,QAAQ,EAAE,IAAI,CAAC,OAAO,aAAa,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;IAC5D,8DAA8D;IAC9D,YAAY,CAAC,EAAE,OAAO,+BAA+B,EAAE,wBAAwB,CAAC;IAChF,kEAAkE;IAClE,QAAQ,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,mBAAmB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;CAC3F;AAoDD;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAC/B,QAAQ,EAAE,qBAAqB,CAAC,cAAc,CAAC,EAC/C,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,oBAAoB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA8qCf"}
@@ -15,6 +15,15 @@ function buildErrorContent(error, prefix) {
15
15
  }
16
16
  return errorContent;
17
17
  }
18
+ function hasMeaningfulTokenUsageForAnalytics(tokenUsage, estimatedCost) {
19
+ if (estimatedCost !== void 0) {
20
+ return true;
21
+ }
22
+ if (!tokenUsage) {
23
+ return false;
24
+ }
25
+ return (tokenUsage.inputTokens ?? 0) > 0 || (tokenUsage.outputTokens ?? 0) > 0 || (tokenUsage.reasoningTokens ?? 0) > 0 || (tokenUsage.cacheReadTokens ?? 0) > 0 || (tokenUsage.cacheWriteTokens ?? 0) > 0 || (tokenUsage.totalTokens ?? 0) > 0;
26
+ }
18
27
  async function processStream(iterator, setters, options) {
19
28
  const {
20
29
  setMessages,
@@ -290,13 +299,12 @@ async function processStream(iterator, setters, options) {
290
299
  state.cumulativeOutputTokens += event.tokenUsage.outputTokens;
291
300
  }
292
301
  }
293
- if (event.tokenUsage && (event.tokenUsage.inputTokens || event.tokenUsage.outputTokens)) {
302
+ if (hasMeaningfulTokenUsageForAnalytics(event.tokenUsage, event.estimatedCost)) {
294
303
  let estimateAccuracyPercent;
295
- if (event.estimatedInputTokens !== void 0 && event.tokenUsage.inputTokens) {
296
- const diff = event.estimatedInputTokens - event.tokenUsage.inputTokens;
297
- estimateAccuracyPercent = Math.round(
298
- diff / event.tokenUsage.inputTokens * 100
299
- );
304
+ const actualInputTokens = event.tokenUsage?.inputTokens;
305
+ if (event.estimatedInputTokens !== void 0 && actualInputTokens) {
306
+ const diff = event.estimatedInputTokens - actualInputTokens;
307
+ estimateAccuracyPercent = Math.round(diff / actualInputTokens * 100);
300
308
  }
301
309
  captureAnalytics("dexto_llm_tokens_consumed", {
302
310
  source: "cli",
@@ -305,12 +313,18 @@ async function processStream(iterator, setters, options) {
305
313
  model: event.model,
306
314
  reasoningVariant: event.reasoningVariant ?? void 0,
307
315
  reasoningBudgetTokens: event.reasoningBudgetTokens ?? void 0,
308
- inputTokens: event.tokenUsage.inputTokens,
309
- outputTokens: event.tokenUsage.outputTokens,
310
- reasoningTokens: event.tokenUsage.reasoningTokens,
311
- totalTokens: event.tokenUsage.totalTokens,
312
- cacheReadTokens: event.tokenUsage.cacheReadTokens,
313
- cacheWriteTokens: event.tokenUsage.cacheWriteTokens,
316
+ inputTokens: event.tokenUsage?.inputTokens,
317
+ outputTokens: event.tokenUsage?.outputTokens,
318
+ reasoningTokens: event.tokenUsage?.reasoningTokens,
319
+ totalTokens: event.tokenUsage?.totalTokens,
320
+ cacheReadTokens: event.tokenUsage?.cacheReadTokens,
321
+ cacheWriteTokens: event.tokenUsage?.cacheWriteTokens,
322
+ estimatedCostUsd: event.estimatedCost,
323
+ inputCostUsd: event.costBreakdown?.inputUsd,
324
+ outputCostUsd: event.costBreakdown?.outputUsd,
325
+ reasoningCostUsd: event.costBreakdown?.reasoningUsd,
326
+ cacheReadCostUsd: event.costBreakdown?.cacheReadUsd,
327
+ cacheWriteCostUsd: event.costBreakdown?.cacheWriteUsd,
314
328
  estimatedInputTokens: event.estimatedInputTokens,
315
329
  estimateAccuracyPercent
316
330
  });
@@ -1,6 +1,12 @@
1
1
  "use strict";
2
2
  var import_vitest = require("vitest");
3
3
  var import_processStream = require("./processStream.js");
4
+ const { captureAnalyticsMock } = import_vitest.vi.hoisted(() => ({
5
+ captureAnalyticsMock: import_vitest.vi.fn()
6
+ }));
7
+ import_vitest.vi.mock("../host/index.js", () => ({
8
+ captureAnalytics: captureAnalyticsMock
9
+ }));
4
10
  function isStateUpdater(action) {
5
11
  return typeof action === "function";
6
12
  }
@@ -77,6 +83,9 @@ function createSetters() {
77
83
  };
78
84
  }
79
85
  (0, import_vitest.describe)("processStream (reasoning)", () => {
86
+ (0, import_vitest.beforeEach)(() => {
87
+ captureAnalyticsMock.mockClear();
88
+ });
80
89
  (0, import_vitest.it)("attaches streamed reasoning chunks to the assistant message", async () => {
81
90
  const { getMessages, getPendingMessages, setters } = createSetters();
82
91
  const events = [
@@ -253,4 +262,57 @@ function createSetters() {
253
262
  (0, import_vitest.expect)(assistantMessages[1]?.content).toBe("Final");
254
263
  (0, import_vitest.expect)(assistantMessages[1]?.reasoning).toBeUndefined();
255
264
  });
265
+ (0, import_vitest.it)("captures analytics cost fields for priced llm responses", async () => {
266
+ const { setters } = createSetters();
267
+ const events = [
268
+ { name: "llm:thinking", sessionId: "test-session" },
269
+ {
270
+ name: "llm:response",
271
+ sessionId: "test-session",
272
+ content: "Priced response",
273
+ provider: "openai",
274
+ model: "gpt-4",
275
+ estimatedCost: 15e-4,
276
+ costBreakdown: {
277
+ inputUsd: 1e-3,
278
+ outputUsd: 5e-4,
279
+ reasoningUsd: 0,
280
+ cacheReadUsd: 0,
281
+ cacheWriteUsd: 0,
282
+ totalUsd: 15e-4
283
+ },
284
+ tokenUsage: {
285
+ inputTokens: 10,
286
+ outputTokens: 20,
287
+ totalTokens: 30
288
+ }
289
+ },
290
+ {
291
+ name: "run:complete",
292
+ sessionId: "test-session",
293
+ finishReason: "stop",
294
+ stepCount: 1,
295
+ durationMs: 1
296
+ }
297
+ ];
298
+ await (0, import_processStream.processStream)(eventStream(events), setters, {
299
+ useStreaming: false,
300
+ autoApproveEditsRef: { current: false },
301
+ bypassPermissionsRef: { current: false },
302
+ eventBus: { emit: import_vitest.vi.fn() }
303
+ });
304
+ (0, import_vitest.expect)(captureAnalyticsMock).toHaveBeenCalledWith(
305
+ "dexto_llm_tokens_consumed",
306
+ import_vitest.expect.objectContaining({
307
+ source: "cli",
308
+ sessionId: "test-session",
309
+ estimatedCostUsd: 15e-4,
310
+ inputCostUsd: 1e-3,
311
+ outputCostUsd: 5e-4,
312
+ reasoningCostUsd: 0,
313
+ cacheReadCostUsd: 0,
314
+ cacheWriteCostUsd: 0
315
+ })
316
+ );
317
+ });
256
318
  });
@@ -1,5 +1,11 @@
1
- import { describe, expect, it, vi } from "vitest";
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
2
  import { processStream } from "./processStream.js";
3
+ const { captureAnalyticsMock } = vi.hoisted(() => ({
4
+ captureAnalyticsMock: vi.fn()
5
+ }));
6
+ vi.mock("../host/index.js", () => ({
7
+ captureAnalytics: captureAnalyticsMock
8
+ }));
3
9
  function isStateUpdater(action) {
4
10
  return typeof action === "function";
5
11
  }
@@ -76,6 +82,9 @@ function createSetters() {
76
82
  };
77
83
  }
78
84
  describe("processStream (reasoning)", () => {
85
+ beforeEach(() => {
86
+ captureAnalyticsMock.mockClear();
87
+ });
79
88
  it("attaches streamed reasoning chunks to the assistant message", async () => {
80
89
  const { getMessages, getPendingMessages, setters } = createSetters();
81
90
  const events = [
@@ -252,4 +261,57 @@ describe("processStream (reasoning)", () => {
252
261
  expect(assistantMessages[1]?.content).toBe("Final");
253
262
  expect(assistantMessages[1]?.reasoning).toBeUndefined();
254
263
  });
264
+ it("captures analytics cost fields for priced llm responses", async () => {
265
+ const { setters } = createSetters();
266
+ const events = [
267
+ { name: "llm:thinking", sessionId: "test-session" },
268
+ {
269
+ name: "llm:response",
270
+ sessionId: "test-session",
271
+ content: "Priced response",
272
+ provider: "openai",
273
+ model: "gpt-4",
274
+ estimatedCost: 15e-4,
275
+ costBreakdown: {
276
+ inputUsd: 1e-3,
277
+ outputUsd: 5e-4,
278
+ reasoningUsd: 0,
279
+ cacheReadUsd: 0,
280
+ cacheWriteUsd: 0,
281
+ totalUsd: 15e-4
282
+ },
283
+ tokenUsage: {
284
+ inputTokens: 10,
285
+ outputTokens: 20,
286
+ totalTokens: 30
287
+ }
288
+ },
289
+ {
290
+ name: "run:complete",
291
+ sessionId: "test-session",
292
+ finishReason: "stop",
293
+ stepCount: 1,
294
+ durationMs: 1
295
+ }
296
+ ];
297
+ await processStream(eventStream(events), setters, {
298
+ useStreaming: false,
299
+ autoApproveEditsRef: { current: false },
300
+ bypassPermissionsRef: { current: false },
301
+ eventBus: { emit: vi.fn() }
302
+ });
303
+ expect(captureAnalyticsMock).toHaveBeenCalledWith(
304
+ "dexto_llm_tokens_consumed",
305
+ expect.objectContaining({
306
+ source: "cli",
307
+ sessionId: "test-session",
308
+ estimatedCostUsd: 15e-4,
309
+ inputCostUsd: 1e-3,
310
+ outputCostUsd: 5e-4,
311
+ reasoningCostUsd: 0,
312
+ cacheReadCostUsd: 0,
313
+ cacheWriteCostUsd: 0
314
+ })
315
+ );
316
+ });
255
317
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexto/tui",
3
- "version": "1.6.21",
3
+ "version": "1.6.24",
4
4
  "description": "Interactive terminal UI for Dexto CLI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -28,9 +28,9 @@
28
28
  "string-width": "^8.1.0",
29
29
  "strip-ansi": "^7.1.2",
30
30
  "wrap-ansi": "^9.0.2",
31
- "@dexto/agent-management": "1.6.21",
32
- "@dexto/core": "1.6.21",
33
- "@dexto/registry": "1.6.21"
31
+ "@dexto/agent-management": "1.6.24",
32
+ "@dexto/registry": "1.6.24",
33
+ "@dexto/core": "1.6.24"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/react": "^19.0.0",