@link-assistant/agent 0.8.6 → 0.8.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/agent",
3
- "version": "0.8.6",
3
+ "version": "0.8.9",
4
4
  "description": "A minimal, public domain AI CLI agent compatible with OpenCode's JSON interface. Bun-only runtime.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -408,9 +408,18 @@ export namespace Session {
408
408
  }
409
409
 
410
410
  try {
411
- // Handle undefined/null explicitly - Number() would convert these to 0 or NaN
411
+ // Handle undefined/null gracefully by returning 0
412
+ // These are expected for optional fields like cachedInputTokens, reasoningTokens
413
+ // See: https://github.com/link-assistant/agent/issues/127
412
414
  if (value === undefined || value === null) {
413
- throw new Error(`Cannot convert ${value} to number`);
415
+ if (Flag.OPENCODE_VERBOSE) {
416
+ log.debug(() => ({
417
+ message: 'toNumber received undefined/null, returning 0',
418
+ context,
419
+ valueType: typeof value,
420
+ }));
421
+ }
422
+ return 0;
414
423
  }
415
424
 
416
425
  // Handle objects with a 'total' field (e.g., { total: 8707, noCache: 6339, cacheRead: 2368 })
@@ -538,6 +547,18 @@ export namespace Session {
538
547
  return obj.reason;
539
548
  }
540
549
 
550
+ // Handle AI SDK unified/raw format: {unified: "tool-calls", raw: "tool_calls"}
551
+ // See: https://github.com/link-assistant/agent/issues/129
552
+ if (typeof obj.unified === 'string') {
553
+ if (Flag.OPENCODE_VERBOSE) {
554
+ log.debug(() => ({
555
+ message: 'toFinishReason extracted unified from object',
556
+ result: obj.unified,
557
+ }));
558
+ }
559
+ return obj.unified;
560
+ }
561
+
541
562
  // If we can't extract a specific field, return JSON representation
542
563
  if (Flag.OPENCODE_VERBOSE) {
543
564
  log.debug(() => ({
@@ -572,9 +593,28 @@ export namespace Session {
572
593
  const safeNum = (n: number): number =>
573
594
  Number.isNaN(n) || !Number.isFinite(n) ? 0 : n;
574
595
 
575
- const cachedInputTokens = safeNum(
596
+ // Extract top-level cachedInputTokens
597
+ const topLevelCachedInputTokens = safeNum(
576
598
  toNumber(input.usage.cachedInputTokens, 'cachedInputTokens')
577
599
  );
600
+
601
+ // Some providers (e.g., opencode/grok-code) nest cacheRead inside inputTokens object
602
+ // e.g., inputTokens: { total: 12703, noCache: 12511, cacheRead: 192 }
603
+ // See: https://github.com/link-assistant/agent/issues/127
604
+ const inputTokensObj = input.usage.inputTokens;
605
+ const nestedCacheRead =
606
+ typeof inputTokensObj === 'object' && inputTokensObj !== null
607
+ ? safeNum(
608
+ toNumber(
609
+ (inputTokensObj as { cacheRead?: unknown }).cacheRead,
610
+ 'inputTokens.cacheRead'
611
+ )
612
+ )
613
+ : 0;
614
+
615
+ // Use top-level if available, otherwise fall back to nested
616
+ const cachedInputTokens = topLevelCachedInputTokens || nestedCacheRead;
617
+
578
618
  const excludesCachedTokens = !!(
579
619
  input.metadata?.['anthropic'] || input.metadata?.['bedrock']
580
620
  );
@@ -595,12 +635,28 @@ export namespace Session {
595
635
  )
596
636
  );
597
637
 
638
+ // Extract reasoning tokens - some providers nest it inside outputTokens
639
+ // e.g., outputTokens: { total: 562, text: -805, reasoning: 1367 }
640
+ // See: https://github.com/link-assistant/agent/issues/127
641
+ const topLevelReasoningTokens = safeNum(
642
+ toNumber(input.usage?.reasoningTokens, 'reasoningTokens')
643
+ );
644
+ const outputTokensObj = input.usage.outputTokens;
645
+ const nestedReasoning =
646
+ typeof outputTokensObj === 'object' && outputTokensObj !== null
647
+ ? safeNum(
648
+ toNumber(
649
+ (outputTokensObj as { reasoning?: unknown }).reasoning,
650
+ 'outputTokens.reasoning'
651
+ )
652
+ )
653
+ : 0;
654
+ const reasoningTokens = topLevelReasoningTokens || nestedReasoning;
655
+
598
656
  const tokens = {
599
657
  input: Math.max(0, adjustedInputTokens), // Ensure non-negative
600
658
  output: safeNum(toNumber(input.usage.outputTokens, 'outputTokens')),
601
- reasoning: safeNum(
602
- toNumber(input.usage?.reasoningTokens, 'reasoningTokens')
603
- ),
659
+ reasoning: reasoningTokens,
604
660
  cache: {
605
661
  write: cacheWriteTokens,
606
662
  read: cachedInputTokens,