@narumitw/pi-chrome-devtools 0.52.2 → 0.52.3

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/README.md CHANGED
@@ -14,7 +14,7 @@ The design is inspired by [`chrome-devtools-mcp`](https://github.com/ChromeDevTo
14
14
  - Reuses an existing CDP endpoint or lazily launches an isolated Chromium-family browser.
15
15
  - Recovers from stale page selections and reports actionable browser startup or endpoint errors.
16
16
  - Loads explicitly approved unpacked extensions only in an extension-owned Chrome for Testing or Chromium process.
17
- - Keeps browser tools lazy and exposes availability, setup, status, and help through `/chrome-devtools`.
17
+ - Uses native deferred browser tools when supported and eager exposure otherwise, with availability, setup, status, and help through `/chrome-devtools`.
18
18
  - Shows compact expandable results and activity only while browser tools are running.
19
19
  - Persists reviewed tool availability while keeping browser connection settings machine-owned.
20
20
 
@@ -148,21 +148,29 @@ It never closes user-started browsers or remote endpoints.
148
148
  - `chrome_devtools_evaluate` — evaluate JavaScript in the selected page.
149
149
  - `chrome_devtools_screenshot` — capture a PNG screenshot and save it as a PNG file.
150
150
 
151
- ### Lazy tool loading
151
+ ### Tool exposure
152
152
 
153
- All six tools are registered, but only `chrome_devtools_load` starts active for this extension.
153
+ All six tools are registered.
154
+
155
+ On a model/provider with native deferred-tool support, only `chrome_devtools_load` starts active for this extension.
154
156
 
155
157
  The loader accepts a task-oriented `query`, matches it against the five capability tools, and adds matching available tools without removing any active Pi tool.
156
158
 
157
159
  Loaded capability tools remain active for the rest of the session unless the user makes them unavailable through `/chrome-devtools`.
158
160
 
159
- Pi uses native deferred tool references on compatible Anthropic and OpenAI models.
161
+ Pi uses native deferred tool references on compatible Anthropic models, native additional-tools or tool-search loading on compatible OpenAI and Codex Responses models, and native Kimi loading on compatible OpenAI Chat Completions models.
162
+
163
+ Kimi-compatible models declare `compat.deferredToolsMode: "kimi"` in Pi's model metadata.
164
+
165
+ `azure-openai-responses` remains eager because Pi's Azure adapter does not implement native deferred tool-search serialization.
166
+
167
+ When the selected model/provider lacks native deferred support, the extension activates every capability allowed by settings before the next model request instead of using Pi's cache-invalidating lazy-loading fallback.
160
168
 
161
- Other models receive Pi's safe fallback: the newly active definitions appear in the normal tool list on the next model request.
169
+ After a session enters eager exposure, it stays eager across later model switches to avoid removing tool definitions within that session.
162
170
 
163
- The capability tools omit active-only prompt snippets so a lazy load does not rebuild the system prompt prefix.
171
+ The capability tools omit active-only prompt snippets so native deferred loading does not rebuild the system-prompt prefix.
164
172
 
165
- The saved `tools` array controls which capabilities the loader may expose.
173
+ The saved `tools` array controls which capabilities the extension may expose.
166
174
 
167
175
  An empty array leaves the loader active but makes every browser capability unavailable.
168
176
 
@@ -198,7 +206,7 @@ If the model cannot inspect the inline image, ask it to read the saved path, for
198
206
  /chrome-devtools
199
207
  ```
200
208
 
201
- Opens a menu that shows the lazy catalog size, whether that catalog is saved, the configured endpoint, the observed managed-browser state, and any settings or launch warning before you choose an action.
209
+ Opens a menu that shows the tool catalog size, whether that catalog is saved, the configured endpoint, the observed managed-browser state, and any settings or launch warning before you choose an action.
202
210
  The five actions stay on one level:
203
211
 
204
212
  - **Choose available browser tools…** — stage any combination of the five capabilities, then review the exact available/unavailable result before selecting **Apply tool changes**.
@@ -235,7 +243,7 @@ Compatibility aliases remain available: `toggle` and `select` mean `tools`, `on`
235
243
  - `settings` opens the same immediate-save browser settings flow used by the menu.
236
244
  - `tools` opens the same staged, width-safe availability and review flow used by the menu.
237
245
  - `toggle` and `select` are compatibility aliases for `tools`.
238
- - `enable` makes all five capability tools available to the loader and saves that catalog; `on` is a compatibility alias.
246
+ - `enable` makes all five capability tools available and follows the current native-deferred or eager exposure mode; `on` is a compatibility alias.
239
247
  - `disable` makes all five capability tools unavailable and saves the empty catalog; `off` is a compatibility alias.
240
248
  The slash command and `chrome_devtools_load` remain available.
241
249
 
@@ -258,7 +266,7 @@ Confirmed menu changes apply before the next browser connection and close only a
258
266
  Manual JSON edits and unpacked-extension changes apply after `/reload` or session replacement.
259
267
 
260
268
  When the file is missing or invalid, the extension preserves Pi's current Chrome DevTools availability policy instead of replacing it.
261
- A valid saved catalog is restored on Pi startup and `/reload`, while its capability definitions remain deferred.
269
+ A valid saved catalog is restored on Pi startup and `/reload`, with capability definitions exposed natively deferred or eagerly according to model/provider support.
262
270
  A missing file is created by the first confirmed browser or tool setting.
263
271
  Within one Pi process, all browser and tool saves run in invocation order, reread the latest valid document, publish by temporary-file rename, and preserve unknown fields.
264
272
  Malformed JSON or invalid recognized fields make menu mutation unavailable and block direct saves without replacement; a failed save restores the prior displayed and effective state.
@@ -8,7 +8,7 @@ import {
8
8
  saveBrowserSettings,
9
9
  shutdownManagedBrowser,
10
10
  state
11
- } from "./chunk-CC37WUUE.js";
11
+ } from "./chunk-RH6KESL3.js";
12
12
 
13
13
  // src/browser-settings-menu.ts
14
14
  import { isAbsolute } from "node:path";
@@ -263,4 +263,4 @@ function formatError(error) {
263
263
  export {
264
264
  showChromeDevtoolsBrowserSettings
265
265
  };
266
- //# sourceMappingURL=browser-settings-menu-VI2I3TXI.js.map
266
+ //# sourceMappingURL=browser-settings-menu-VWCNG4HB.js.map
@@ -1250,7 +1250,9 @@ var DevToolsEndpointError = class extends Error {
1250
1250
  };
1251
1251
 
1252
1252
  // src/lazy-tools.ts
1253
- import { defineTool } from "@earendil-works/pi-coding-agent";
1253
+ import {
1254
+ defineTool
1255
+ } from "@earendil-works/pi-coding-agent";
1254
1256
  import { Type } from "typebox";
1255
1257
  var CHROME_DEVTOOLS_LOAD_TOOL_NAME = "chrome_devtools_load";
1256
1258
  var AVAILABLE_TOOLS_STORE = /* @__PURE__ */ Symbol.for("@narumitw/pi-chrome-devtools.available-tools-store");
@@ -1258,6 +1260,7 @@ var sharedGlobal = globalThis;
1258
1260
  var existingAvailableToolsStore = sharedGlobal[AVAILABLE_TOOLS_STORE];
1259
1261
  var availableToolsByApi = existingAvailableToolsStore ?? /* @__PURE__ */ new WeakMap();
1260
1262
  if (!existingAvailableToolsStore) sharedGlobal[AVAILABLE_TOOLS_STORE] = availableToolsByApi;
1263
+ var lazyExposureByApi = /* @__PURE__ */ new WeakMap();
1261
1264
  var SEARCH_TEXT = {
1262
1265
  chrome_devtools_list_pages: "list open inspectable chrome browser pages tabs targets",
1263
1266
  chrome_devtools_select_page: "select choose active chrome browser page tab target",
@@ -1273,17 +1276,53 @@ function initializeAvailableChromeDevtoolsTools(pi) {
1273
1276
  CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => activeTools.has(name))
1274
1277
  );
1275
1278
  }
1276
- function configureLazyChromeDevtoolsTools(pi, availableTools) {
1277
- setAvailableTools(pi, availableTools);
1279
+ function configureChromeDevtoolsToolExposure(pi, availableTools, model) {
1280
+ const available = setAvailableTools(pi, availableTools);
1281
+ const lazyExposure = supportsNativeDeferredToolLoading(model);
1282
+ lazyExposureByApi.set(pi, lazyExposure);
1283
+ const exposedTools = lazyExposure ? [] : CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => available.has(name));
1278
1284
  const nonCapabilityTools = pi.getActiveTools().filter((name) => !CHROME_DEVTOOLS_TOOL_NAMES.includes(name));
1279
- pi.setActiveTools(unique([...nonCapabilityTools, CHROME_DEVTOOLS_LOAD_TOOL_NAME]));
1285
+ pi.setActiveTools(
1286
+ unique([...nonCapabilityTools, CHROME_DEVTOOLS_LOAD_TOOL_NAME, ...exposedTools])
1287
+ );
1288
+ }
1289
+ function requireEagerChromeDevtoolsToolExposure(pi) {
1290
+ lazyExposureByApi.set(pi, false);
1291
+ const active = pi.getActiveTools();
1292
+ const available = availableChromeDevtoolsTools(pi);
1293
+ pi.setActiveTools(unique([...active, CHROME_DEVTOOLS_LOAD_TOOL_NAME, ...available]));
1280
1294
  }
1281
1295
  function applyAvailableChromeDevtoolsTools(pi, availableTools) {
1282
1296
  const available = setAvailableTools(pi, availableTools);
1297
+ const lazyExposure = lazyExposureByApi.get(pi) === true;
1283
1298
  const active = pi.getActiveTools().filter(
1284
- (name) => !CHROME_DEVTOOLS_TOOL_NAMES.includes(name) || available.has(name)
1299
+ (name) => !CHROME_DEVTOOLS_TOOL_NAMES.includes(name) || lazyExposure && available.has(name)
1285
1300
  );
1286
- pi.setActiveTools(unique([...active, CHROME_DEVTOOLS_LOAD_TOOL_NAME]));
1301
+ const eagerTools = lazyExposure ? [] : CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => available.has(name));
1302
+ pi.setActiveTools(unique([...active, CHROME_DEVTOOLS_LOAD_TOOL_NAME, ...eagerTools]));
1303
+ }
1304
+ function chromeDevtoolsToolExposureMode(pi) {
1305
+ return lazyExposureByApi.get(pi) === true ? "native deferred" : "eager";
1306
+ }
1307
+ function supportsNativeDeferredToolLoading(model) {
1308
+ if (!model) return false;
1309
+ if (model.api === "anthropic-messages") {
1310
+ const configured = compatBoolean(model.compat, "supportsToolReferences");
1311
+ if (configured !== void 0) return configured;
1312
+ if (model.provider !== "anthropic" || model.id.includes("haiku")) return false;
1313
+ const version = model.id.match(/^claude-(?:opus|sonnet|fable)-(\d+)(?:-(\d+))?(?:-|$)/);
1314
+ if (!version) return false;
1315
+ const major = Number(version[1]);
1316
+ const minor = version[2] && version[2].length < 8 ? Number(version[2]) : 0;
1317
+ return major > 4 || major === 4 && minor >= 5;
1318
+ }
1319
+ if (model.api === "openai-completions") {
1320
+ return compatString(model.compat, "deferredToolsMode") === "kimi";
1321
+ }
1322
+ if (model.api === "openai-responses" || model.api === "openai-codex-responses") {
1323
+ return compatBoolean(model.compat, "supportsAdditionalTools") === true || compatBoolean(model.compat, "supportsToolSearch") === true;
1324
+ }
1325
+ return false;
1287
1326
  }
1288
1327
  function availableChromeDevtoolsTools(pi) {
1289
1328
  const available = availableToolsByApi.get(pi) ?? /* @__PURE__ */ new Set();
@@ -1343,6 +1382,16 @@ function setAvailableTools(pi, availableTools) {
1343
1382
  availableToolsByApi.set(pi, available);
1344
1383
  return available;
1345
1384
  }
1385
+ function compatBoolean(value, key) {
1386
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
1387
+ const record = value;
1388
+ return typeof record[key] === "boolean" ? record[key] : void 0;
1389
+ }
1390
+ function compatString(value, key) {
1391
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
1392
+ const record = value;
1393
+ return typeof record[key] === "string" ? record[key] : void 0;
1394
+ }
1346
1395
  function unique(values) {
1347
1396
  return [...new Set(values)];
1348
1397
  }
@@ -1361,7 +1410,7 @@ async function updateChromeDevtoolsTools(pi, ctx, selectedTools, action) {
1361
1410
  if (result !== "saved" || generation !== state.sessionGeneration) return;
1362
1411
  const status = await buildToolStatusMessage(pi);
1363
1412
  if (generation !== state.sessionGeneration) return;
1364
- ctx.ui.notify(`Chrome DevTools lazy catalog ${action}.
1413
+ ctx.ui.notify(`Chrome DevTools tool catalog ${action}.
1365
1414
 
1366
1415
  ${status}`, "info");
1367
1416
  }
@@ -1411,7 +1460,8 @@ async function transactSelectedToolsNow(pi, ctx, selectedTools, expectedGenerati
1411
1460
  const previousLoadedChromeTools = previousActiveTools.filter(
1412
1461
  (name) => CHROME_DEVTOOLS_TOOL_NAMES.includes(name)
1413
1462
  );
1414
- pi.setActiveTools(unique2([...currentNonChromeTools, ...previousLoadedChromeTools]));
1463
+ const restoredChromeTools = chromeDevtoolsToolExposureMode(pi) === "eager" ? previousAvailableTools : previousLoadedChromeTools;
1464
+ pi.setActiveTools(unique2([...currentNonChromeTools, ...restoredChromeTools]));
1415
1465
  } catch (caught) {
1416
1466
  rollbackError = caught;
1417
1467
  }
@@ -1454,10 +1504,11 @@ async function buildToolStatusMessage(pi) {
1454
1504
  const persistedSetting = await persistedSettingLabel();
1455
1505
  return sanitizeChromeDevtoolsDisplay(
1456
1506
  [
1457
- `Chrome DevTools tools available to lazy-load: ${formatRuntimeStatus(summary)}`,
1507
+ `Chrome DevTools tools available: ${formatRuntimeStatus(summary)}`,
1508
+ `Tool exposure: ${chromeDevtoolsToolExposureMode(pi)}`,
1458
1509
  `Loaded capability tools this session: ${summary.loadedChromeToolCount}/${CHROME_DEVTOOLS_TOOL_NAMES.length}`,
1459
1510
  `Loader: ${pi.getActiveTools().includes(CHROME_DEVTOOLS_LOAD_TOOL_NAME) ? "active" : "inactive"}`,
1460
- `Persisted lazy catalog: ${persistedSetting}`,
1511
+ `Persisted tool catalog: ${persistedSetting}`,
1461
1512
  ...browserSettingsStatusLines(),
1462
1513
  ...state.settingsNotice ? [`Settings note: ${state.settingsNotice}`] : [],
1463
1514
  `Other active tools preserved: ${summary.activeNonChromeToolCount}`,
@@ -1537,9 +1588,9 @@ function buildCommandGuide() {
1537
1588
  "/chrome-devtools quickstart \u2014 show endpoint and launch help",
1538
1589
  "/chrome-devtools status \u2014 show tool and settings status",
1539
1590
  "/chrome-devtools settings \u2014 edit browser connection settings",
1540
- "/chrome-devtools tools \u2014 choose tools available to lazy-load",
1591
+ "/chrome-devtools tools \u2014 choose available Chrome DevTools tools",
1541
1592
  "/chrome-devtools toggle|select \u2014 compatibility aliases for tools",
1542
- "/chrome-devtools enable|on \u2014 make all Chrome DevTools tools available to lazy-load",
1593
+ "/chrome-devtools enable|on \u2014 make all Chrome DevTools tools available",
1543
1594
  "/chrome-devtools disable|off \u2014 make all Chrome DevTools capability tools unavailable"
1544
1595
  ].join("\n");
1545
1596
  }
@@ -1591,7 +1642,9 @@ export {
1591
1642
  launchHint,
1592
1643
  formatPageListItem,
1593
1644
  initializeAvailableChromeDevtoolsTools,
1594
- configureLazyChromeDevtoolsTools,
1645
+ configureChromeDevtoolsToolExposure,
1646
+ requireEagerChromeDevtoolsToolExposure,
1647
+ supportsNativeDeferredToolLoading,
1595
1648
  availableChromeDevtoolsTools,
1596
1649
  createChromeDevtoolsLoadTool,
1597
1650
  updateChromeDevtoolsTools,
@@ -1604,4 +1657,4 @@ export {
1604
1657
  buildCommandGuide,
1605
1658
  allChromeDevtoolsTools
1606
1659
  };
1607
- //# sourceMappingURL=chunk-CC37WUUE.js.map
1660
+ //# sourceMappingURL=chunk-RH6KESL3.js.map