@herbertgao/pi-extensions 2026.8.15 → 2026.9.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.
Files changed (59) hide show
  1. package/README.md +4 -1
  2. package/THIRD_PARTY_NOTICES.md +26 -0
  3. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +20 -0
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +46 -3
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +6 -0
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +5 -0
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +34 -30
  9. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  10. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  11. package/node_modules/@narumitw/pi-btw/README.md +5 -1
  12. package/node_modules/@narumitw/pi-btw/dist/index.ts +143 -14
  13. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
  14. package/node_modules/@narumitw/pi-btw/package.json +4 -4
  15. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +158 -7
  16. package/node_modules/@narumitw/pi-btw/src/text.ts +18 -0
  17. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +5 -16
  18. package/node_modules/@narumitw/pi-caffeinate/LICENSE +21 -0
  19. package/node_modules/@narumitw/pi-caffeinate/README.md +224 -0
  20. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts +1112 -0
  21. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts.map +7 -0
  22. package/node_modules/@narumitw/pi-caffeinate/package.json +53 -0
  23. package/node_modules/@narumitw/pi-caffeinate/src/caffeinate.ts +811 -0
  24. package/node_modules/@narumitw/pi-caffeinate/src/dbus-inhibit.ts +153 -0
  25. package/node_modules/@narumitw/pi-caffeinate/src/index.ts +1 -0
  26. package/node_modules/@narumitw/pi-caffeinate/src/inhibitor-process.ts +43 -0
  27. package/node_modules/@narumitw/pi-caffeinate/src/inhibitors.ts +167 -0
  28. package/node_modules/@narumitw/pi-caffeinate/src/settings.ts +202 -0
  29. package/node_modules/@tifan/pi-handoff/README.md +6 -3
  30. package/node_modules/@tifan/pi-handoff/package.json +1 -1
  31. package/node_modules/@tifan/pi-handoff/src/index.ts +8 -3
  32. package/node_modules/pi-mcp-adapter/CHANGELOG.md +27 -0
  33. package/node_modules/pi-mcp-adapter/README.md +13 -13
  34. package/node_modules/pi-mcp-adapter/commands.ts +58 -21
  35. package/node_modules/pi-mcp-adapter/config.ts +18 -4
  36. package/node_modules/pi-mcp-adapter/direct-tools.ts +26 -6
  37. package/node_modules/pi-mcp-adapter/dist/config.d.ts +4 -0
  38. package/node_modules/pi-mcp-adapter/dist/config.js +13 -4
  39. package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
  40. package/node_modules/pi-mcp-adapter/dist/types.d.ts +11 -3
  41. package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -1
  42. package/node_modules/pi-mcp-adapter/error-signal.ts +15 -4
  43. package/node_modules/pi-mcp-adapter/errors.ts +141 -0
  44. package/node_modules/pi-mcp-adapter/host-html-template.ts +58 -5
  45. package/node_modules/pi-mcp-adapter/index.ts +2 -3
  46. package/node_modules/pi-mcp-adapter/init.ts +5 -0
  47. package/node_modules/pi-mcp-adapter/mcp-panel.ts +30 -9
  48. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +66 -28
  49. package/node_modules/pi-mcp-adapter/mcp-status.ts +2 -0
  50. package/node_modules/pi-mcp-adapter/package.json +2 -1
  51. package/node_modules/pi-mcp-adapter/proxy-modes.ts +66 -13
  52. package/node_modules/pi-mcp-adapter/sandbox-proxy-template.ts +217 -0
  53. package/node_modules/pi-mcp-adapter/server-manager.ts +337 -6
  54. package/node_modules/pi-mcp-adapter/skills/mcp-scripting/SKILL.md +1 -0
  55. package/node_modules/pi-mcp-adapter/types.ts +18 -3
  56. package/node_modules/pi-mcp-adapter/ui-resource-handler.ts +18 -2
  57. package/node_modules/pi-mcp-adapter/ui-server.ts +179 -54
  58. package/node_modules/pi-mcp-adapter/ui-session.ts +28 -8
  59. package/package.json +11 -7
@@ -12,6 +12,11 @@ import type { ConsentManager } from "./consent-manager.ts";
12
12
  import { ServerError, wrapError } from "./errors.ts";
13
13
  import { formatAuthRequiredMessage, normalizeToolArguments } from "./utils.ts";
14
14
  import { buildHostHtmlTemplate, buildCspMetaContent } from "./host-html-template.ts";
15
+ import {
16
+ buildSandboxProxyCsp,
17
+ buildSandboxProxyHtml,
18
+ SANDBOX_PROXY_PATH,
19
+ } from "./sandbox-proxy-template.ts";
15
20
  import { logger } from "./logger.ts";
16
21
  import type { McpServerManager } from "./server-manager.ts";
17
22
  import type { McpExtensionState } from "./state.ts";
@@ -99,6 +104,11 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
99
104
  const eventLog: Array<{ id: number; name: string; payload: unknown }> = [];
100
105
  let latestCheckpointEventId: number | undefined;
101
106
  let streamSummary: UiStreamSummary | undefined;
107
+ let server: http.Server | null = null;
108
+ let sandboxProxyServer: http.Server | null = null;
109
+ let sandboxProxyUrl: string | null = null;
110
+ let closeTimer: NodeJS.Timeout | null = null;
111
+ let listenersClosed = false;
102
112
 
103
113
  // Track messages from UI for retrieval
104
114
  const sessionMessages: UiSessionMessages = {
@@ -266,6 +276,32 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
266
276
  watchdog = null;
267
277
  };
268
278
 
279
+ const closeListeners = () => {
280
+ if (listenersClosed) return;
281
+ listenersClosed = true;
282
+ stopWatchdog();
283
+ if (closeTimer) {
284
+ clearTimeout(closeTimer);
285
+ closeTimer = null;
286
+ }
287
+ try {
288
+ server?.close();
289
+ } catch {}
290
+ try {
291
+ sandboxProxyServer?.close();
292
+ } catch {}
293
+ closeSse();
294
+ };
295
+
296
+ const scheduleListenerClose = () => {
297
+ if (closeTimer || listenersClosed) return;
298
+ closeTimer = setTimeout(() => {
299
+ closeTimer = null;
300
+ closeListeners();
301
+ }, 20);
302
+ closeTimer.unref();
303
+ };
304
+
269
305
  const markCompleted = (reason: string) => {
270
306
  if (completed) return;
271
307
  log.debug("Session completed", { reason });
@@ -273,9 +309,10 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
273
309
  completed = true;
274
310
  stopWatchdog();
275
311
  options.onComplete?.(reason);
312
+ scheduleListenerClose();
276
313
  };
277
314
 
278
- const server = http.createServer(async (req, res) => {
315
+ const hostServer = http.createServer(async (req, res) => {
279
316
  try {
280
317
  const method = req.method || "GET";
281
318
  const hostHeader = req.headers.host;
@@ -302,6 +339,10 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
302
339
  }
303
340
  if (!validateTokenQuery(url, sessionToken, res)) return;
304
341
  touchHeartbeat();
342
+ if (!sandboxProxyUrl) {
343
+ sendText(res, 503, "Sandbox proxy is not ready");
344
+ return;
345
+ }
305
346
 
306
347
  const html = buildHostHtmlTemplate({
307
348
  sessionToken,
@@ -314,6 +355,7 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
314
355
  requireToolConsent: options.consentManager.requiresPrompt(options.serverName),
315
356
  cacheToolConsent: options.consentManager.shouldCacheConsent(),
316
357
  hostContext,
358
+ sandboxProxyUrl,
317
359
  });
318
360
 
319
361
  res.writeHead(200, {
@@ -498,9 +540,15 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
498
540
  ...(options.onNeedsAuth ? { onNeedsAuth: options.onNeedsAuth } : {}),
499
541
  },
500
542
  options.serverName,
501
- (conn) => conn.client.callTool(callArgs, options.manager.getRequestOptions?.(options.serverName)),
543
+ async (conn) => {
544
+ await options.manager.ensureListen?.(options.serverName, conn);
545
+ return conn.client.callTool(callArgs, options.manager.getRequestOptions?.(options.serverName));
546
+ },
502
547
  )
503
- : await connection.client.callTool(callArgs, options.manager.getRequestOptions?.(options.serverName));
548
+ : await (async () => {
549
+ await options.manager.ensureListen?.(options.serverName, connection);
550
+ return connection.client.callTool(callArgs, options.manager.getRequestOptions?.(options.serverName));
551
+ })();
504
552
  sendJson(res, 200, { ok: true, result });
505
553
  } finally {
506
554
  options.manager.decrementInFlight(options.serverName);
@@ -614,12 +662,6 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
614
662
  : "done";
615
663
  markCompleted(reason);
616
664
  sendJson(res, 200, { ok: true, result: {} });
617
- setTimeout(() => {
618
- try {
619
- server.close();
620
- } catch {}
621
- closeSse();
622
- }, 20).unref();
623
665
  return;
624
666
  }
625
667
 
@@ -641,6 +683,7 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
641
683
  sendJson(res, status, { ok: false, error: wrapped.message });
642
684
  }
643
685
  });
686
+ server = hostServer;
644
687
 
645
688
  if (options.initialResultPromise) {
646
689
  options.initialResultPromise.then(
@@ -656,10 +699,6 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
656
699
  if (completed) return;
657
700
  if (Date.now() - lastHeartbeatAt <= ABANDONED_GRACE_MS) return;
658
701
  markCompleted("stale");
659
- try {
660
- server.close();
661
- } catch {}
662
- closeSse();
663
702
  }, WATCHDOG_INTERVAL_MS);
664
703
  watchdog.unref();
665
704
 
@@ -667,13 +706,79 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
667
706
  const candidates = resolvePortCandidates(options.port);
668
707
  let candidateIndex = 0;
669
708
 
709
+ const startSandboxProxy = (parentOrigin: string): Promise<number> => {
710
+ const proxy = http.createServer((req, res) => {
711
+ try {
712
+ const method = req.method || "GET";
713
+ const hostHeader = req.headers.host;
714
+ const url = new URL(req.url || "/", `http://${hostHeader || "127.0.0.1"}`);
715
+ if (hostHeader !== undefined && !isAllowedHost(url.hostname)) {
716
+ sendText(res, 403, "Invalid host");
717
+ return;
718
+ }
719
+
720
+ if (method === "HEAD" && url.pathname === SANDBOX_PROXY_PATH) {
721
+ res.writeHead(200, {
722
+ "Content-Type": "text/html; charset=utf-8",
723
+ "Cache-Control": "no-store",
724
+ "Content-Security-Policy": buildSandboxProxyCsp(),
725
+ });
726
+ res.end();
727
+ return;
728
+ }
729
+
730
+ if (method === "GET" && url.pathname === SANDBOX_PROXY_PATH) {
731
+ res.writeHead(200, {
732
+ "Content-Type": "text/html; charset=utf-8",
733
+ "Cache-Control": "no-store",
734
+ "Content-Security-Policy": buildSandboxProxyCsp(),
735
+ "Referrer-Policy": "no-referrer",
736
+ "X-Content-Type-Options": "nosniff",
737
+ });
738
+ res.end(buildSandboxProxyHtml({ parentOrigin }));
739
+ return;
740
+ }
741
+
742
+ sendJson(res, 404, { ok: false, error: "Not found" });
743
+ } catch (error) {
744
+ sendJson(res, 500, { ok: false, error: error instanceof Error ? error.message : String(error) });
745
+ }
746
+ });
747
+ sandboxProxyServer = proxy;
748
+
749
+ return new Promise((resolveProxy, rejectProxy) => {
750
+ const onError = (error: NodeJS.ErrnoException) => {
751
+ proxy.off("listening", onListening);
752
+ rejectProxy(error);
753
+ };
754
+ const onListening = () => {
755
+ proxy.off("error", onError);
756
+ const address = proxy.address();
757
+ if (!address || typeof address === "string") {
758
+ rejectProxy(new ServerError("invalid sandbox proxy address"));
759
+ return;
760
+ }
761
+ resolveProxy(address.port);
762
+ };
763
+ proxy.once("error", onError);
764
+ proxy.listen(0, "127.0.0.1", onListening);
765
+ });
766
+ };
767
+
670
768
  const listen = () => {
671
- server.once("error", onError);
672
- server.listen(candidates[candidateIndex], "127.0.0.1", onListening);
769
+ const candidate = candidates[candidateIndex];
770
+ if (candidate === undefined) {
771
+ const error = new ServerError("no UI server port candidates available");
772
+ closeListeners();
773
+ reject(error);
774
+ return;
775
+ }
776
+ hostServer.once("error", onError);
777
+ hostServer.listen(candidate, "127.0.0.1", onListening);
673
778
  };
674
779
 
675
780
  const onError = (error: NodeJS.ErrnoException) => {
676
- server.off("listening", onListening);
781
+ hostServer.off("listening", onListening);
677
782
  if (error.code === "EADDRINUSE" && candidateIndex < candidates.length - 1) {
678
783
  candidateIndex += 1;
679
784
  listen();
@@ -681,6 +786,7 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
681
786
  }
682
787
  log.error("Failed to start server", error);
683
788
  const port = candidates[candidateIndex];
789
+ closeListeners();
684
790
  reject(new ServerError(error.message, {
685
791
  ...(port !== undefined ? { port } : {}),
686
792
  cause: error,
@@ -688,52 +794,71 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
688
794
  };
689
795
 
690
796
  const onListening = () => {
691
- server.off("error", onError);
692
- const address = server.address();
797
+ hostServer.off("error", onError);
798
+ const address = hostServer.address();
693
799
  if (!address || typeof address === "string") {
694
800
  const err = new ServerError("invalid address");
695
801
  log.error("Invalid server address", err);
802
+ closeListeners();
696
803
  reject(err);
697
804
  return;
698
805
  }
699
806
 
700
- log.debug("Server started", { port: address.port });
701
- rememberMoshiDiscoveryPort(address.port);
702
-
703
- const handle: UiServerHandle = {
704
- url: `http://localhost:${address.port}/?session=${sessionToken}`,
705
- port: address.port,
706
- sessionToken,
707
- serverName: options.serverName,
708
- toolName: options.toolName,
709
- close: (reason?: string) => {
710
- markCompleted(reason ?? "closed");
711
- try {
712
- server.close();
713
- } catch {}
714
- closeSse();
715
- },
716
- sendToolInput: (args: Record<string, unknown>) => {
717
- pushEvent("tool-input", { arguments: args });
718
- },
719
- sendToolResult: (result: CallToolResult) => {
720
- pushEvent("tool-result", result);
721
- },
722
- sendResultPatch: (result: CallToolResult) => {
723
- pushEvent("result-patch", result);
724
- },
725
- sendToolCancelled: (reason: string) => {
726
- pushEvent("tool-cancelled", { reason });
727
- },
728
- sendHostContext: (context: UiHostContext) => {
729
- Object.assign(hostContext, context);
730
- pushEvent("host-context", context);
731
- },
732
- getSessionMessages: () => ({ ...sessionMessages }),
733
- getStreamSummary: () => streamSummary ? { ...streamSummary, phases: [...streamSummary.phases] } : undefined,
734
- };
807
+ const parentOrigin = `http://localhost:${address.port}`;
808
+ void startSandboxProxy(parentOrigin).then((proxyPort) => {
809
+ if (completed || listenersClosed) {
810
+ closeListeners();
811
+ reject(new ServerError("UI session completed before sandbox proxy was ready"));
812
+ return;
813
+ }
814
+ sandboxProxyUrl = `http://localhost:${proxyPort}${SANDBOX_PROXY_PATH}`;
815
+ log.debug("Servers started", { port: address.port, proxyPort });
816
+ rememberMoshiDiscoveryPort(address.port);
817
+
818
+ const handle: UiServerHandle = {
819
+ url: `http://localhost:${address.port}/?session=${sessionToken}`,
820
+ port: address.port,
821
+ proxyUrl: sandboxProxyUrl,
822
+ proxyPort,
823
+ sessionToken,
824
+ serverName: options.serverName,
825
+ toolName: options.toolName,
826
+ close: (reason?: string) => {
827
+ markCompleted(reason ?? "closed");
828
+ closeListeners();
829
+ },
830
+ sendToolInput: (args: Record<string, unknown>) => {
831
+ pushEvent("tool-input", { arguments: args });
832
+ },
833
+ sendToolResult: (result: CallToolResult) => {
834
+ pushEvent("tool-result", result);
835
+ },
836
+ sendResultPatch: (result: CallToolResult) => {
837
+ pushEvent("result-patch", result);
838
+ },
839
+ sendToolCancelled: (reason: string) => {
840
+ pushEvent("tool-cancelled", { reason });
841
+ },
842
+ sendResourceUpdated: (uri: string) => {
843
+ pushEvent("resource-updated", { uri });
844
+ },
845
+ sendHostContext: (context: UiHostContext) => {
846
+ Object.assign(hostContext, context);
847
+ pushEvent("host-context", context);
848
+ },
849
+ getSessionMessages: () => ({ ...sessionMessages }),
850
+ getStreamSummary: () => streamSummary ? { ...streamSummary, phases: [...streamSummary.phases] } : undefined,
851
+ };
735
852
 
736
- resolve(handle);
853
+ resolve(handle);
854
+ }).catch((error) => {
855
+ log.error("Failed to start sandbox proxy", error instanceof Error ? error : undefined);
856
+ closeListeners();
857
+ const wrapped = error instanceof ServerError
858
+ ? error
859
+ : new ServerError(error instanceof Error ? error.message : String(error), { cause: error });
860
+ reject(wrapped);
861
+ });
737
862
  };
738
863
 
739
864
  listen();
@@ -21,6 +21,7 @@ import { isGlimpseAvailable, openGlimpseWindow } from "./glimpse-ui.ts";
21
21
  import type { SessionRecoveryDeps } from "./session-recovery.ts";
22
22
  import { combineAbortSignals, isAbortError } from "./runtime-owner.ts";
23
23
  import { throwIfAborted } from "./abort.ts";
24
+ import { InputRequiredNeedsUiError } from "./errors.ts";
24
25
 
25
26
  let activeGlimpseWindow: { close(): void } | null = null;
26
27
 
@@ -172,7 +173,7 @@ function probeMoshiGateway(): Promise<boolean> {
172
173
  });
173
174
  }
174
175
 
175
- function remoteAccessHint(opts: { url: string; port: number; moshi: boolean; openError: string | null; openedOnHost?: boolean }): string {
176
+ function remoteAccessHint(opts: { url: string; port: number; proxyPort: number; moshi: boolean; openError: string | null; openedOnHost?: boolean }): string {
176
177
  const lines = [
177
178
  opts.openError !== null
178
179
  ? "Couldn't open MCP UI here. Open it from your own device:"
@@ -185,10 +186,12 @@ function remoteAccessHint(opts: { url: string; port: number; moshi: boolean; ope
185
186
  lines.push(`Browser launch failed: ${opts.openError}`);
186
187
  }
187
188
  if (opts.moshi) {
188
- lines.push("Moshi: tap the preview button in the terminal title bar and pick this MCP UI server.");
189
+ lines.push(
190
+ `Moshi: tap the preview button in the terminal title bar and pick this MCP UI server (it must reach ports ${opts.port} and ${opts.proxyPort}).`,
191
+ );
189
192
  }
190
193
  lines.push(
191
- `SSH: run \`ssh -L ${opts.port}:127.0.0.1:${opts.port} <this-host>\` on your local machine, then open the URL above.`,
194
+ `SSH: run \`ssh -L ${opts.port}:127.0.0.1:${opts.port} -L ${opts.proxyPort}:127.0.0.1:${opts.proxyPort} <this-host>\` on your local machine, then open the URL above.`,
192
195
  "mosh can't forward ports - run that ssh command in a separate terminal.",
193
196
  );
194
197
  return lines.join("\n");
@@ -314,11 +317,13 @@ export async function maybeStartUiSession(
314
317
  let active = true;
315
318
  let nextStreamSequence = 0;
316
319
  let handle: UiServerHandle;
320
+ const resourceListenerToken = randomUUID();
317
321
 
318
- const cleanupStreamListener = () => {
322
+ const cleanupListeners = () => {
319
323
  if (streamToken) {
320
324
  state.manager.removeUiStreamListener(streamToken);
321
325
  }
326
+ state.manager.removeResourceUpdatedListener?.(resourceListenerToken);
322
327
  };
323
328
 
324
329
  handle = await startUiServer({
@@ -394,7 +399,7 @@ export async function maybeStartUiSession(
394
399
 
395
400
  onComplete: (reason: string) => {
396
401
  active = false;
397
- cleanupStreamListener();
402
+ cleanupListeners();
398
403
 
399
404
  if (state.uiServer === handle) {
400
405
  const messages = handle.getSessionMessages();
@@ -454,6 +459,16 @@ export async function maybeStartUiSession(
454
459
  });
455
460
  }
456
461
 
462
+ state.manager.registerResourceUpdatedListener?.(
463
+ resourceListenerToken,
464
+ request.serverName,
465
+ request.uiResourceUri,
466
+ (_serverName, uri) => {
467
+ if (!active || state.uiServer !== handle) return;
468
+ handle.sendResourceUpdated(uri);
469
+ },
470
+ );
471
+
457
472
  state.uiServer = handle;
458
473
 
459
474
  const viewerPref = process.env.MCP_UI_VIEWER?.toLowerCase();
@@ -466,7 +481,11 @@ export async function maybeStartUiSession(
466
481
  if (uiSuppressed) {
467
482
  viewer = "suppressed";
468
483
  windowOpen = false;
469
- state.ui?.notify(`MCP UI window suppressed (MCP_UI_VIEWER=${viewerPref}). Open manually: ${handle.url}`, "info");
484
+ state.ui?.notify(
485
+ `MCP UI window suppressed (MCP_UI_VIEWER=${viewerPref}). Open manually: ${handle.url}\n` +
486
+ `If this session is remote, run ssh -L ${handle.port}:127.0.0.1:${handle.port} -L ${handle.proxyPort}:127.0.0.1:${handle.proxyPort} <this-host> first.`,
487
+ "info",
488
+ );
470
489
  log.info("Suppressing MCP UI window (MCP_UI_VIEWER=" + viewerPref + ")", { url: handle.url });
471
490
  } else {
472
491
  const remoteLikely = remoteByEnv || await hasActiveRemoteLogin();
@@ -474,6 +493,7 @@ export async function maybeStartUiSession(
474
493
  state.ui?.notify(remoteAccessHint({
475
494
  url: handle.url,
476
495
  port: handle.port,
496
+ proxyPort: handle.proxyPort,
477
497
  moshi: await probeMoshiGateway(),
478
498
  openError,
479
499
  openedOnHost,
@@ -554,12 +574,12 @@ export async function maybeStartUiSession(
554
574
  },
555
575
  close: (reason?: string) => {
556
576
  active = false;
557
- cleanupStreamListener();
577
+ cleanupListeners();
558
578
  handle.close(reason);
559
579
  },
560
580
  };
561
581
  } catch (error) {
562
- if (error instanceof UrlElicitationRequiredError || isAbortError(error, runtimeSignal)) throw error;
582
+ if (error instanceof UrlElicitationRequiredError || error instanceof InputRequiredNeedsUiError || isAbortError(error, runtimeSignal)) throw error;
563
583
  const message = error instanceof Error ? error.message : String(error);
564
584
  log.error("Failed to start UI session", error instanceof Error ? error : undefined);
565
585
  state.ui?.notify(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herbertgao/pi-extensions",
3
- "version": "2026.8.15",
3
+ "version": "2026.9.0",
4
4
  "description": "Aggregate installer for HerbertGao-maintained Pi extensions.",
5
5
  "keywords": [
6
6
  "extensions",
@@ -44,11 +44,11 @@
44
44
  "@effect/platform-node": "4.0.0-beta.103",
45
45
  "@effect/platform-node-shared": "4.0.0-beta.103",
46
46
  "@herbertgao/pi-bark": "0.1.0",
47
- "@herbertgao/pi-cc-extensions": "0.8.60",
47
+ "@herbertgao/pi-cc-extensions": "0.9.0",
48
48
  "@herbertgao/pi-subagents": "0.17.1",
49
49
  "@herbertgao/resume-from": "0.2.0",
50
- "@juicesharp/rpiv-ask-user-question": "2.8.0",
51
- "@juicesharp/rpiv-config": "^2.8.0",
50
+ "@juicesharp/rpiv-ask-user-question": "2.9.0",
51
+ "@juicesharp/rpiv-config": "^2.9.0",
52
52
  "@luxusai/pi-hindsight": "0.12.0",
53
53
  "@modelcontextprotocol/client": "2.0.0",
54
54
  "@modelcontextprotocol/core": "2.0.0",
@@ -56,14 +56,15 @@
56
56
  "@modelcontextprotocol/sdk": "^1.29.0",
57
57
  "@mozilla/readability": "^0.6.0",
58
58
  "@napi-rs/keyring": "^1.3.0",
59
- "@narumitw/pi-btw": "0.56.0",
59
+ "@narumitw/pi-btw": "0.57.0",
60
+ "@narumitw/pi-caffeinate": "0.49.7",
60
61
  "@narumitw/pi-tui-kit": "^0.59.0",
61
62
  "@noble/ed25519": "^3.1.0",
62
63
  "@pi-plugins/fast-mode": "0.1.10",
63
64
  "@shikijs/cli": "^4.0.2",
64
65
  "@sinclair/typebox": "^0.34.49",
65
66
  "@tifan/pi-copy-response": "0.2.6",
66
- "@tifan/pi-handoff": "2.2.0",
67
+ "@tifan/pi-handoff": "2.2.1",
67
68
  "@tifan/pi-inline-skills": "1.0.5",
68
69
  "@tifan/pi-mermaid-open": "0.2.0",
69
70
  "@tifan/pi-preferred-thinking": "1.0.1",
@@ -75,6 +76,7 @@
75
76
  "chalk": "^6.0.0",
76
77
  "croner": "^10.0.1",
77
78
  "cross-spawn": "^7.0.6",
79
+ "dbus-native": "^0.15.2",
78
80
  "defuddle": "0.19.3",
79
81
  "effect": "4.0.0-beta.103",
80
82
  "gpt-tokenizer": "^3.4.0",
@@ -89,7 +91,7 @@
89
91
  "p-limit": "^6.1.0",
90
92
  "pi-footer": "0.5.1",
91
93
  "pi-lens": "4.1.3",
92
- "pi-mcp-adapter": "2.31.0",
94
+ "pi-mcp-adapter": "2.32.1",
93
95
  "pi-web-access": "0.27.0",
94
96
  "pidusage": "^4.0.1",
95
97
  "promise.try": "^2.0.1",
@@ -120,6 +122,7 @@
120
122
  "@juicesharp/rpiv-ask-user-question",
121
123
  "@luxusai/pi-hindsight",
122
124
  "@narumitw/pi-btw",
125
+ "@narumitw/pi-caffeinate",
123
126
  "@pi-plugins/fast-mode",
124
127
  "@tifan/pi-copy-response",
125
128
  "@tifan/pi-handoff",
@@ -155,6 +158,7 @@
155
158
  "./node_modules/@herbertgao/pi-bark/src/index.ts",
156
159
  "./node_modules/@luxusai/pi-hindsight/extensions/index.ts",
157
160
  "./node_modules/@narumitw/pi-btw/dist/index.ts",
161
+ "./node_modules/@narumitw/pi-caffeinate/dist/index.ts",
158
162
  "./node_modules/@pi-plugins/fast-mode/dist/index.mjs",
159
163
  "./node_modules/pi-mcp-adapter/index.ts",
160
164
  "./node_modules/pi-lens/dist/index.js",