@link-assistant/agent 0.16.7 → 0.16.8

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.7",
3
+ "version": "0.16.8",
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,6 +1201,113 @@ 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
1206
+ // See: https://github.com/link-assistant/agent/issues/200
1207
+ if (Flag.OPENCODE_VERBOSE) {
1208
+ const innerFetch = options['fetch'];
1209
+ options['fetch'] = async (
1210
+ input: RequestInfo | URL,
1211
+ init?: RequestInit
1212
+ ): Promise<Response> => {
1213
+ const url =
1214
+ typeof input === 'string'
1215
+ ? input
1216
+ : input instanceof URL
1217
+ ? input.toString()
1218
+ : input.url;
1219
+ const method = init?.method ?? 'GET';
1220
+
1221
+ // Sanitize headers - mask authorization values
1222
+ const sanitizedHeaders: Record<string, string> = {};
1223
+ const rawHeaders = init?.headers;
1224
+ if (rawHeaders) {
1225
+ const entries =
1226
+ rawHeaders instanceof Headers
1227
+ ? Array.from(rawHeaders.entries())
1228
+ : Array.isArray(rawHeaders)
1229
+ ? rawHeaders
1230
+ : Object.entries(rawHeaders);
1231
+ for (const [key, value] of entries) {
1232
+ const lower = key.toLowerCase();
1233
+ if (
1234
+ lower === 'authorization' ||
1235
+ lower === 'x-api-key' ||
1236
+ lower === 'api-key'
1237
+ ) {
1238
+ sanitizedHeaders[key] =
1239
+ typeof value === 'string' && value.length > 8
1240
+ ? value.slice(0, 4) + '...' + value.slice(-4)
1241
+ : '[REDACTED]';
1242
+ } else {
1243
+ sanitizedHeaders[key] = String(value);
1244
+ }
1245
+ }
1246
+ }
1247
+
1248
+ // Log request body preview (truncated)
1249
+ let bodyPreview: string | undefined;
1250
+ if (init?.body) {
1251
+ const bodyStr =
1252
+ typeof init.body === 'string'
1253
+ ? init.body
1254
+ : init.body instanceof ArrayBuffer ||
1255
+ init.body instanceof Uint8Array
1256
+ ? `[binary ${(init.body as ArrayBuffer).byteLength ?? (init.body as Uint8Array).length} bytes]`
1257
+ : undefined;
1258
+ if (bodyStr && typeof bodyStr === 'string') {
1259
+ bodyPreview =
1260
+ bodyStr.length > 2000
1261
+ ? bodyStr.slice(0, 2000) +
1262
+ `... [truncated, total ${bodyStr.length} chars]`
1263
+ : bodyStr;
1264
+ }
1265
+ }
1266
+
1267
+ log.info(() => ({
1268
+ message: 'HTTP request',
1269
+ providerID: provider.id,
1270
+ method,
1271
+ url,
1272
+ headers: sanitizedHeaders,
1273
+ bodyPreview,
1274
+ }));
1275
+
1276
+ const startMs = Date.now();
1277
+ try {
1278
+ const response = await innerFetch(input, init);
1279
+ const durationMs = Date.now() - startMs;
1280
+
1281
+ log.info(() => ({
1282
+ message: 'HTTP response',
1283
+ providerID: provider.id,
1284
+ method,
1285
+ url,
1286
+ status: response.status,
1287
+ statusText: response.statusText,
1288
+ durationMs,
1289
+ responseHeaders: Object.fromEntries(response.headers.entries()),
1290
+ }));
1291
+
1292
+ return response;
1293
+ } catch (error) {
1294
+ const durationMs = Date.now() - startMs;
1295
+ log.error(() => ({
1296
+ message: 'HTTP request failed',
1297
+ providerID: provider.id,
1298
+ method,
1299
+ url,
1300
+ durationMs,
1301
+ error:
1302
+ error instanceof Error
1303
+ ? { name: error.name, message: error.message }
1304
+ : String(error),
1305
+ }));
1306
+ throw error;
1307
+ }
1308
+ };
1309
+ }
1310
+
1204
1311
  // Check if we have a bundled provider first - this avoids runtime package installation
1205
1312
  // which can hang or timeout due to known Bun issues
1206
1313
  // @see https://github.com/link-assistant/agent/issues/173
@@ -1310,8 +1417,21 @@ export namespace Provider {
1310
1417
 
1311
1418
  // For synthetic providers, we don't need model info from the database
1312
1419
  const info = isSyntheticProvider ? null : provider.info.models[modelID];
1313
- if (!isSyntheticProvider && !info)
1314
- throw new ModelNotFoundError({ providerID, modelID });
1420
+ if (!isSyntheticProvider && !info) {
1421
+ const availableInProvider = Object.keys(provider.info.models).slice(
1422
+ 0,
1423
+ 10
1424
+ );
1425
+ const suggestion = `Model "${modelID}" not found in provider "${providerID}". Available models: ${availableInProvider.join(', ')}${Object.keys(provider.info.models).length > 10 ? ` (and ${Object.keys(provider.info.models).length - 10} more)` : ''}.`;
1426
+ log.error(() => ({
1427
+ message: 'model not found in provider',
1428
+ providerID,
1429
+ modelID,
1430
+ availableModels: availableInProvider,
1431
+ totalModels: Object.keys(provider.info.models).length,
1432
+ }));
1433
+ throw new ModelNotFoundError({ providerID, modelID, suggestion });
1434
+ }
1315
1435
 
1316
1436
  try {
1317
1437
  const keyReal = `${providerID}/${modelID}`;