chrome-ai-bridge 2.4.0 → 2.5.1

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.
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { connectViaExtensionRaw } from './extension-raw.js';
4
4
  import { CdpClient } from './cdp-client.js';
5
- import { logConnectionState, logInfo, logError, logWarn } from './mcp-logger.js';
5
+ import { logConnectionState, logInfo, logError, logWarn } from './debug-logger.js';
6
6
  import { DOM_UTILS_CODE } from './utils/index.js';
7
7
  import { getDriver } from './drivers/index.js';
8
8
  import { NetworkInterceptor } from './network-interceptor.js';
@@ -49,18 +49,29 @@ function setClientForAgent(kind, client, relay) {
49
49
  conn.geminiRelay = relay;
50
50
  }
51
51
  }
52
- const CONNECT_REUSE_TIMEOUT_MS = Number(process.env.MCP_CONNECT_REUSE_TIMEOUT_MS || '12000');
53
- const CONNECT_NEWTAB_TIMEOUT_MS = Number(process.env.MCP_CONNECT_NEWTAB_TIMEOUT_MS || '20000');
54
- const MCP_TOOL_BUDGET_MS = Number(process.env.CAI_MCP_TOOL_BUDGET_MS || '50000');
52
+ // Env var deprecation helpers
53
+ function envWithFallback(newName, oldName, defaultVal) {
54
+ if (process.env[newName])
55
+ return process.env[newName];
56
+ if (process.env[oldName]) {
57
+ console.error(`[deprecation] ${oldName} is deprecated, use ${newName} instead`);
58
+ return process.env[oldName];
59
+ }
60
+ return defaultVal;
61
+ }
62
+ const CONNECT_REUSE_TIMEOUT_MS = Number(envWithFallback('CAI_CONNECT_REUSE_TIMEOUT_MS', 'MCP_CONNECT_REUSE_TIMEOUT_MS', '12000'));
63
+ const CONNECT_NEWTAB_TIMEOUT_MS = Number(envWithFallback('CAI_CONNECT_NEWTAB_TIMEOUT_MS', 'MCP_CONNECT_NEWTAB_TIMEOUT_MS', '20000'));
64
+ const TOOL_BUDGET_MS = Number(envWithFallback('CAI_TOOL_BUDGET_MS', 'CAI_MCP_TOOL_BUDGET_MS', '50000'));
55
65
  const RESPONSE_WAIT_MAX_MS = Number(process.env.CAI_RESPONSE_WAIT_MAX_MS || '40000');
56
- const BUDGET_RESERVE_MS = Number(process.env.CAI_MCP_BUDGET_RESERVE_MS || '3000');
57
- function getRemainingBudgetMs(startMs) {
58
- return MCP_TOOL_BUDGET_MS - (nowMs() - startMs) - BUDGET_RESERVE_MS;
66
+ const BUDGET_RESERVE_MS = Number(envWithFallback('CAI_BUDGET_RESERVE_MS', 'CAI_MCP_BUDGET_RESERVE_MS', '3000'));
67
+ function getRemainingBudgetMs(startMs, overrideBudgetMs) {
68
+ return (overrideBudgetMs ?? TOOL_BUDGET_MS) - (nowMs() - startMs) - BUDGET_RESERVE_MS;
59
69
  }
60
- function getResponseWaitBudgetMs(startMs, ceilingMs, stage) {
61
- const remaining = getRemainingBudgetMs(startMs);
70
+ function getResponseWaitBudgetMs(startMs, ceilingMs, stage, overrideBudgetMs) {
71
+ const effectiveBudget = overrideBudgetMs ?? TOOL_BUDGET_MS;
72
+ const remaining = getRemainingBudgetMs(startMs, overrideBudgetMs);
62
73
  if (remaining <= 1000) {
63
- throw new Error(`MCP_TOOL_BUDGET_EXCEEDED: stage=${stage} budgetMs=${MCP_TOOL_BUDGET_MS} reserveMs=${BUDGET_RESERVE_MS}`);
74
+ throw new Error(`TOOL_BUDGET_EXCEEDED: stage=${stage} budgetMs=${effectiveBudget} reserveMs=${BUDGET_RESERVE_MS}`);
64
75
  }
65
76
  return Math.max(1000, Math.min(ceilingMs, remaining));
66
77
  }
@@ -72,6 +83,14 @@ function nowMs() {
72
83
  * 軽量なevaluateコマンドで接続が生きているかチェック
73
84
  */
74
85
  async function isConnectionHealthy(client, kind) {
86
+ // Fast-path: if relay is already disconnected, skip the expensive evaluate call
87
+ if (kind) {
88
+ const relay = getRelayFromAgent(kind);
89
+ if (relay && !relay.isReady()) {
90
+ logConnectionState(kind, 'unhealthy', { elapsed: 0, error: 'relay not ready (fast-path)' });
91
+ return false;
92
+ }
93
+ }
75
94
  const startTime = Date.now();
76
95
  try {
77
96
  // 4秒タイムアウトで簡単なコマンドを実行(2秒では不十分な場合があった)
@@ -228,7 +247,7 @@ export async function resetConnection(kind) {
228
247
  }
229
248
  /**
230
249
  * 全接続をクリーンアップ(プロセス終了時用)
231
- * MCPサーバー終了時にゾンビプロセスを防ぐために使用
250
+ * サーバー終了時にゾンビプロセスを防ぐために使用
232
251
  */
233
252
  export async function cleanupAllConnections() {
234
253
  // Snapshot entries before clearing to avoid mutation during iteration
@@ -505,7 +524,17 @@ async function navigate(client, url) {
505
524
  await client.send('Page.navigate', { url });
506
525
  await client.waitForFunction(`document.readyState === 'complete'`, 30000);
507
526
  }
508
- async function askChatGPTFastInternal(question, debug) {
527
+ /** Strip conversation-specific paths (/c/<id>, /app/<id>) to prevent chat pollution on reuse */
528
+ function getBaseUrl(kind, url) {
529
+ if (kind === 'chatgpt') {
530
+ return url.replace(/\/c\/[a-zA-Z0-9_-]+.*$/, '/');
531
+ }
532
+ if (kind === 'gemini') {
533
+ return url.replace(/\/app\/[a-zA-Z0-9_-]+.*$/, '/');
534
+ }
535
+ return url;
536
+ }
537
+ async function askChatGPTFastInternal(question, debug, budgetMs) {
509
538
  const t0 = nowMs();
510
539
  const timings = {};
511
540
  logInfo('chatgpt', 'askChatGPTFast started', { questionLength: question.length });
@@ -523,16 +552,14 @@ async function askChatGPTFastInternal(question, debug) {
523
552
  // SPA描画安定化のため追加待機
524
553
  await new Promise(r => setTimeout(r, 500));
525
554
  console.error('[ChatGPT] Waited 500ms for SPA rendering');
526
- // 既存チャット(/c/を含むURL)の場合、メッセージが描画されるまで待機
555
+ // 既存チャット(/c/を含むURL)の場合、新規チャットへ遷移
556
+ // 同じ会話に質問を投入すると前回のコンテキストが応答に影響するため
527
557
  const currentUrl = await client.evaluate('location.href');
528
558
  if (currentUrl.includes('/c/')) {
529
- try {
530
- await client.waitForFunction(`document.querySelectorAll('[data-message-author-role="assistant"]').length > 0`, 5000);
531
- console.error('[ChatGPT] Existing chat messages loaded');
532
- }
533
- catch {
534
- console.error('[ChatGPT] No existing messages found, continuing as new chat');
535
- }
559
+ console.error('[ChatGPT] Existing conversation detected, navigating to new chat...');
560
+ await navigate(client, 'https://chatgpt.com/');
561
+ await new Promise(r => setTimeout(r, 500));
562
+ console.error('[ChatGPT] New chat page loaded');
536
563
  }
537
564
  // 入力欄が表示されるまで待機してから取得
538
565
  const tWaitInput = nowMs();
@@ -1008,12 +1035,14 @@ async function askChatGPTFastInternal(question, debug) {
1008
1035
  const tWaitResp = nowMs();
1009
1036
  console.error('[ChatGPT] Waiting for response (using stop button detection)...');
1010
1037
  // 60秒 caller deadline を超えないよう、残り予算内で待機する。
1011
- const maxWaitMs = getResponseWaitBudgetMs(t0, RESPONSE_WAIT_MAX_MS, 'chatgpt-response');
1038
+ const maxWaitMs = getResponseWaitBudgetMs(t0, RESPONSE_WAIT_MAX_MS, 'chatgpt-response', budgetMs);
1012
1039
  const pollIntervalMs = 1000;
1013
1040
  const startWait = Date.now();
1014
1041
  let lastLoggedState = '';
1015
1042
  let sawStopButton = false; // 生成中状態を検出したかどうか
1016
1043
  let streamingText = ''; // ストリーミング中に取得したテキスト(完了後に折りたたまれる対策)
1044
+ let textStableCount = 0; // テキスト長が安定した回数(2-poll confirmation)
1045
+ let lastTextLength = -1; // 前回のテキスト長
1017
1046
  while (Date.now() - startWait < maxWaitMs) {
1018
1047
  const state = await client.evaluate(`
1019
1048
  (() => {
@@ -1288,46 +1317,65 @@ async function askChatGPTFastInternal(question, debug) {
1288
1317
  // 2. AND 入力欄が空
1289
1318
  // 3. AND 新しいアシスタントメッセージが増えた
1290
1319
  // 注: hasResponseText は CDP でテキスト取得できない場合があるため必須条件から外す
1320
+ // テキスト安定性チェック(全完了条件で共通使用)
1321
+ const currentTextLen = state.debug_lastAssistantInnerTextLen;
1322
+ if (currentTextLen === lastTextLength && currentTextLen > 0) {
1323
+ textStableCount++;
1324
+ }
1325
+ else {
1326
+ textStableCount = 0;
1327
+ lastTextLength = currentTextLen;
1328
+ }
1291
1329
  if (sawStopButton && !state.hasStopButton && !state.inputBoxHasText &&
1292
1330
  state.assistantMsgCount > initialAssistantCount) {
1293
- console.error(`[ChatGPT] Response complete - stop button disappeared, input empty, assistant count increased (${initialAssistantCount} -> ${state.assistantMsgCount})`);
1294
- // ChatGPT 5.2 Thinking: 完了直後にストリーミング中のテキストをキャプチャ
1295
- // (完了後は折りたたまれてしまうため、この時点で取得)
1296
- streamingText = await client.evaluate(`
1297
- (() => {
1298
- const msgs = document.querySelectorAll('[data-message-author-role="assistant"]');
1299
- if (msgs.length === 0) return '';
1300
- const last = msgs[msgs.length - 1];
1301
- // .markdown, .result-thinking, または直接テキストを試す
1302
- const md = last.querySelector('.markdown');
1303
- if (md) {
1304
- const t = (md.innerText || md.textContent || '').trim();
1305
- if (t.length > 0) return t;
1306
- }
1307
- const rt = last.querySelector('.result-thinking');
1308
- if (rt) {
1309
- const t = (rt.innerText || rt.textContent || '').trim();
1310
- if (t.length > 0) return t;
1331
+ // 2-poll confirmation: テキスト長が2回連続安定してから完了とする
1332
+ if (textStableCount >= 2) {
1333
+ console.error(`[ChatGPT] Response complete - stop gone, text stable for ${textStableCount} polls (len=${currentTextLen})`);
1334
+ streamingText = await client.evaluate(`
1335
+ (() => {
1336
+ const msgs = document.querySelectorAll('[data-message-author-role="assistant"]');
1337
+ if (msgs.length === 0) return '';
1338
+ const last = msgs[msgs.length - 1];
1339
+ const md = last.querySelector('.markdown');
1340
+ if (md) {
1341
+ const t = (md.innerText || md.textContent || '').trim();
1342
+ if (t.length > 0) return t;
1343
+ }
1344
+ const rt = last.querySelector('.result-thinking');
1345
+ if (rt) {
1346
+ const t = (rt.innerText || rt.textContent || '').trim();
1347
+ if (t.length > 0) return t;
1348
+ }
1349
+ return (last.innerText || last.textContent || '').trim();
1350
+ })()
1351
+ `);
1352
+ break;
1311
1353
  }
1312
- return (last.innerText || last.textContent || '').trim();
1313
- })()
1314
- `);
1315
- break;
1354
+ // まだ安定していない 次のポールまで待機
1355
+ console.error(`[ChatGPT] Stop button gone but text not stable yet (len=${currentTextLen}, stableCount=${textStableCount})`);
1356
+ await new Promise(r => setTimeout(r, pollIntervalMs));
1357
+ continue;
1316
1358
  }
1317
1359
  // フォールバック: 5秒以上待って、stopボタンなし、入力欄空、新しいアシスタントメッセージが増えた
1318
1360
  // (stopボタンを見逃した場合の救済)
1319
1361
  const elapsed = Date.now() - startWait;
1320
1362
  if (elapsed > 5000 && !state.hasStopButton && !state.inputBoxHasText &&
1321
1363
  state.assistantMsgCount > initialAssistantCount && !state.isStillGenerating) {
1322
- console.error(`[ChatGPT] Response complete - fallback after 5s (no stop button, input empty, assistant count increased ${initialAssistantCount} -> ${state.assistantMsgCount})`);
1323
- break;
1364
+ if (textStableCount >= 2) {
1365
+ console.error(`[ChatGPT] Response complete - fallback after 5s, text stable (len=${currentTextLen}, stableCount=${textStableCount})`);
1366
+ break;
1367
+ }
1368
+ console.error(`[ChatGPT] Fallback conditions met but text not stable yet (len=${currentTextLen}, stableCount=${textStableCount})`);
1324
1369
  }
1325
1370
  // Thinkingモード専用フォールバック: stopボタンなしでも、生成完了していれば完了
1326
1371
  // 重要: 「今すぐ回答」ボタンがある間は、まだThinking中なので待機を継続
1327
1372
  if (elapsed > 10000 && !state.isStillGenerating && !state.hasSkipThinkingButton &&
1328
1373
  state.assistantMsgCount > initialAssistantCount && !state.inputBoxHasText) {
1329
- console.error(`[ChatGPT] Response complete - Thinking mode fallback after 10s (generating complete, no skip button)`);
1330
- break;
1374
+ if (textStableCount >= 2) {
1375
+ console.error(`[ChatGPT] Response complete - Thinking mode fallback after 10s, text stable (len=${currentTextLen}, stableCount=${textStableCount})`);
1376
+ break;
1377
+ }
1378
+ console.error(`[ChatGPT] Thinking fallback conditions met but text not stable yet (len=${currentTextLen}, stableCount=${textStableCount})`);
1331
1379
  }
1332
1380
  await new Promise(r => setTimeout(r, pollIntervalMs));
1333
1381
  }
@@ -1401,7 +1449,7 @@ async function askChatGPTFastInternal(question, debug) {
1401
1449
  // 回答完了後、DOM安定化のための追加待機
1402
1450
  // ChatGPT Thinkingモードでは、停止ボタン消失後も最終回答がレンダリングされるまで遅延がある
1403
1451
  // 回答テキストが存在するまでポーリングで待機
1404
- const maxWaitForText = getResponseWaitBudgetMs(t0, 15000, 'chatgpt-finalize');
1452
+ const maxWaitForText = getResponseWaitBudgetMs(t0, 15000, 'chatgpt-finalize', budgetMs);
1405
1453
  const pollInterval = 200;
1406
1454
  const waitStart = Date.now();
1407
1455
  let hasResponseText = false;
@@ -1941,7 +1989,7 @@ async function askChatGPTFastInternal(question, debug) {
1941
1989
  console.error(`[ChatGPT] Response extracted: ${finalAnswer.slice(0, 100)}...`);
1942
1990
  const finalUrl = await client.evaluate('location.href');
1943
1991
  if (finalUrl && finalUrl.includes('chatgpt.com')) {
1944
- await saveAgentSession('chatgpt', finalUrl);
1992
+ await saveAgentSession('chatgpt', getBaseUrl('chatgpt', finalUrl));
1945
1993
  }
1946
1994
  timings.waitResponseMs = nowMs() - tWaitResp;
1947
1995
  timings.totalMs = nowMs() - t0;
@@ -2053,7 +2101,7 @@ async function askChatGPTFastInternal(question, debug) {
2053
2101
  * Driver経由でChatGPTに質問(実験的)
2054
2102
  * 環境変数 CAI_USE_DRIVERS=1 で有効化
2055
2103
  */
2056
- async function askChatGPTViaDriver(question, debug) {
2104
+ async function askChatGPTViaDriver(question, debug, budgetMs) {
2057
2105
  const t0 = nowMs();
2058
2106
  const timings = {};
2059
2107
  // 接続
@@ -2082,7 +2130,7 @@ async function askChatGPTViaDriver(question, debug) {
2082
2130
  timings.sendMs = nowMs() - tSend;
2083
2131
  // 応答待機
2084
2132
  const tWaitResp = nowMs();
2085
- const driverWaitBudgetMs = getResponseWaitBudgetMs(t0, RESPONSE_WAIT_MAX_MS, 'chatgpt-driver-response');
2133
+ const driverWaitBudgetMs = getResponseWaitBudgetMs(t0, RESPONSE_WAIT_MAX_MS, 'chatgpt-driver-response', budgetMs);
2086
2134
  await driver.waitForResponse({ maxWaitMs: driverWaitBudgetMs });
2087
2135
  timings.waitResponseMs = nowMs() - tWaitResp;
2088
2136
  // 応答抽出
@@ -2096,7 +2144,7 @@ async function askChatGPTViaDriver(question, debug) {
2096
2144
  // セッション保存
2097
2145
  const finalUrl = await driver.getCurrentUrl();
2098
2146
  if (finalUrl.includes('chatgpt.com')) {
2099
- await saveAgentSession('chatgpt', finalUrl);
2147
+ await saveAgentSession('chatgpt', getBaseUrl('chatgpt', finalUrl));
2100
2148
  }
2101
2149
  timings.totalMs = nowMs() - t0;
2102
2150
  // 履歴保存
@@ -2120,7 +2168,7 @@ async function askChatGPTViaDriver(question, debug) {
2120
2168
  /**
2121
2169
  * Driver経由でGeminiに質問(実験的)
2122
2170
  */
2123
- async function askGeminiViaDriver(question, debug) {
2171
+ async function askGeminiViaDriver(question, debug, budgetMs) {
2124
2172
  const t0 = nowMs();
2125
2173
  const timings = {};
2126
2174
  // 接続
@@ -2150,7 +2198,7 @@ async function askGeminiViaDriver(question, debug) {
2150
2198
  timings.sendMs = nowMs() - tSend;
2151
2199
  // 応答待機
2152
2200
  const tWaitResp = nowMs();
2153
- const driverWaitBudgetMs = getResponseWaitBudgetMs(t0, RESPONSE_WAIT_MAX_MS, 'gemini-driver-response');
2201
+ const driverWaitBudgetMs = getResponseWaitBudgetMs(t0, RESPONSE_WAIT_MAX_MS, 'gemini-driver-response', budgetMs);
2154
2202
  await driver.waitForResponse({ maxWaitMs: driverWaitBudgetMs });
2155
2203
  timings.waitResponseMs = nowMs() - tWaitResp;
2156
2204
  // 応答抽出
@@ -2164,7 +2212,7 @@ async function askGeminiViaDriver(question, debug) {
2164
2212
  // セッション保存
2165
2213
  const finalUrl = await driver.getCurrentUrl();
2166
2214
  if (finalUrl.includes('gemini.google.com')) {
2167
- await saveAgentSession('gemini', finalUrl);
2215
+ await saveAgentSession('gemini', getBaseUrl('gemini', finalUrl));
2168
2216
  }
2169
2217
  timings.totalMs = nowMs() - t0;
2170
2218
  // 履歴保存
@@ -2190,24 +2238,24 @@ const USE_DRIVERS = process.env.CAI_USE_DRIVERS === '1';
2190
2238
  /**
2191
2239
  * ChatGPTに質問して回答を取得(後方互換用)
2192
2240
  */
2193
- export async function askChatGPTFast(question, debug) {
2241
+ export async function askChatGPTFast(question, debug, budgetMs) {
2194
2242
  if (USE_DRIVERS) {
2195
- const result = await askChatGPTViaDriver(question, debug);
2243
+ const result = await askChatGPTViaDriver(question, debug, budgetMs);
2196
2244
  return result.answer;
2197
2245
  }
2198
- const result = await askChatGPTFastInternal(question, debug);
2246
+ const result = await askChatGPTFastInternal(question, debug, budgetMs);
2199
2247
  return result.answer;
2200
2248
  }
2201
2249
  /**
2202
2250
  * ChatGPTに質問して回答とタイミング情報を取得
2203
2251
  */
2204
- export async function askChatGPTFastWithTimings(question, debug) {
2252
+ export async function askChatGPTFastWithTimings(question, debug, budgetMs) {
2205
2253
  if (USE_DRIVERS) {
2206
- return askChatGPTViaDriver(question, debug);
2254
+ return askChatGPTViaDriver(question, debug, budgetMs);
2207
2255
  }
2208
- return askChatGPTFastInternal(question, debug);
2256
+ return askChatGPTFastInternal(question, debug, budgetMs);
2209
2257
  }
2210
- async function askGeminiFastInternal(question, debug) {
2258
+ async function askGeminiFastInternal(question, debug, budgetMs) {
2211
2259
  const t0 = nowMs();
2212
2260
  const timings = {};
2213
2261
  const client = await getClient('gemini');
@@ -2234,31 +2282,15 @@ async function askGeminiFastInternal(question, debug) {
2234
2282
  // SPA描画安定化のため追加待機
2235
2283
  await new Promise(r => setTimeout(r, 500));
2236
2284
  console.error('[Gemini] Waited 500ms for SPA rendering');
2237
- // 既存チャット(URLにチャットIDが含まれる)の場合、メッセージが描画されるまで待機
2285
+ // 既存チャット(URLにチャットIDが含まれる)の場合、新規チャットへ遷移
2286
+ // 同じ会話に質問を投入すると前回のコンテキストが応答に影響するため
2238
2287
  const geminiCurrentUrl = await client.evaluate('location.href');
2239
- // 既存チャットのURLパターン: /app/xxxxx (チャットID)
2240
2288
  const isExistingGeminiChat = /\/app\/[a-zA-Z0-9]+/.test(geminiCurrentUrl);
2241
2289
  if (isExistingGeminiChat) {
2242
- try {
2243
- await client.waitForFunction(`document.querySelectorAll('model-response, .model-response').length > 0`, 5000);
2244
- console.error('[Gemini] Existing chat messages loaded');
2245
- // 既存チャットの状態をチェック(停止ボタンがスタックしていないか)
2246
- const stuckCheckResult = await checkGeminiStuckState(client);
2247
- if (stuckCheckResult.isStuck) {
2248
- console.error(`[Gemini] Existing chat appears stuck (stop button detected for ${stuckCheckResult.waitedMs}ms). Clearing session and retrying.`);
2249
- // 協調クリーンアップ(RelayServer + Client + Session を一括リセット)
2250
- await resetConnection('gemini');
2251
- // エラーを投げて、呼び出し元でリトライを促す
2252
- throw new Error('GEMINI_STUCK_EXISTING_CHAT: Previous chat appears stuck (stop button visible). Session cleared, please retry.');
2253
- }
2254
- }
2255
- catch (error) {
2256
- // GEMINI_STUCK_* エラーは再スロー(リトライ用)
2257
- if (error instanceof Error && error.message.includes('GEMINI_STUCK_')) {
2258
- throw error;
2259
- }
2260
- console.error('[Gemini] No existing messages found, continuing as new chat');
2261
- }
2290
+ console.error('[Gemini] Existing conversation detected, navigating to new chat...');
2291
+ await navigate(client, 'https://gemini.google.com/');
2292
+ await new Promise(r => setTimeout(r, 500));
2293
+ console.error('[Gemini] New chat page loaded');
2262
2294
  }
2263
2295
  const tWaitInput = nowMs();
2264
2296
  await client.waitForFunction(`!!document.querySelector('[role="textbox"], div[contenteditable="true"], textarea') || !!document.querySelector('a[href*="accounts.google.com"]')`, 15000);
@@ -2680,7 +2712,7 @@ async function askGeminiFastInternal(question, debug) {
2680
2712
  const tWaitResp = nowMs();
2681
2713
  console.error('[Gemini] Waiting for response completion (polling with diagnostics)...');
2682
2714
  // ChatGPT側と同様のポーリングループで応答完了を検出
2683
- const maxWaitMs = getResponseWaitBudgetMs(t0, RESPONSE_WAIT_MAX_MS, 'gemini-response');
2715
+ const maxWaitMs = getResponseWaitBudgetMs(t0, RESPONSE_WAIT_MAX_MS, 'gemini-response', budgetMs);
2684
2716
  const pollIntervalMs = 1000;
2685
2717
  const startWait = Date.now();
2686
2718
  let lastLoggedState = '';
@@ -2953,7 +2985,7 @@ async function askGeminiFastInternal(question, debug) {
2953
2985
  console.error(`[Gemini] Response extracted: ${normalized.slice(0, 100)}...`);
2954
2986
  const finalUrl = await client.evaluate('location.href');
2955
2987
  if (finalUrl && finalUrl.includes('gemini.google.com')) {
2956
- await saveAgentSession('gemini', finalUrl);
2988
+ await saveAgentSession('gemini', getBaseUrl('gemini', finalUrl));
2957
2989
  }
2958
2990
  timings.waitResponseMs = nowMs() - tWaitResp;
2959
2991
  timings.totalMs = nowMs() - t0;
@@ -3054,22 +3086,22 @@ async function askGeminiFastInternal(question, debug) {
3054
3086
  /**
3055
3087
  * Geminiに質問して回答を取得(後方互換用)
3056
3088
  */
3057
- export async function askGeminiFast(question, debug) {
3089
+ export async function askGeminiFast(question, debug, budgetMs) {
3058
3090
  if (USE_DRIVERS) {
3059
- const result = await askGeminiViaDriver(question, debug);
3091
+ const result = await askGeminiViaDriver(question, debug, budgetMs);
3060
3092
  return result.answer;
3061
3093
  }
3062
- const result = await askGeminiFastInternal(question, debug);
3094
+ const result = await askGeminiFastInternal(question, debug, budgetMs);
3063
3095
  return result.answer;
3064
3096
  }
3065
3097
  /**
3066
3098
  * Geminiに質問して回答とタイミング情報を取得
3067
3099
  */
3068
- export async function askGeminiFastWithTimings(question, debug) {
3100
+ export async function askGeminiFastWithTimings(question, debug, budgetMs) {
3069
3101
  if (USE_DRIVERS) {
3070
- return askGeminiViaDriver(question, debug);
3102
+ return askGeminiViaDriver(question, debug, budgetMs);
3071
3103
  }
3072
- return askGeminiFastInternal(question, debug);
3104
+ return askGeminiFastInternal(question, debug, budgetMs);
3073
3105
  }
3074
3106
  export async function takeCdpSnapshot(kind, options) {
3075
3107
  const result = {
@@ -5,9 +5,9 @@
5
5
  */
6
6
  import fs from 'node:fs';
7
7
  import debug from 'debug';
8
- const mcpDebugNamespace = 'mcp:log';
8
+ const cabDebugNamespace = 'cab:log';
9
9
  const namespacesToEnable = [
10
- mcpDebugNamespace,
10
+ cabDebugNamespace,
11
11
  ...(process.env['DEBUG'] ? [process.env['DEBUG']] : []),
12
12
  ];
13
13
  export function saveLogsToFile(fileName) {
@@ -24,4 +24,4 @@ export function saveLogsToFile(fileName) {
24
24
  });
25
25
  return logFile;
26
26
  }
27
- export const logger = debug(mcpDebugNamespace);
27
+ export const logger = debug(cabDebugNamespace);