@vietor/agent-core 0.7.4 → 0.7.5

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/README.md CHANGED
@@ -46,8 +46,10 @@ const session = await createSession({
46
46
 
47
47
  session.onEvent((e) => {
48
48
  if (e.type === "assistant_delta") process.stdout.write(e.text);
49
- else if (e.type === "run_metrics")
50
- console.log(`tokens: ${e.inputTokens} prompt / ${e.outputTokens} completion`);
49
+ else if (e.type === "run_metrics") {
50
+ const inputTokens = e.cacheInputTokens + e.missInputTokens;
51
+ console.log(`tokens: ${inputTokens} input / ${e.outputTokens} output`);
52
+ }
51
53
  });
52
54
 
53
55
  const result = await session.prompt("What files are in the current directory?");
@@ -219,7 +221,8 @@ interface RunMetrics {
219
221
  elapsed: number; // seconds since the current prompt started
220
222
  thinkingElapsed: number; // seconds before the first assistant text token (incl. thinking/tools)
221
223
  replyElapsed: number; // seconds after the first assistant text token (incl. later tool rounds)
222
- inputTokens: number; // cumulative input (prompt) tokens for the current run
224
+ cacheInputTokens: number; // cumulative cached input (prompt) tokens for the current run
225
+ missInputTokens: number; // cumulative non-cached input (prompt) tokens for the current run
223
226
  outputTokens: number; // cumulative output (completion) tokens for the current run
224
227
  }
225
228
  ```
@@ -44,7 +44,7 @@ export class AnthropicAdapter extends BaseAdapter {
44
44
  const stream = this.client.messages.stream(params, { signal: opts.signal });
45
45
  if (opts.onUsage)
46
46
  stream.on("streamEvent", (e) => { if (e.type === "message_start")
47
- opts.onUsage(e.message.usage.input_tokens, 0); });
47
+ opts.onUsage(0, e.message.usage.input_tokens, 0); });
48
48
  if (opts.onDelta)
49
49
  stream.on("text", (delta) => opts.onDelta(delta));
50
50
  if (opts.onThinking)
@@ -53,7 +53,8 @@ export class AnthropicAdapter extends BaseAdapter {
53
53
  stream.on("contentBlock", (block) => { if (block.type === "tool_use")
54
54
  opts.onToolCall(); });
55
55
  const final = await stream.finalMessage();
56
- opts.onUsage?.(final.usage.input_tokens, final.usage.output_tokens);
56
+ const cacheTokens = (final.usage.cache_read_input_tokens ?? 0) + (final.usage.cache_creation_input_tokens ?? 0);
57
+ opts.onUsage?.(cacheTokens, final.usage.input_tokens, final.usage.output_tokens);
57
58
  const thinking = [];
58
59
  let text = "";
59
60
  const toolCalls = [];
@@ -31,7 +31,8 @@ export class CompletionsAdapter extends BaseAdapter {
31
31
  const stream = await this.client.chat.completions.create(params, { signal });
32
32
  for await (const chunk of stream) {
33
33
  if (chunk.usage) {
34
- onUsage?.(chunk.usage.prompt_tokens ?? 0, chunk.usage.completion_tokens ?? 0);
34
+ const cached = chunk.usage.prompt_tokens_details?.cached_tokens ?? 0;
35
+ onUsage?.(cached, chunk.usage.prompt_tokens ?? 0, chunk.usage.completion_tokens ?? 0);
35
36
  }
36
37
  const delta = chunk.choices[0]?.delta;
37
38
  if (!delta)
@@ -59,7 +59,8 @@ export class ResponsesAdapter extends BaseAdapter {
59
59
  throw new Error(`Responses API error: ${detail}`);
60
60
  }
61
61
  if (finalResponse.usage) {
62
- onUsage?.(finalResponse.usage.input_tokens, finalResponse.usage.output_tokens);
62
+ const cacheTokens = finalResponse.usage.input_tokens_details.cached_tokens;
63
+ onUsage?.(cacheTokens, finalResponse.usage.input_tokens, finalResponse.usage.output_tokens);
63
64
  }
64
65
  const textParts = [];
65
66
  const toolCalls = [];
@@ -23,7 +23,7 @@ export interface ChatOptions {
23
23
  onDelta?: (text: string) => void;
24
24
  onThinking?: (text: string) => void;
25
25
  onRetry?: (attempt: number, max: number, error: unknown) => void;
26
- onUsage?: (inputTokens: number, outputTokens: number) => void;
26
+ onUsage?: (cacheInputTokens: number, missInputTokens: number, outputTokens: number) => void;
27
27
  onToolCall?: () => void;
28
28
  thinking?: boolean;
29
29
  signal?: AbortSignal;
@@ -31,15 +31,18 @@ export declare class Agent {
31
31
  private todoSnapshot;
32
32
  private resolveSkill?;
33
33
  private onCompact?;
34
- private inputTokens;
34
+ private cacheInputTokens;
35
+ private missInputTokens;
35
36
  private outputTokens;
36
37
  constructor(opts: AgentOptions);
37
38
  get contextTokens(): number;
38
39
  get usage(): {
39
- inputTokens: number;
40
+ cacheInputTokens: number;
41
+ missInputTokens: number;
40
42
  outputTokens: number;
41
43
  };
42
44
  resetUsage(): void;
45
+ addUsage(cacheInputTokens: number, missInputTokens: number, outputTokens: number): void;
43
46
  get model(): string;
44
47
  get thinkingEffort(): import("../llm/types.js").LLMThinkingEffort;
45
48
  clear(): void;
@@ -18,7 +18,8 @@ export class Agent {
18
18
  todoSnapshot = [];
19
19
  resolveSkill;
20
20
  onCompact;
21
- inputTokens = 0;
21
+ cacheInputTokens = 0;
22
+ missInputTokens = 0;
22
23
  outputTokens = 0;
23
24
  constructor(opts) {
24
25
  this.llm = opts.llm;
@@ -37,12 +38,18 @@ export class Agent {
37
38
  return this.conversation.getEstimatedTokens();
38
39
  }
39
40
  get usage() {
40
- return { inputTokens: this.inputTokens, outputTokens: this.outputTokens };
41
+ return { cacheInputTokens: this.cacheInputTokens, missInputTokens: this.missInputTokens, outputTokens: this.outputTokens };
41
42
  }
42
43
  resetUsage() {
43
- this.inputTokens = 0;
44
+ this.cacheInputTokens = 0;
45
+ this.missInputTokens = 0;
44
46
  this.outputTokens = 0;
45
47
  }
48
+ addUsage(cacheInputTokens, missInputTokens, outputTokens) {
49
+ this.cacheInputTokens += cacheInputTokens;
50
+ this.missInputTokens += missInputTokens;
51
+ this.outputTokens += outputTokens;
52
+ }
46
53
  get model() {
47
54
  return this.llm.model;
48
55
  }
@@ -207,6 +214,7 @@ export class Agent {
207
214
  }
208
215
  async chatOnce(opts, onAbort) {
209
216
  try {
217
+ let usage;
210
218
  const message = await withAbort(this.llm.chat({
211
219
  messages: opts.messages,
212
220
  tools: opts.tools,
@@ -214,12 +222,13 @@ export class Agent {
214
222
  onDelta: (text) => opts.onEvent?.({ type: "assistant_delta", text }),
215
223
  onThinking: (text) => opts.onEvent?.({ type: "thinking_delta", text }),
216
224
  onRetry: (attempt, max, error) => opts.onEvent?.({ type: "retry", attempt, max, reason: toErrorMessage(error) }),
217
- onUsage: (inputTokens, outputTokens) => {
218
- this.inputTokens = inputTokens;
219
- this.outputTokens = outputTokens;
225
+ onUsage: (cacheInputTokens, missInputTokens, outputTokens) => {
226
+ usage = { cacheInputTokens, missInputTokens, outputTokens };
220
227
  },
221
228
  signal: opts.signal,
222
229
  }), opts.signal);
230
+ if (usage)
231
+ this.addUsage(usage.cacheInputTokens, usage.missInputTokens, usage.outputTokens);
223
232
  return { ok: true, message };
224
233
  }
225
234
  catch (e) {
@@ -3,7 +3,8 @@ export interface RunMetrics {
3
3
  elapsed: number;
4
4
  thinkingElapsed: number;
5
5
  replyElapsed: number;
6
- inputTokens: number;
6
+ cacheInputTokens: number;
7
+ missInputTokens: number;
7
8
  outputTokens: number;
8
9
  }
9
10
  export declare const INITIAL_RUN_METRICS: RunMetrics;
@@ -1 +1 @@
1
- export const INITIAL_RUN_METRICS = { running: false, elapsed: 0, thinkingElapsed: 0, replyElapsed: 0, inputTokens: 0, outputTokens: 0 };
1
+ export const INITIAL_RUN_METRICS = { running: false, elapsed: 0, thinkingElapsed: 0, replyElapsed: 0, cacheInputTokens: 0, missInputTokens: 0, outputTokens: 0 };
@@ -211,6 +211,7 @@ export class Session {
211
211
  maxTurns: deps.maxTurns ?? DEFAULT_MAX_TURNS,
212
212
  stallThreshold: deps.stallThreshold ?? DEFAULT_STALL_THRESHOLD,
213
213
  contextLimit: deps.contextLimit,
214
+ onUsage: (cacheInputTokens, missInputTokens, outputTokens) => this.agent.addUsage(cacheInputTokens, missInputTokens, outputTokens),
214
215
  })(systemPrompt, task, signal),
215
216
  },
216
217
  });
@@ -9,6 +9,7 @@ export interface SubAgentRunOptions {
9
9
  maxTurns: number;
10
10
  stallThreshold: number;
11
11
  contextLimit: number;
12
+ onUsage?: (cacheInputTokens: number, missInputTokens: number, outputTokens: number) => void;
12
13
  }
13
14
  export interface SubAgentRunResult {
14
15
  status: RunStatus;
@@ -19,6 +19,7 @@ export function createSubAgentRunner(opts) {
19
19
  contextLimit: opts.contextLimit,
20
20
  });
21
21
  const status = await subAgent.run(task, undefined, signal);
22
+ opts.onUsage?.(subAgent.usage.cacheInputTokens, subAgent.usage.missInputTokens, subAgent.usage.outputTokens);
22
23
  const reply = conversation.lastAssistantText() || `(sub-agent produced no final text; status ${status})`;
23
24
  const messages = status !== "ok" ? conversation.export() : [];
24
25
  return { status, reply, messages };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vietor/agent-core",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",