@link-assistant/agent 0.16.10 → 0.16.12

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.10",
3
+ "version": "0.16.12",
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",
@@ -1201,15 +1201,23 @@ export namespace Provider {
1201
1201
  sessionID: provider.id,
1202
1202
  });
1203
1203
 
1204
- // Wrap fetch with verbose HTTP logging when --verbose is enabled
1205
- // This logs raw HTTP request/response details for debugging provider issues
1204
+ // Wrap fetch with verbose HTTP logging for debugging provider issues.
1205
+ // IMPORTANT: The verbose check is done at call time (not SDK creation time)
1206
+ // because the SDK is cached and Flag.OPENCODE_VERBOSE may change after creation.
1207
+ // When verbose is disabled, the wrapper is a no-op passthrough with negligible overhead.
1206
1208
  // See: https://github.com/link-assistant/agent/issues/200
1207
- if (Flag.OPENCODE_VERBOSE) {
1209
+ // See: https://github.com/link-assistant/agent/issues/206
1210
+ {
1208
1211
  const innerFetch = options['fetch'];
1209
1212
  options['fetch'] = async (
1210
1213
  input: RequestInfo | URL,
1211
1214
  init?: RequestInit
1212
1215
  ): Promise<Response> => {
1216
+ // Check verbose flag at call time — not at SDK creation time
1217
+ if (!Flag.OPENCODE_VERBOSE) {
1218
+ return innerFetch(input, init);
1219
+ }
1220
+
1213
1221
  const url =
1214
1222
  typeof input === 'string'
1215
1223
  ? input
@@ -1289,6 +1297,85 @@ export namespace Provider {
1289
1297
  responseHeaders: Object.fromEntries(response.headers.entries()),
1290
1298
  }));
1291
1299
 
1300
+ // Log response body for debugging provider failures
1301
+ // For streaming responses (SSE/event-stream), tee() the stream so the AI SDK
1302
+ // still receives the full stream while we asynchronously log a preview.
1303
+ // For non-streaming responses, buffer the body and reconstruct the Response.
1304
+ // See: https://github.com/link-assistant/agent/issues/204
1305
+ const responseBodyMaxChars = 4000;
1306
+ const contentType = response.headers.get('content-type') ?? '';
1307
+ const isStreaming =
1308
+ contentType.includes('event-stream') ||
1309
+ contentType.includes('octet-stream');
1310
+
1311
+ if (response.body) {
1312
+ if (isStreaming) {
1313
+ // Tee the stream: one copy for AI SDK, one for logging
1314
+ const [sdkStream, logStream] = response.body.tee();
1315
+
1316
+ // Consume log stream asynchronously (does not block SDK)
1317
+ (async () => {
1318
+ try {
1319
+ const reader = logStream.getReader();
1320
+ const decoder = new TextDecoder();
1321
+ let bodyPreview = '';
1322
+ let truncated = false;
1323
+ while (true) {
1324
+ const { done, value } = await reader.read();
1325
+ if (done) break;
1326
+ if (!truncated) {
1327
+ const chunk = decoder.decode(value, { stream: true });
1328
+ bodyPreview += chunk;
1329
+ if (bodyPreview.length > responseBodyMaxChars) {
1330
+ bodyPreview = bodyPreview.slice(
1331
+ 0,
1332
+ responseBodyMaxChars
1333
+ );
1334
+ truncated = true;
1335
+ }
1336
+ }
1337
+ }
1338
+ log.info(() => ({
1339
+ message: 'HTTP response body (stream)',
1340
+ providerID: provider.id,
1341
+ url,
1342
+ bodyPreview: truncated
1343
+ ? bodyPreview + `... [truncated]`
1344
+ : bodyPreview,
1345
+ }));
1346
+ } catch {
1347
+ // Ignore logging errors — do not affect the SDK stream
1348
+ }
1349
+ })();
1350
+
1351
+ // Return response with the SDK's copy of the stream
1352
+ return new Response(sdkStream, {
1353
+ status: response.status,
1354
+ statusText: response.statusText,
1355
+ headers: response.headers,
1356
+ });
1357
+ } else {
1358
+ // Non-streaming: buffer body, log it, reconstruct Response
1359
+ const bodyText = await response.text();
1360
+ const bodyPreview =
1361
+ bodyText.length > responseBodyMaxChars
1362
+ ? bodyText.slice(0, responseBodyMaxChars) +
1363
+ `... [truncated, total ${bodyText.length} chars]`
1364
+ : bodyText;
1365
+ log.info(() => ({
1366
+ message: 'HTTP response body',
1367
+ providerID: provider.id,
1368
+ url,
1369
+ bodyPreview,
1370
+ }));
1371
+ return new Response(bodyText, {
1372
+ status: response.status,
1373
+ statusText: response.statusText,
1374
+ headers: response.headers,
1375
+ });
1376
+ }
1377
+ }
1378
+
1292
1379
  return response;
1293
1380
  } catch (error) {
1294
1381
  const durationMs = Date.now() - startMs;