@link-assistant/agent 0.16.14 → 0.16.16

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.16.14",
3
+ "version": "0.16.16",
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",
@@ -1272,22 +1272,26 @@ export namespace Provider {
1272
1272
  }
1273
1273
  }
1274
1274
 
1275
- log.info(() => ({
1276
- message: 'HTTP request',
1275
+ // Use direct (non-lazy) logging for HTTP request/response to ensure output
1276
+ // is not lost when piped through external process wrappers (e.g., solve.mjs).
1277
+ // The verbose check is already done above, so lazy evaluation is not needed here.
1278
+ // See: https://github.com/link-assistant/agent/issues/211
1279
+ log.info('HTTP request', {
1277
1280
  providerID: provider.id,
1278
1281
  method,
1279
1282
  url,
1280
1283
  headers: sanitizedHeaders,
1281
1284
  bodyPreview,
1282
- }));
1285
+ });
1283
1286
 
1284
1287
  const startMs = Date.now();
1285
1288
  try {
1286
1289
  const response = await innerFetch(input, init);
1287
1290
  const durationMs = Date.now() - startMs;
1288
1291
 
1289
- log.info(() => ({
1290
- message: 'HTTP response',
1292
+ // Use direct (non-lazy) logging to ensure HTTP response details are captured
1293
+ // See: https://github.com/link-assistant/agent/issues/211
1294
+ log.info('HTTP response', {
1291
1295
  providerID: provider.id,
1292
1296
  method,
1293
1297
  url,
@@ -1295,7 +1299,7 @@ export namespace Provider {
1295
1299
  statusText: response.statusText,
1296
1300
  durationMs,
1297
1301
  responseHeaders: Object.fromEntries(response.headers.entries()),
1298
- }));
1302
+ });
1299
1303
 
1300
1304
  // Log response body for debugging provider failures
1301
1305
  // For streaming responses (SSE/event-stream), tee() the stream so the AI SDK
@@ -1335,14 +1339,15 @@ export namespace Provider {
1335
1339
  }
1336
1340
  }
1337
1341
  }
1338
- log.info(() => ({
1339
- message: 'HTTP response body (stream)',
1342
+ // Use direct (non-lazy) logging for stream body
1343
+ // See: https://github.com/link-assistant/agent/issues/211
1344
+ log.info('HTTP response body (stream)', {
1340
1345
  providerID: provider.id,
1341
1346
  url,
1342
1347
  bodyPreview: truncated
1343
1348
  ? bodyPreview + `... [truncated]`
1344
1349
  : bodyPreview,
1345
- }));
1350
+ });
1346
1351
  } catch {
1347
1352
  // Ignore logging errors — do not affect the SDK stream
1348
1353
  }
@@ -1362,12 +1367,13 @@ export namespace Provider {
1362
1367
  ? bodyText.slice(0, responseBodyMaxChars) +
1363
1368
  `... [truncated, total ${bodyText.length} chars]`
1364
1369
  : bodyText;
1365
- log.info(() => ({
1366
- message: 'HTTP response body',
1370
+ // Use direct (non-lazy) logging for non-streaming body
1371
+ // See: https://github.com/link-assistant/agent/issues/211
1372
+ log.info('HTTP response body', {
1367
1373
  providerID: provider.id,
1368
1374
  url,
1369
1375
  bodyPreview,
1370
- }));
1376
+ });
1371
1377
  return new Response(bodyText, {
1372
1378
  status: response.status,
1373
1379
  statusText: response.statusText,
@@ -1379,8 +1385,9 @@ export namespace Provider {
1379
1385
  return response;
1380
1386
  } catch (error) {
1381
1387
  const durationMs = Date.now() - startMs;
1382
- log.error(() => ({
1383
- message: 'HTTP request failed',
1388
+ // Use direct (non-lazy) logging for error path
1389
+ // See: https://github.com/link-assistant/agent/issues/211
1390
+ log.error('HTTP request failed', {
1384
1391
  providerID: provider.id,
1385
1392
  method,
1386
1393
  url,
@@ -1389,7 +1396,7 @@ export namespace Provider {
1389
1396
  error instanceof Error
1390
1397
  ? { name: error.name, message: error.message }
1391
1398
  : String(error),
1392
- }));
1399
+ });
1393
1400
  throw error;
1394
1401
  }
1395
1402
  };
@@ -630,9 +630,14 @@ export namespace Session {
630
630
  }
631
631
  | undefined;
632
632
 
633
+ // Check if standard usage has valid data (inputTokens or outputTokens defined)
634
+ // Also check for zero-valued tokens (some providers set them to 0 instead of undefined)
633
635
  const standardUsageIsEmpty =
634
- input.usage.inputTokens === undefined &&
635
- input.usage.outputTokens === undefined;
636
+ (input.usage.inputTokens === undefined &&
637
+ input.usage.outputTokens === undefined) ||
638
+ (input.usage.inputTokens === 0 &&
639
+ input.usage.outputTokens === 0 &&
640
+ !input.usage.totalTokens);
636
641
 
637
642
  // If standard usage is empty but openrouter metadata has usage, use it as source
638
643
  let effectiveUsage = input.usage;
@@ -658,6 +663,41 @@ export namespace Session {
658
663
  };
659
664
  }
660
665
 
666
+ // If still empty, try providerMetadata.anthropic.usage as fallback
667
+ // Some providers (e.g., opencode using @ai-sdk/anthropic) return usage in
668
+ // Anthropic-specific metadata with snake_case keys (input_tokens, output_tokens)
669
+ // while the standard AI SDK usage object remains empty.
670
+ // See: https://github.com/link-assistant/agent/issues/211
671
+ if (standardUsageIsEmpty && !openrouterUsage) {
672
+ const anthropicUsage = input.metadata?.['anthropic']?.['usage'] as
673
+ | {
674
+ input_tokens?: number;
675
+ output_tokens?: number;
676
+ cache_creation_input_tokens?: number;
677
+ cache_read_input_tokens?: number;
678
+ }
679
+ | undefined;
680
+
681
+ if (
682
+ anthropicUsage &&
683
+ (anthropicUsage.input_tokens || anthropicUsage.output_tokens)
684
+ ) {
685
+ if (Flag.OPENCODE_VERBOSE) {
686
+ log.debug(() => ({
687
+ message:
688
+ 'Standard usage empty, falling back to anthropic provider metadata',
689
+ anthropicUsage: JSON.stringify(anthropicUsage),
690
+ }));
691
+ }
692
+ effectiveUsage = {
693
+ ...input.usage,
694
+ inputTokens: anthropicUsage.input_tokens ?? 0,
695
+ outputTokens: anthropicUsage.output_tokens ?? 0,
696
+ cachedInputTokens: anthropicUsage.cache_read_input_tokens ?? 0,
697
+ };
698
+ }
699
+ }
700
+
661
701
  // Extract top-level cachedInputTokens
662
702
  const topLevelCachedInputTokens = safeNum(
663
703
  toNumber(effectiveUsage.cachedInputTokens, 'cachedInputTokens')