@link-assistant/agent 0.16.17 → 0.17.0

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.17",
3
+ "version": "0.17.0",
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",
@@ -2,6 +2,7 @@ import crypto from 'crypto';
2
2
  import path from 'path';
3
3
  import { Global } from '../global';
4
4
  import { Log } from '../util/log';
5
+ import { createVerboseFetch } from '../util/verbose-fetch';
5
6
  import z from 'zod';
6
7
 
7
8
  /**
@@ -24,6 +25,7 @@ import z from 'zod';
24
25
  */
25
26
  export namespace ClaudeOAuth {
26
27
  const log = Log.create({ service: 'claude-oauth' });
28
+ const verboseFetch = createVerboseFetch(fetch, { caller: 'claude-oauth' });
27
29
 
28
30
  /**
29
31
  * OAuth Configuration
@@ -218,7 +220,7 @@ export namespace ClaudeOAuth {
218
220
  message: 'exchanging authorization code for tokens',
219
221
  }));
220
222
 
221
- const response = await fetch(Config.tokenUrl, {
223
+ const response = await verboseFetch(Config.tokenUrl, {
222
224
  method: 'POST',
223
225
  headers: {
224
226
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -402,7 +404,7 @@ export namespace ClaudeOAuth {
402
404
  log.info(() => ({ message: 'refreshing access token' }));
403
405
 
404
406
  try {
405
- const response = await fetch(Config.tokenUrl, {
407
+ const response = await verboseFetch(Config.tokenUrl, {
406
408
  method: 'POST',
407
409
  headers: {
408
410
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -446,7 +448,7 @@ export namespace ClaudeOAuth {
446
448
  } else {
447
449
  headers.set('anthropic-beta', Config.betaHeader);
448
450
  }
449
- return fetch(url, { ...init, headers });
451
+ return verboseFetch(url, { ...init, headers });
450
452
  };
451
453
  }
452
454
  }
@@ -3,6 +3,7 @@ import * as http from 'node:http';
3
3
  import * as net from 'node:net';
4
4
  import { Auth } from './index';
5
5
  import { Log } from '../util/log';
6
+ import { createVerboseFetch } from '../util/verbose-fetch';
6
7
 
7
8
  /**
8
9
  * Auth Plugins Module
@@ -12,6 +13,7 @@ import { Log } from '../util/log';
12
13
  */
13
14
 
14
15
  const log = Log.create({ service: 'auth-plugins' });
16
+ const verboseFetch = createVerboseFetch(fetch, { caller: 'auth-plugins' });
15
17
 
16
18
  /**
17
19
  * OAuth callback result types
@@ -142,7 +144,7 @@ const AnthropicPlugin: AuthPlugin = {
142
144
  if (!code) return { type: 'failed' };
143
145
 
144
146
  const splits = code.split('#');
145
- const result = await fetch(
147
+ const result = await verboseFetch(
146
148
  'https://console.anthropic.com/v1/oauth/token',
147
149
  {
148
150
  method: 'POST',
@@ -210,7 +212,7 @@ const AnthropicPlugin: AuthPlugin = {
210
212
  if (!code) return { type: 'failed' };
211
213
 
212
214
  const splits = code.split('#');
213
- const tokenResult = await fetch(
215
+ const tokenResult = await verboseFetch(
214
216
  'https://console.anthropic.com/v1/oauth/token',
215
217
  {
216
218
  method: 'POST',
@@ -240,7 +242,7 @@ const AnthropicPlugin: AuthPlugin = {
240
242
  const credentials = await tokenResult.json();
241
243
 
242
244
  // Create API key using the access token
243
- const apiKeyResult = await fetch(
245
+ const apiKeyResult = await verboseFetch(
244
246
  'https://api.anthropic.com/api/oauth/claude_cli/create_api_key',
245
247
  {
246
248
  method: 'POST',
@@ -291,7 +293,7 @@ const AnthropicPlugin: AuthPlugin = {
291
293
  log.info(() => ({
292
294
  message: 'refreshing anthropic oauth token',
293
295
  }));
294
- const response = await fetch(
296
+ const response = await verboseFetch(
295
297
  'https://console.anthropic.com/v1/oauth/token',
296
298
  {
297
299
  method: 'POST',
@@ -446,7 +448,7 @@ const GitHubCopilotPlugin: AuthPlugin = {
446
448
 
447
449
  const urls = getCopilotUrls(domain);
448
450
 
449
- const deviceResponse = await fetch(urls.DEVICE_CODE_URL, {
451
+ const deviceResponse = await verboseFetch(urls.DEVICE_CODE_URL, {
450
452
  method: 'POST',
451
453
  headers: {
452
454
  Accept: 'application/json',
@@ -476,7 +478,7 @@ const GitHubCopilotPlugin: AuthPlugin = {
476
478
  method: 'auto',
477
479
  async callback(): Promise<AuthResult> {
478
480
  while (true) {
479
- const response = await fetch(urls.ACCESS_TOKEN_URL, {
481
+ const response = await verboseFetch(urls.ACCESS_TOKEN_URL, {
480
482
  method: 'POST',
481
483
  headers: {
482
484
  Accept: 'application/json',
@@ -571,7 +573,7 @@ const GitHubCopilotPlugin: AuthPlugin = {
571
573
  const urls = getCopilotUrls(domain);
572
574
 
573
575
  log.info(() => ({ message: 'refreshing github copilot token' }));
574
- const response = await fetch(urls.COPILOT_API_KEY_URL, {
576
+ const response = await verboseFetch(urls.COPILOT_API_KEY_URL, {
575
577
  headers: {
576
578
  Accept: 'application/json',
577
579
  Authorization: `Bearer ${currentInfo.refresh}`,
@@ -731,7 +733,7 @@ const OpenAIPlugin: AuthPlugin = {
731
733
  }
732
734
 
733
735
  // Exchange authorization code for tokens
734
- const tokenResult = await fetch(OPENAI_TOKEN_URL, {
736
+ const tokenResult = await verboseFetch(OPENAI_TOKEN_URL, {
735
737
  method: 'POST',
736
738
  headers: {
737
739
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -797,7 +799,7 @@ const OpenAIPlugin: AuthPlugin = {
797
799
  // Refresh token if expired
798
800
  if (!currentAuth.access || currentAuth.expires < Date.now()) {
799
801
  log.info(() => ({ message: 'refreshing openai oauth token' }));
800
- const response = await fetch(OPENAI_TOKEN_URL, {
802
+ const response = await verboseFetch(OPENAI_TOKEN_URL, {
801
803
  method: 'POST',
802
804
  headers: {
803
805
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -1091,7 +1093,7 @@ const GooglePlugin: AuthPlugin = {
1091
1093
  const { code } = await authPromise;
1092
1094
 
1093
1095
  // Exchange authorization code for tokens
1094
- const tokenResult = await fetch(GOOGLE_TOKEN_URL, {
1096
+ const tokenResult = await verboseFetch(GOOGLE_TOKEN_URL, {
1095
1097
  method: 'POST',
1096
1098
  headers: {
1097
1099
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -1187,7 +1189,7 @@ const GooglePlugin: AuthPlugin = {
1187
1189
 
1188
1190
  try {
1189
1191
  // Exchange authorization code for tokens
1190
- const tokenResult = await fetch(GOOGLE_TOKEN_URL, {
1192
+ const tokenResult = await verboseFetch(GOOGLE_TOKEN_URL, {
1191
1193
  method: 'POST',
1192
1194
  headers: {
1193
1195
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -1348,7 +1350,7 @@ const GooglePlugin: AuthPlugin = {
1348
1350
  // Call loadCodeAssist to discover project and tier
1349
1351
  try {
1350
1352
  const loadUrl = `${CLOUD_CODE_ENDPOINT}/${CLOUD_CODE_API_VERSION}:loadCodeAssist`;
1351
- const loadRes = await fetch(loadUrl, {
1353
+ const loadRes = await verboseFetch(loadUrl, {
1352
1354
  method: 'POST',
1353
1355
  headers: {
1354
1356
  'Content-Type': 'application/json',
@@ -1440,7 +1442,7 @@ const GooglePlugin: AuthPlugin = {
1440
1442
  },
1441
1443
  };
1442
1444
 
1443
- let lroRes = await fetch(onboardUrl, {
1445
+ let lroRes = await verboseFetch(onboardUrl, {
1444
1446
  method: 'POST',
1445
1447
  headers: {
1446
1448
  'Content-Type': 'application/json',
@@ -1456,11 +1458,11 @@ const GooglePlugin: AuthPlugin = {
1456
1458
  if (lroRes.name) {
1457
1459
  // Poll operation status
1458
1460
  const opUrl = `${CLOUD_CODE_ENDPOINT}/${CLOUD_CODE_API_VERSION}/${lroRes.name}`;
1459
- lroRes = await fetch(opUrl, {
1461
+ lroRes = await verboseFetch(opUrl, {
1460
1462
  headers: { Authorization: `Bearer ${accessToken}` },
1461
1463
  }).then((r) => r.json());
1462
1464
  } else {
1463
- lroRes = await fetch(onboardUrl, {
1465
+ lroRes = await verboseFetch(onboardUrl, {
1464
1466
  method: 'POST',
1465
1467
  headers: {
1466
1468
  'Content-Type': 'application/json',
@@ -1924,7 +1926,7 @@ const GooglePlugin: AuthPlugin = {
1924
1926
  // Invalidate project cache when token changes
1925
1927
  cachedProjectContext = null;
1926
1928
 
1927
- const response = await fetch(GOOGLE_TOKEN_URL, {
1929
+ const response = await verboseFetch(GOOGLE_TOKEN_URL, {
1928
1930
  method: 'POST',
1929
1931
  headers: {
1930
1932
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -2043,7 +2045,7 @@ const GooglePlugin: AuthPlugin = {
2043
2045
  await new Promise((resolve) => setTimeout(resolve, delay));
2044
2046
  }
2045
2047
 
2046
- const cloudCodeResponse = await fetch(finalCloudCodeUrl, {
2048
+ const cloudCodeResponse = await verboseFetch(finalCloudCodeUrl, {
2047
2049
  ...init,
2048
2050
  body,
2049
2051
  headers,
@@ -2135,7 +2137,7 @@ const GooglePlugin: AuthPlugin = {
2135
2137
  };
2136
2138
  delete headers['x-goog-api-key'];
2137
2139
 
2138
- const oauthResponse = await fetch(input, {
2140
+ const oauthResponse = await verboseFetch(input, {
2139
2141
  ...init,
2140
2142
  headers,
2141
2143
  });
@@ -2248,19 +2250,22 @@ const QwenPlugin: AuthPlugin = {
2248
2250
  const codeChallenge = generateCodeChallenge(codeVerifier);
2249
2251
 
2250
2252
  // Request device code
2251
- const deviceResponse = await fetch(QWEN_OAUTH_DEVICE_CODE_ENDPOINT, {
2252
- method: 'POST',
2253
- headers: {
2254
- 'Content-Type': 'application/x-www-form-urlencoded',
2255
- Accept: 'application/json',
2256
- },
2257
- body: new URLSearchParams({
2258
- client_id: QWEN_OAUTH_CLIENT_ID,
2259
- scope: QWEN_OAUTH_SCOPE,
2260
- code_challenge: codeChallenge,
2261
- code_challenge_method: 'S256',
2262
- }).toString(),
2263
- });
2253
+ const deviceResponse = await verboseFetch(
2254
+ QWEN_OAUTH_DEVICE_CODE_ENDPOINT,
2255
+ {
2256
+ method: 'POST',
2257
+ headers: {
2258
+ 'Content-Type': 'application/x-www-form-urlencoded',
2259
+ Accept: 'application/json',
2260
+ },
2261
+ body: new URLSearchParams({
2262
+ client_id: QWEN_OAUTH_CLIENT_ID,
2263
+ scope: QWEN_OAUTH_SCOPE,
2264
+ code_challenge: codeChallenge,
2265
+ code_challenge_method: 'S256',
2266
+ }).toString(),
2267
+ }
2268
+ );
2264
2269
 
2265
2270
  if (!deviceResponse.ok) {
2266
2271
  const errorText = await deviceResponse.text();
@@ -2308,19 +2313,22 @@ const QwenPlugin: AuthPlugin = {
2308
2313
  async callback(): Promise<AuthResult> {
2309
2314
  // Poll for authorization completion
2310
2315
  for (let attempt = 0; attempt < maxPollAttempts; attempt++) {
2311
- const tokenResponse = await fetch(QWEN_OAUTH_TOKEN_ENDPOINT, {
2312
- method: 'POST',
2313
- headers: {
2314
- 'Content-Type': 'application/x-www-form-urlencoded',
2315
- Accept: 'application/json',
2316
- },
2317
- body: new URLSearchParams({
2318
- client_id: QWEN_OAUTH_CLIENT_ID,
2319
- device_code: deviceData.device_code,
2320
- grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
2321
- code_verifier: codeVerifier,
2322
- }).toString(),
2323
- });
2316
+ const tokenResponse = await verboseFetch(
2317
+ QWEN_OAUTH_TOKEN_ENDPOINT,
2318
+ {
2319
+ method: 'POST',
2320
+ headers: {
2321
+ 'Content-Type': 'application/x-www-form-urlencoded',
2322
+ Accept: 'application/json',
2323
+ },
2324
+ body: new URLSearchParams({
2325
+ client_id: QWEN_OAUTH_CLIENT_ID,
2326
+ device_code: deviceData.device_code,
2327
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
2328
+ code_verifier: codeVerifier,
2329
+ }).toString(),
2330
+ }
2331
+ );
2324
2332
 
2325
2333
  if (!tokenResponse.ok) {
2326
2334
  const errorText = await tokenResponse.text();
@@ -2426,7 +2434,7 @@ const QwenPlugin: AuthPlugin = {
2426
2434
  : 'token expiring soon',
2427
2435
  }));
2428
2436
 
2429
- const response = await fetch(QWEN_OAUTH_TOKEN_ENDPOINT, {
2437
+ const response = await verboseFetch(QWEN_OAUTH_TOKEN_ENDPOINT, {
2430
2438
  method: 'POST',
2431
2439
  headers: {
2432
2440
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -2575,7 +2583,7 @@ const AlibabaPlugin: AuthPlugin = {
2575
2583
  message: 'refreshing qwen oauth token (alibaba provider)',
2576
2584
  }));
2577
2585
 
2578
- const response = await fetch(QWEN_OAUTH_TOKEN_ENDPOINT, {
2586
+ const response = await verboseFetch(QWEN_OAUTH_TOKEN_ENDPOINT, {
2579
2587
  method: 'POST',
2580
2588
  headers: {
2581
2589
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -2651,7 +2659,7 @@ const KiloPlugin: AuthPlugin = {
2651
2659
  type: 'oauth',
2652
2660
  async authorize() {
2653
2661
  // Initiate device authorization
2654
- const initResponse = await fetch(
2662
+ const initResponse = await verboseFetch(
2655
2663
  `${KILO_API_BASE}/api/device-auth/codes`,
2656
2664
  {
2657
2665
  method: 'POST',
@@ -2704,7 +2712,7 @@ const KiloPlugin: AuthPlugin = {
2704
2712
  setTimeout(resolve, KILO_POLL_INTERVAL_MS)
2705
2713
  );
2706
2714
 
2707
- const pollResponse = await fetch(
2715
+ const pollResponse = await verboseFetch(
2708
2716
  `${KILO_API_BASE}/api/device-auth/codes/${authData.code}`
2709
2717
  );
2710
2718
 
@@ -9,6 +9,9 @@ import path from 'path';
9
9
  import os from 'os';
10
10
  import { Global } from '../../global';
11
11
  import { map, pipe, sortBy, values } from 'remeda';
12
+ import { createVerboseFetch } from '../../util/verbose-fetch';
13
+
14
+ const verboseFetch = createVerboseFetch(fetch, { caller: 'auth-cmd' });
12
15
 
13
16
  /**
14
17
  * Auth Command
@@ -86,9 +89,9 @@ export const AuthLoginCommand = cmd({
86
89
  // Handle wellknown URL login
87
90
  if (args.url) {
88
91
  try {
89
- const wellknown = await fetch(`${args.url}/.well-known/opencode`).then(
90
- (x) => x.json() as any
91
- );
92
+ const wellknown = await verboseFetch(
93
+ `${args.url}/.well-known/opencode`
94
+ ).then((x) => x.json() as any);
92
95
  prompts.log.info(`Running \`${wellknown.auth.command.join(' ')}\``);
93
96
  const proc = Bun.spawn({
94
97
  cmd: wellknown.auth.command,
@@ -12,6 +12,7 @@ import { createEventHandler } from '../json-standard/index.ts';
12
12
  import { createContinuousStdinReader } from './input-queue.js';
13
13
  import { Log } from '../util/log.ts';
14
14
  import { Flag } from '../flag/flag.ts';
15
+ import { createVerboseFetch } from '../util/verbose-fetch.ts';
15
16
  import { outputStatus, outputError, outputInput } from './output.ts';
16
17
 
17
18
  // Shared error tracking
@@ -215,7 +216,10 @@ export async function runContinuousServerMode(
215
216
  sessionID = resumeInfo.sessionID;
216
217
  } else {
217
218
  // Create a new session
218
- const createRes = await fetch(
219
+ const localVerboseFetch = createVerboseFetch(fetch, {
220
+ caller: 'continuous-mode',
221
+ });
222
+ const createRes = await localVerboseFetch(
219
223
  `http://${server.hostname}:${server.port}/session`,
220
224
  {
221
225
  method: 'POST',
@@ -11,6 +11,7 @@ import { lazy } from '../util/lazy';
11
11
  import { NamedError } from '../util/error';
12
12
  import { Flag } from '../flag/flag';
13
13
  import { Auth } from '../auth';
14
+ import { createVerboseFetch } from '../util/verbose-fetch';
14
15
  import {
15
16
  type ParseError as JsoncParseError,
16
17
  parse as parseJsonc,
@@ -21,6 +22,7 @@ import { ConfigMarkdown } from './markdown';
21
22
 
22
23
  export namespace Config {
23
24
  const log = Log.create({ service: 'config' });
25
+ const verboseFetch = createVerboseFetch(fetch, { caller: 'config' });
24
26
 
25
27
  /**
26
28
  * Automatically migrate .opencode directories to .link-assistant-agent
@@ -163,9 +165,9 @@ export namespace Config {
163
165
  for (const [key, value] of Object.entries(auth)) {
164
166
  if (value.type === 'wellknown') {
165
167
  process.env[value.key] = value.token;
166
- const wellknown = (await fetch(`${key}/.well-known/opencode`).then(
167
- (x) => x.json()
168
- )) as any;
168
+ const wellknown = (await verboseFetch(
169
+ `${key}/.well-known/opencode`
170
+ ).then((x) => x.json())) as any;
169
171
  result = mergeDeep(
170
172
  result,
171
173
  await load(JSON.stringify(wellknown.config ?? {}), process.cwd())
@@ -9,9 +9,11 @@ import { $ } from 'bun';
9
9
 
10
10
  import { ZipReader, BlobReader, BlobWriter } from '@zip.js/zip.js';
11
11
  import { Log } from '../util/log';
12
+ import { createVerboseFetch } from '../util/verbose-fetch';
12
13
 
13
14
  export namespace Ripgrep {
14
15
  const log = Log.create({ service: 'ripgrep' });
16
+ const verboseFetch = createVerboseFetch(fetch, { caller: 'ripgrep' });
15
17
  const Stats = z.object({
16
18
  elapsed: z.object({
17
19
  secs: z.number(),
@@ -142,7 +144,7 @@ export namespace Ripgrep {
142
144
  const filename = `ripgrep-${version}-${config.platform}.${config.extension}`;
143
145
  const url = `https://github.com/BurntSushi/ripgrep/releases/download/${version}/${filename}`;
144
146
 
145
- const response = await fetch(url);
147
+ const response = await verboseFetch(url);
146
148
  if (!response.ok)
147
149
  throw new DownloadFailedError({ url, status: response.status });
148
150
 
package/src/flag/flag.ts CHANGED
@@ -103,13 +103,19 @@ export namespace Flag {
103
103
  }
104
104
 
105
105
  // Session summarization configuration
106
- // When disabled, session summaries will not be generated
107
- // This saves tokens and prevents rate limit issues with free tier models
108
- // See: https://github.com/link-assistant/agent/issues/179
109
- export let SUMMARIZE_SESSION = truthyCompat(
110
- 'LINK_ASSISTANT_AGENT_SUMMARIZE_SESSION',
111
- 'AGENT_SUMMARIZE_SESSION'
112
- );
106
+ // Enabled by default - generates AI-powered session summaries using the same model
107
+ // Can be disabled with --no-summarize-session or AGENT_SUMMARIZE_SESSION=false
108
+ // See: https://github.com/link-assistant/agent/issues/217
109
+ export let SUMMARIZE_SESSION = (() => {
110
+ const value = (
111
+ getEnv(
112
+ 'LINK_ASSISTANT_AGENT_SUMMARIZE_SESSION',
113
+ 'AGENT_SUMMARIZE_SESSION'
114
+ ) ?? ''
115
+ ).toLowerCase();
116
+ if (value === 'false' || value === '0') return false;
117
+ return true; // Default to true
118
+ })();
113
119
 
114
120
  // Allow setting summarize-session mode programmatically (e.g., from CLI --summarize-session flag)
115
121
  export function setSummarizeSession(value: boolean) {
package/src/index.js CHANGED
@@ -21,6 +21,7 @@ import { McpCommand } from './cli/cmd/mcp.ts';
21
21
  import { AuthCommand } from './cli/cmd/auth.ts';
22
22
  import { FormatError } from './cli/error.ts';
23
23
  import { UI } from './cli/ui.ts';
24
+ import { createVerboseFetch } from './util/verbose-fetch.ts';
24
25
  import {
25
26
  runContinuousServerMode,
26
27
  runContinuousDirectMode,
@@ -427,7 +428,8 @@ async function runServerMode(
427
428
  sessionID = resumeInfo.sessionID;
428
429
  } else {
429
430
  // Create a new session
430
- const createRes = await fetch(
431
+ const localVerboseFetch = createVerboseFetch(fetch, { caller: 'cli' });
432
+ const createRes = await localVerboseFetch(
431
433
  `http://${server.hostname}:${server.port}/session`,
432
434
  {
433
435
  method: 'POST',
@@ -731,8 +733,9 @@ async function main() {
731
733
  })
732
734
  .option('summarize-session', {
733
735
  type: 'boolean',
734
- description: 'Generate AI session summaries',
735
- default: false,
736
+ description:
737
+ 'Generate AI session summaries (default: true). Use --no-summarize-session to disable.',
738
+ default: true,
736
739
  }),
737
740
  handler: async (argv) => {
738
741
  // Check both CLI flag and environment variable for compact JSON mode
@@ -915,7 +918,10 @@ async function main() {
915
918
  if (argv['output-response-model'] === false) {
916
919
  Flag.setOutputResponseModel(false);
917
920
  }
918
- if (argv['summarize-session'] === true) {
921
+ // summarize-session is enabled by default, only set if explicitly disabled
922
+ if (argv['summarize-session'] === false) {
923
+ Flag.setSummarizeSession(false);
924
+ } else {
919
925
  Flag.setSummarizeSession(true);
920
926
  }
921
927
  // retry-on-rate-limits is enabled by default, only set if explicitly disabled
@@ -927,6 +933,15 @@ async function main() {
927
933
  level: Flag.OPENCODE_VERBOSE ? 'DEBUG' : 'INFO',
928
934
  compactJson: isCompact,
929
935
  });
936
+
937
+ // Monkey-patch globalThis.fetch for raw HTTP logging in --verbose mode.
938
+ // Catches ALL HTTP calls regardless of AI SDK fetch passthrough. (#217)
939
+ if (!globalThis.__agentVerboseFetchInstalled) {
940
+ globalThis.fetch = createVerboseFetch(globalThis.fetch, {
941
+ caller: 'global',
942
+ });
943
+ globalThis.__agentVerboseFetchInstalled = true;
944
+ }
930
945
  })
931
946
  .fail((msg, err, yargs) => {
932
947
  // Handle errors from command handlers
@@ -15,8 +15,10 @@
15
15
 
16
16
  import { Log } from '../util/log';
17
17
  import { Auth } from '../auth';
18
+ import { createVerboseFetch } from '../util/verbose-fetch';
18
19
 
19
20
  const log = Log.create({ service: 'google-cloudcode' });
21
+ const verboseFetch = createVerboseFetch(fetch, { caller: 'google-cloudcode' });
20
22
 
21
23
  // Cloud Code API endpoints (from gemini-cli)
22
24
  // Configurable via environment variables for testing or alternative endpoints
@@ -179,7 +181,7 @@ export class CloudCodeClient {
179
181
  message: 'refreshing google oauth token for cloud code',
180
182
  }));
181
183
 
182
- const response = await fetch(GOOGLE_TOKEN_URL, {
184
+ const response = await verboseFetch(GOOGLE_TOKEN_URL, {
183
185
  method: 'POST',
184
186
  headers: {
185
187
  'Content-Type': 'application/x-www-form-urlencoded',
@@ -223,7 +225,7 @@ export class CloudCodeClient {
223
225
  const baseUrl = `${CODE_ASSIST_ENDPOINT}/${CODE_ASSIST_API_VERSION}:${method}`;
224
226
  const url = options.stream ? `${baseUrl}?alt=sse` : baseUrl;
225
227
 
226
- const response = await fetch(url, {
228
+ const response = await verboseFetch(url, {
227
229
  method: 'POST',
228
230
  headers: {
229
231
  'Content-Type': 'application/json',
@@ -1,11 +1,13 @@
1
1
  import { Global } from '../global';
2
2
  import { Log } from '../util/log';
3
+ import { createVerboseFetch } from '../util/verbose-fetch';
3
4
  import path from 'path';
4
5
  import z from 'zod';
5
6
  import { data } from './models-macro';
6
7
 
7
8
  export namespace ModelsDev {
8
9
  const log = Log.create({ service: 'models.dev' });
10
+ const verboseFetch = createVerboseFetch(fetch, { caller: 'models.dev' });
9
11
  const filepath = path.join(Global.Path.cache, 'models.json');
10
12
 
11
13
  export const Model = z
@@ -145,7 +147,7 @@ export namespace ModelsDev {
145
147
  export async function refresh() {
146
148
  const file = Bun.file(filepath);
147
149
  log.info(() => ({ message: 'refreshing', file }));
148
- const result = await fetch('https://models.dev/api.json', {
150
+ const result = await verboseFetch('https://models.dev/api.json', {
149
151
  headers: {
150
152
  'User-Agent': 'agent-cli/1.0.0',
151
153
  },