@herbertgao/pi-extensions 2026.8.1 → 2026.8.2
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 +5 -2
- package/node_modules/@czottmann/pi-automode/README.md +16 -2
- package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +35 -0
- package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +18 -9
- package/node_modules/@czottmann/pi-automode/docs/defaults.md +6 -2
- package/node_modules/@czottmann/pi-automode/examples/automode.local.json +6 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +11 -7
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +100 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +32 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +78 -3
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +16 -4
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/paths.ts +23 -1
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/permissions.ts +22 -1
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/types.ts +18 -2
- package/node_modules/@czottmann/pi-automode/package.json +4 -4
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-audit.md +5 -0
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-debt.md +5 -0
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-gain.md +5 -0
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-help.md +5 -0
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-review.md +5 -0
- package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail.md +5 -0
- package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail-frontmatter.cjs +23 -0
- package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail.mjs +99 -0
- package/node_modules/@dietrichgebert/ponytail/.qoder/rules/ponytail.md +30 -0
- package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +16 -0
- package/node_modules/@dietrichgebert/ponytail/AGENTS.md +32 -0
- package/node_modules/@dietrichgebert/ponytail/LICENSE +21 -0
- package/node_modules/@dietrichgebert/ponytail/README.es.md +298 -0
- package/node_modules/@dietrichgebert/ponytail/README.ko.md +315 -0
- package/node_modules/@dietrichgebert/ponytail/README.md +351 -0
- package/node_modules/@dietrichgebert/ponytail/assets/benchmark-3model.svg +21 -0
- package/node_modules/@dietrichgebert/ponytail/assets/benchmark-agentic.svg +62 -0
- package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.png +0 -0
- package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.svg +115 -0
- package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt-dark.svg +27 -0
- package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt.svg +27 -0
- package/node_modules/@dietrichgebert/ponytail/assets/logo.png +0 -0
- package/node_modules/@dietrichgebert/ponytail/assets/social-preview.png +0 -0
- package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-es.png +0 -0
- package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-ko.png +0 -0
- package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner.png +0 -0
- package/node_modules/@dietrichgebert/ponytail/hooks/claude-codex-hooks.json +44 -0
- package/node_modules/@dietrichgebert/ponytail/hooks/copilot-hooks.json +21 -0
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +96 -0
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-config.js +169 -0
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-instructions.js +98 -0
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-mode-tracker.js +130 -0
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-runtime.js +85 -0
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-statusline.ps1 +24 -0
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-statusline.sh +18 -0
- package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-subagent.js +77 -0
- package/node_modules/@dietrichgebert/ponytail/hooks/qoder-hooks.json +26 -0
- package/node_modules/@dietrichgebert/ponytail/package.json +47 -0
- package/node_modules/@dietrichgebert/ponytail/pi-extension/index.js +211 -0
- package/node_modules/@dietrichgebert/ponytail/pi-extension/package.json +8 -0
- package/node_modules/@dietrichgebert/ponytail/scripts/uninstall.js +60 -0
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail/SKILL.md +120 -0
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +41 -0
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +44 -0
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +50 -0
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +71 -0
- package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +57 -0
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -3
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +10 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary.ts +6 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +24 -83
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/markdown-enhance.ts +119 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +73 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-hover.ts +130 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-interaction.ts +65 -542
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-layout.ts +160 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-packets.ts +143 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-scroll.ts +184 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool-diff/diff-renderer.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool-grouping.ts +9 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +91 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/agent-summary.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-cc-extensions/themes/cc-dark.json +1 -0
- package/node_modules/@herbertgao/pi-cc-extensions/themes/cc-light.json +2 -2
- package/node_modules/@herbertgao/pi-stash/README.md +9 -9
- package/node_modules/@herbertgao/pi-stash/package.json +1 -1
- package/node_modules/@herbertgao/pi-stash/src/index.ts +2 -1
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +14 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +6 -2
- package/node_modules/@herbertgao/pi-subagents/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +85 -11
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +28 -4
- package/node_modules/@herbertgao/pi-subagents/src/settings.ts +12 -0
- package/node_modules/@herbertgao/pi-subagents/src/types.ts +2 -0
- package/node_modules/@narumitw/pi-btw/LICENSE +21 -0
- package/node_modules/@narumitw/pi-btw/README.md +210 -0
- package/node_modules/@narumitw/pi-btw/package.json +53 -0
- package/node_modules/@narumitw/pi-btw/src/bring-to-main.ts +632 -0
- package/node_modules/@narumitw/pi-btw/src/btw.ts +996 -0
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +339 -0
- package/node_modules/@narumitw/pi-btw/src/index.ts +1 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +296 -0
- package/node_modules/@narumitw/pi-btw/src/settings.ts +237 -0
- package/node_modules/@narumitw/pi-btw/src/side-thread.ts +238 -0
- package/node_modules/@narumitw/pi-btw/src/text.ts +10 -0
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +620 -0
- package/node_modules/@pi-plugins/fast-mode/LICENSE +21 -0
- package/node_modules/@pi-plugins/fast-mode/README.md +92 -0
- package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts +6 -0
- package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -0
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +2026 -0
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -0
- package/node_modules/@pi-plugins/fast-mode/package.json +73 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +59 -0
- package/node_modules/pi-mcp-adapter/README.md +62 -13
- package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +377 -0
- package/node_modules/pi-mcp-adapter/commands.ts +9 -7
- package/node_modules/pi-mcp-adapter/config.ts +110 -49
- package/node_modules/pi-mcp-adapter/direct-tools.ts +34 -19
- package/node_modules/pi-mcp-adapter/elicitation-handler.ts +37 -36
- package/node_modules/pi-mcp-adapter/errors.ts +21 -17
- package/node_modules/pi-mcp-adapter/host-html-template.ts +13 -7
- package/node_modules/pi-mcp-adapter/index.ts +37 -14
- package/node_modules/pi-mcp-adapter/init.ts +9 -7
- package/node_modules/pi-mcp-adapter/json-schema-validator.ts +2 -2
- package/node_modules/pi-mcp-adapter/lifecycle.ts +8 -8
- package/node_modules/pi-mcp-adapter/logger.ts +1 -1
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +36 -22
- package/node_modules/pi-mcp-adapter/mcp-auth.ts +120 -2
- package/node_modules/pi-mcp-adapter/mcp-code.ts +13 -11
- package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +19 -13
- package/node_modules/pi-mcp-adapter/mcp-output-guard.ts +15 -11
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +34 -15
- package/node_modules/pi-mcp-adapter/mcp-probe.ts +131 -34
- package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +2 -2
- package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +7 -4
- package/node_modules/pi-mcp-adapter/mcp-trace.ts +59 -64
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +34 -26
- package/node_modules/pi-mcp-adapter/npx-resolver.ts +6 -4
- package/node_modules/pi-mcp-adapter/oauth-handler.ts +1 -1
- package/node_modules/pi-mcp-adapter/onboarding-state.ts +19 -11
- package/node_modules/pi-mcp-adapter/package.json +13 -8
- package/node_modules/pi-mcp-adapter/panel-keys.ts +20 -4
- package/node_modules/pi-mcp-adapter/prompts.ts +2 -2
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +48 -11
- package/node_modules/pi-mcp-adapter/sampling-handler.ts +22 -15
- package/node_modules/pi-mcp-adapter/search-ranking.ts +2 -1
- package/node_modules/pi-mcp-adapter/server-manager.ts +165 -113
- package/node_modules/pi-mcp-adapter/session-recovery.ts +11 -12
- package/node_modules/pi-mcp-adapter/skills/mcp-scripting/SKILL.md +3 -3
- package/node_modules/pi-mcp-adapter/state.ts +3 -1
- package/node_modules/pi-mcp-adapter/tool-approval.ts +67 -4
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +6 -5
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +14 -2
- package/node_modules/pi-mcp-adapter/ts-shape.ts +9 -2
- package/node_modules/pi-mcp-adapter/types.ts +109 -22
- package/node_modules/pi-mcp-adapter/ui-app-bridge-helpers.ts +40 -0
- package/node_modules/pi-mcp-adapter/ui-resource-handler.ts +28 -19
- package/node_modules/pi-mcp-adapter/ui-server.ts +90 -58
- package/node_modules/pi-mcp-adapter/ui-session.ts +16 -14
- package/node_modules/pi-mcp-adapter/unix-socket-transport.ts +4 -4
- package/node_modules/pi-mcp-adapter/utils.ts +23 -9
- package/package.json +22 -7
|
@@ -2,12 +2,12 @@ import http, { type IncomingMessage, type ServerResponse } from "node:http";
|
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
|
-
import { buildAllowAttribute } from "
|
|
5
|
+
import { buildAllowAttribute } from "./ui-app-bridge-helpers.ts";
|
|
6
6
|
import {
|
|
7
|
-
ContentBlockSchema,
|
|
8
7
|
type CallToolRequest,
|
|
9
8
|
type CallToolResult,
|
|
10
|
-
} from "@modelcontextprotocol/
|
|
9
|
+
} from "@modelcontextprotocol/client";
|
|
10
|
+
import { ContentBlockSchema } from "@modelcontextprotocol/core";
|
|
11
11
|
import type { ConsentManager } from "./consent-manager.ts";
|
|
12
12
|
import { ServerError, wrapError } from "./errors.ts";
|
|
13
13
|
import { formatAuthRequiredMessage } from "./utils.ts";
|
|
@@ -17,7 +17,7 @@ import type { McpServerManager } from "./server-manager.ts";
|
|
|
17
17
|
import type { McpExtensionState } from "./state.ts";
|
|
18
18
|
import { SessionRecoveryAuthRequiredError, withSessionRecovery, type SessionRecoveryDeps } from "./session-recovery.ts";
|
|
19
19
|
import { ensureToolCallApproved, isToolCallApprovalRequired } from "./tool-approval.ts";
|
|
20
|
-
import { extractUiToolVisibility, isUiToolCallableByApp } from "./ui-tool-visibility.ts";
|
|
20
|
+
import { extractUiToolVisibility, isUiToolCallableByApp, isUiToolVisibleToModel } from "./ui-tool-visibility.ts";
|
|
21
21
|
import {
|
|
22
22
|
createUiModelContextUpdate,
|
|
23
23
|
extractUiPromptText,
|
|
@@ -95,6 +95,7 @@ export interface UiServerHandle {
|
|
|
95
95
|
|
|
96
96
|
export async function startUiServer(options: UiServerOptions): Promise<UiServerHandle> {
|
|
97
97
|
const sessionToken = options.sessionToken ?? randomUUID();
|
|
98
|
+
const uiResourceToken = randomUUID();
|
|
98
99
|
const log = logger.child({
|
|
99
100
|
component: "UiServer",
|
|
100
101
|
server: options.serverName,
|
|
@@ -144,6 +145,41 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
144
145
|
}
|
|
145
146
|
}
|
|
146
147
|
|
|
148
|
+
const isAppOnlyTool = (toolName: string): boolean => {
|
|
149
|
+
const toolDefinition = options.manager.getConnection(options.serverName)?.tools?.find((tool) => tool.name === toolName);
|
|
150
|
+
if (!toolDefinition) return false;
|
|
151
|
+
const visibility = extractUiToolVisibility(toolDefinition._meta);
|
|
152
|
+
return isUiToolCallableByApp(visibility) && !isUiToolVisibleToModel(visibility);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const recordUiMessage = async (msgParams: UiMessageParams): Promise<void> => {
|
|
156
|
+
const promptText = extractUiPromptText(msgParams);
|
|
157
|
+
|
|
158
|
+
// Track messages by type (order: prompt → intent → notify)
|
|
159
|
+
// Must match the order in index.ts onMessage handler
|
|
160
|
+
if (promptText) {
|
|
161
|
+
sessionMessages.prompts.push(promptText);
|
|
162
|
+
log.debug("UI prompt received", { prompt: promptText.slice(0, 100) });
|
|
163
|
+
} else if (msgParams.type === "intent" || msgParams.intent) {
|
|
164
|
+
const intentName = msgParams.intent ?? "";
|
|
165
|
+
if (intentName) {
|
|
166
|
+
sessionMessages.intents.push({
|
|
167
|
+
intent: intentName,
|
|
168
|
+
...(msgParams.params !== undefined ? { params: msgParams.params } : {}),
|
|
169
|
+
});
|
|
170
|
+
log.debug("UI intent received", { intent: intentName });
|
|
171
|
+
}
|
|
172
|
+
} else if (msgParams.type === "notify" || msgParams.message) {
|
|
173
|
+
const notifyText = msgParams.message ?? "";
|
|
174
|
+
if (notifyText) {
|
|
175
|
+
sessionMessages.notifications.push(notifyText);
|
|
176
|
+
log.debug("UI notification", { message: notifyText.slice(0, 100) });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
await options.onMessage?.(msgParams);
|
|
181
|
+
};
|
|
182
|
+
|
|
147
183
|
const touchHeartbeat = () => {
|
|
148
184
|
lastHeartbeatAt = Date.now();
|
|
149
185
|
};
|
|
@@ -164,7 +200,8 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
164
200
|
streamSummary.phases.push(envelope.phase);
|
|
165
201
|
}
|
|
166
202
|
streamSummary.finalStatus = envelope.status;
|
|
167
|
-
streamSummary.lastMessage = envelope.message;
|
|
203
|
+
if (envelope.message !== undefined) streamSummary.lastMessage = envelope.message;
|
|
204
|
+
else delete streamSummary.lastMessage;
|
|
168
205
|
};
|
|
169
206
|
|
|
170
207
|
const serializeEvent = (eventId: number, name: string, payload: unknown): string => {
|
|
@@ -174,6 +211,7 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
174
211
|
const getLatestCheckpointIndex = () => {
|
|
175
212
|
for (let index = eventLog.length - 1; index >= 0; index -= 1) {
|
|
176
213
|
const entry = eventLog[index];
|
|
214
|
+
if (!entry) continue;
|
|
177
215
|
const envelope = getVisualizationStreamEnvelope((entry.payload as { structuredContent?: unknown } | null)?.structuredContent);
|
|
178
216
|
if (envelope?.frameType === "checkpoint" || envelope?.frameType === "final") {
|
|
179
217
|
return index;
|
|
@@ -271,13 +309,11 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
271
309
|
}
|
|
272
310
|
|
|
273
311
|
if (method === "GET" && url.pathname === "/") {
|
|
274
|
-
if (!url.searchParams.has("session")
|
|
275
|
-
const dest = `/?session=${encodeURIComponent(sessionToken)}`;
|
|
312
|
+
if (!url.searchParams.has("session")) {
|
|
276
313
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store" });
|
|
277
314
|
res.end(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
`<body><script>location.replace(${JSON.stringify(dest)});</script></body></html>`,
|
|
315
|
+
"<!doctype html><html><head><meta charset=\"utf-8\"><title>MCP UI</title></head>" +
|
|
316
|
+
"<body><p>Open the authenticated MCP UI URL shown by Pi.</p></body></html>",
|
|
281
317
|
);
|
|
282
318
|
return;
|
|
283
319
|
}
|
|
@@ -286,6 +322,7 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
286
322
|
|
|
287
323
|
const html = buildHostHtmlTemplate({
|
|
288
324
|
sessionToken,
|
|
325
|
+
uiResourceToken,
|
|
289
326
|
serverName: options.serverName,
|
|
290
327
|
toolName: options.toolName,
|
|
291
328
|
toolArgs: options.toolArgs,
|
|
@@ -330,7 +367,7 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
330
367
|
}
|
|
331
368
|
|
|
332
369
|
if (method === "GET" && url.pathname === "/ui-app") {
|
|
333
|
-
if (!validateTokenQuery(url,
|
|
370
|
+
if (!validateTokenQuery(url, uiResourceToken, res, "resource")) return;
|
|
334
371
|
touchHeartbeat();
|
|
335
372
|
// Enforce host metadata independently of where app HTML places its document head.
|
|
336
373
|
const cspContent = buildCspMetaContent(options.resource.meta.csp);
|
|
@@ -411,8 +448,8 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
411
448
|
name: callParams.name,
|
|
412
449
|
originalName: callParams.name,
|
|
413
450
|
description: toolDefinition?.description ?? "",
|
|
414
|
-
inputSchema: toolDefinition
|
|
415
|
-
uiVisibility,
|
|
451
|
+
...(toolDefinition?.inputSchema !== undefined ? { inputSchema: toolDefinition.inputSchema } : {}),
|
|
452
|
+
...(uiVisibility !== undefined ? { uiVisibility } : {}),
|
|
416
453
|
};
|
|
417
454
|
const approval = options.state
|
|
418
455
|
? await ensureToolCallApproved(
|
|
@@ -421,6 +458,7 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
421
458
|
toolMeta,
|
|
422
459
|
callArgs.arguments,
|
|
423
460
|
options.state.owner?.signal,
|
|
461
|
+
"iframe",
|
|
424
462
|
)
|
|
425
463
|
: options.config && isToolCallApprovalRequired(options.config, options.serverName, toolMeta)
|
|
426
464
|
? { ok: false as const, reason: "approval_required_headless" as const }
|
|
@@ -449,11 +487,15 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
449
487
|
options.manager.incrementInFlight(options.serverName);
|
|
450
488
|
const result = options.config
|
|
451
489
|
? await withSessionRecovery(
|
|
452
|
-
{
|
|
490
|
+
{
|
|
491
|
+
manager: options.manager,
|
|
492
|
+
config: options.config,
|
|
493
|
+
...(options.onNeedsAuth ? { onNeedsAuth: options.onNeedsAuth } : {}),
|
|
494
|
+
},
|
|
453
495
|
options.serverName,
|
|
454
|
-
(conn) => conn.client.callTool(callArgs,
|
|
496
|
+
(conn) => conn.client.callTool(callArgs, options.manager.getRequestOptions?.(options.serverName)),
|
|
455
497
|
)
|
|
456
|
-
: await connection.client.callTool(callArgs,
|
|
498
|
+
: await connection.client.callTool(callArgs, options.manager.getRequestOptions?.(options.serverName));
|
|
457
499
|
sendJson(res, 200, { ok: true, result });
|
|
458
500
|
} finally {
|
|
459
501
|
options.manager.decrementInFlight(options.serverName);
|
|
@@ -469,33 +511,27 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
469
511
|
return;
|
|
470
512
|
}
|
|
471
513
|
|
|
472
|
-
if (url.pathname === "/proxy/ui/
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
params: msgParams.params
|
|
487
|
-
});
|
|
488
|
-
log.debug("UI intent received", { intent: intentName });
|
|
489
|
-
}
|
|
490
|
-
} else if (msgParams.type === "notify" || msgParams.message) {
|
|
491
|
-
const notifyText = msgParams.message ?? "";
|
|
492
|
-
if (notifyText) {
|
|
493
|
-
sessionMessages.notifications.push(notifyText);
|
|
494
|
-
log.debug("UI notification", { message: notifyText.slice(0, 100) });
|
|
495
|
-
}
|
|
514
|
+
if (url.pathname === "/proxy/ui/generated-tool-call-intent") {
|
|
515
|
+
const tool = typeof params.tool === "string" ? params.tool : undefined;
|
|
516
|
+
if (tool && isAppOnlyTool(tool)) {
|
|
517
|
+
log.debug("Ignored generated app-only tool call intent", { tool });
|
|
518
|
+
} else {
|
|
519
|
+
await recordUiMessage({
|
|
520
|
+
type: "intent",
|
|
521
|
+
intent: "call_tool",
|
|
522
|
+
params: {
|
|
523
|
+
...(tool !== undefined ? { tool } : {}),
|
|
524
|
+
...(params.arguments !== undefined ? { arguments: params.arguments } : {}),
|
|
525
|
+
...(params.isError !== undefined ? { isError: params.isError } : {}),
|
|
526
|
+
},
|
|
527
|
+
});
|
|
496
528
|
}
|
|
497
|
-
|
|
498
|
-
|
|
529
|
+
sendJson(res, 200, { ok: true, result: {} });
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (url.pathname === "/proxy/ui/message") {
|
|
534
|
+
await recordUiMessage(params as UiMessageParams);
|
|
499
535
|
sendJson(res, 200, { ok: true, result: {} });
|
|
500
536
|
return;
|
|
501
537
|
}
|
|
@@ -639,7 +675,11 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
639
675
|
return;
|
|
640
676
|
}
|
|
641
677
|
log.error("Failed to start server", error);
|
|
642
|
-
|
|
678
|
+
const port = candidates[candidateIndex];
|
|
679
|
+
reject(new ServerError(error.message, {
|
|
680
|
+
...(port !== undefined ? { port } : {}),
|
|
681
|
+
cause: error,
|
|
682
|
+
}));
|
|
643
683
|
};
|
|
644
684
|
|
|
645
685
|
const onListening = () => {
|
|
@@ -760,21 +800,13 @@ function isAllowedHost(hostname: string): boolean {
|
|
|
760
800
|
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
761
801
|
}
|
|
762
802
|
|
|
763
|
-
function
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
.replace(/</g, "<")
|
|
771
|
-
.replace(/>/g, ">")
|
|
772
|
-
.replace(/"/g, """)
|
|
773
|
-
.replace(/'/g, "'");
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
function validateTokenQuery(url: URL, expected: string, res: ServerResponse): boolean {
|
|
777
|
-
const token = url.searchParams.get("session");
|
|
803
|
+
function validateTokenQuery(
|
|
804
|
+
url: URL,
|
|
805
|
+
expected: string,
|
|
806
|
+
res: ServerResponse,
|
|
807
|
+
parameter = "session",
|
|
808
|
+
): boolean {
|
|
809
|
+
const token = url.searchParams.get(parameter);
|
|
778
810
|
if (token !== expected) {
|
|
779
811
|
sendJson(res, 403, { ok: false, error: "Invalid session" });
|
|
780
812
|
return false;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import net from "node:net";
|
|
4
|
-
import { UrlElicitationRequiredError, type CallToolResult } from "@modelcontextprotocol/
|
|
4
|
+
import { UrlElicitationRequiredError, type CallToolResult } from "@modelcontextprotocol/client";
|
|
5
5
|
import type { McpExtensionState } from "./state.ts";
|
|
6
6
|
import {
|
|
7
7
|
createUiModelContextUpdate,
|
|
@@ -99,8 +99,10 @@ function withStreamEnvelope(
|
|
|
99
99
|
return result;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
const structuredContent
|
|
103
|
-
|
|
102
|
+
const structuredContent: Record<string, unknown> = result.structuredContent
|
|
103
|
+
&& typeof result.structuredContent === "object"
|
|
104
|
+
&& !Array.isArray(result.structuredContent)
|
|
105
|
+
? { ...(result.structuredContent as Record<string, unknown>) }
|
|
104
106
|
: {};
|
|
105
107
|
|
|
106
108
|
const rawEnvelope = structuredContent[UI_STREAM_STRUCTURED_CONTENT_KEY];
|
|
@@ -239,10 +241,10 @@ export async function maybeStartUiSession(
|
|
|
239
241
|
serverName: request.serverName,
|
|
240
242
|
toolName: request.toolName,
|
|
241
243
|
reused: true,
|
|
242
|
-
streamId,
|
|
243
|
-
streamToken,
|
|
244
|
-
streamMode,
|
|
245
|
-
|
|
244
|
+
...(streamId !== undefined ? { streamId } : {}),
|
|
245
|
+
...(streamToken !== undefined ? { streamToken } : {}),
|
|
246
|
+
...(streamMode !== undefined ? { streamMode } : {}),
|
|
247
|
+
...(streamToken ? { requestMeta: { [UI_STREAM_REQUEST_META_KEY]: streamToken } } : {}),
|
|
246
248
|
url: existingHandle.url,
|
|
247
249
|
viewer: existingHandle.viewer ?? "browser",
|
|
248
250
|
windowOpen: existingHandle.windowOpen ?? true,
|
|
@@ -326,9 +328,9 @@ export async function maybeStartUiSession(
|
|
|
326
328
|
manager: state.manager,
|
|
327
329
|
config: state.config,
|
|
328
330
|
state,
|
|
329
|
-
onNeedsAuth: request.onNeedsAuth,
|
|
331
|
+
...(request.onNeedsAuth ? { onNeedsAuth: request.onNeedsAuth } : {}),
|
|
330
332
|
consentManager: state.consentManager,
|
|
331
|
-
hostContext,
|
|
333
|
+
...(hostContext !== undefined ? { hostContext } : {}),
|
|
332
334
|
|
|
333
335
|
onMessage: (params: UiMessageParams) => {
|
|
334
336
|
const prompt = extractUiPromptText(params);
|
|
@@ -410,7 +412,7 @@ export async function maybeStartUiSession(
|
|
|
410
412
|
completedAt: new Date(),
|
|
411
413
|
reason,
|
|
412
414
|
messages,
|
|
413
|
-
stream,
|
|
415
|
+
...(stream !== undefined ? { stream } : {}),
|
|
414
416
|
});
|
|
415
417
|
|
|
416
418
|
while (state.completedUiSessions.length > MAX_COMPLETED_SESSIONS) {
|
|
@@ -527,10 +529,10 @@ export async function maybeStartUiSession(
|
|
|
527
529
|
serverName: request.serverName,
|
|
528
530
|
toolName: request.toolName,
|
|
529
531
|
reused: false,
|
|
530
|
-
streamId,
|
|
531
|
-
streamToken,
|
|
532
|
-
streamMode,
|
|
533
|
-
|
|
532
|
+
...(streamId !== undefined ? { streamId } : {}),
|
|
533
|
+
...(streamToken !== undefined ? { streamToken } : {}),
|
|
534
|
+
...(streamMode !== undefined ? { streamMode } : {}),
|
|
535
|
+
...(streamToken ? { requestMeta: { [UI_STREAM_REQUEST_META_KEY]: streamToken } } : {}),
|
|
534
536
|
url: handle.url,
|
|
535
537
|
viewer,
|
|
536
538
|
windowOpen,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createConnection, type Socket } from "node:net";
|
|
2
|
-
import { ReadBuffer, serializeMessage } from "@modelcontextprotocol/
|
|
3
|
-
import type { Transport } from "@modelcontextprotocol/
|
|
4
|
-
import type { JSONRPCMessage } from "@modelcontextprotocol/
|
|
2
|
+
import { ReadBuffer, serializeMessage } from "@modelcontextprotocol/client";
|
|
3
|
+
import type { Transport } from "@modelcontextprotocol/client";
|
|
4
|
+
import type { JSONRPCMessage } from "@modelcontextprotocol/client";
|
|
5
5
|
|
|
6
6
|
/** MCP JSONL transport for an explicitly configured Unix-domain socket. */
|
|
7
7
|
export class UnixSocketClientTransport implements Transport {
|
|
8
|
-
private socket
|
|
8
|
+
private socket: Socket | undefined;
|
|
9
9
|
private readonly readBuffer = new ReadBuffer();
|
|
10
10
|
|
|
11
11
|
onclose?: () => void;
|
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
3
|
import { homedir, platform } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
4
|
+
import { extname, isAbsolute, join } from "node:path";
|
|
5
5
|
import type { McpConfig, ServerEntry } from "./types.ts";
|
|
6
6
|
|
|
7
7
|
async function execOpen(pi: ExtensionAPI, target: string, browser?: string, signal?: AbortSignal) {
|
|
8
8
|
const os = platform();
|
|
9
9
|
|
|
10
10
|
if (os === "darwin") {
|
|
11
|
-
|
|
11
|
+
if (browser) {
|
|
12
|
+
// An absolute executable path (e.g. from $BROWSER) names a launcher
|
|
13
|
+
// binary. App bundles still go through `open -a`, which handles them.
|
|
14
|
+
return isAbsolute(browser) && extname(browser).toLowerCase() !== ".app"
|
|
15
|
+
? pi.exec(browser, [target], signal ? { signal } : {})
|
|
16
|
+
: pi.exec("open", ["-a", browser, target], signal ? { signal } : {});
|
|
17
|
+
}
|
|
18
|
+
return pi.exec("open", [target], signal ? { signal } : {});
|
|
12
19
|
}
|
|
13
20
|
if (os === "win32") {
|
|
14
21
|
return browser
|
|
15
|
-
? pi.exec("cmd", ["/c", "start", "", browser, target], { signal })
|
|
16
|
-
: pi.exec("cmd", ["/c", "start", "", target], { signal });
|
|
22
|
+
? pi.exec("cmd", ["/c", "start", "", browser, target], signal ? { signal } : {})
|
|
23
|
+
: pi.exec("cmd", ["/c", "start", "", target], signal ? { signal } : {});
|
|
17
24
|
}
|
|
18
|
-
return browser
|
|
25
|
+
return browser
|
|
26
|
+
? pi.exec(browser, [target], signal ? { signal } : {})
|
|
27
|
+
: pi.exec("xdg-open", [target], signal ? { signal } : {});
|
|
19
28
|
}
|
|
20
29
|
|
|
21
30
|
export async function openUrl(pi: ExtensionAPI, url: string, browser?: string, signal?: AbortSignal): Promise<void> {
|
|
@@ -38,12 +47,14 @@ export async function parallelLimit<T, R>(
|
|
|
38
47
|
fn: (item: T) => Promise<R>
|
|
39
48
|
): Promise<R[]> {
|
|
40
49
|
const results: R[] = [];
|
|
41
|
-
|
|
50
|
+
const iterator = items.entries();
|
|
42
51
|
|
|
43
52
|
async function worker() {
|
|
44
|
-
while (
|
|
45
|
-
const
|
|
46
|
-
|
|
53
|
+
while (true) {
|
|
54
|
+
const next = iterator.next();
|
|
55
|
+
if (next.done) return;
|
|
56
|
+
const [index, item] = next.value;
|
|
57
|
+
results[index] = await fn(item);
|
|
47
58
|
}
|
|
48
59
|
}
|
|
49
60
|
|
|
@@ -106,6 +117,9 @@ const COMMAND_SECRET_TIMEOUT_MS = 10_000;
|
|
|
106
117
|
const COMMAND_SECRET_MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
107
118
|
|
|
108
119
|
/** Resolve a secret value, executing only a single leading `!` command marker. */
|
|
120
|
+
export function resolveCommandSecret(value: string, context: string): string;
|
|
121
|
+
export function resolveCommandSecret(value: undefined, context: string): undefined;
|
|
122
|
+
export function resolveCommandSecret(value: string | undefined, context: string): string | undefined;
|
|
109
123
|
export function resolveCommandSecret(value: string | undefined, context: string): string | undefined {
|
|
110
124
|
if (value === undefined) return undefined;
|
|
111
125
|
if (value.startsWith("!!")) return interpolateEnvVars(value.slice(1));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herbertgao/pi-extensions",
|
|
3
|
-
"version": "2026.8.
|
|
3
|
+
"version": "2026.8.2",
|
|
4
4
|
"description": "Aggregate installer for HerbertGao-maintained Pi extensions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"extensions",
|
|
@@ -35,9 +35,12 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@ast-grep/cli": "^0.45.0",
|
|
37
37
|
"@ast-grep/napi": "^0.45.0",
|
|
38
|
-
"@czottmann/pi-automode": "1.
|
|
38
|
+
"@czottmann/pi-automode": "1.11.0",
|
|
39
|
+
"@dietrichgebert/ponytail": "4.9.0",
|
|
39
40
|
"@earendil-works/pi-tui": "^0.82.1",
|
|
40
|
-
"@
|
|
41
|
+
"@effect/platform-node": "4.0.0-beta.103",
|
|
42
|
+
"@effect/platform-node-shared": "4.0.0-beta.103",
|
|
43
|
+
"@herbertgao/pi-cc-extensions": "0.8.52",
|
|
41
44
|
"@herbertgao/pi-copy-response": "0.2.6",
|
|
42
45
|
"@herbertgao/pi-handoff": "1.1.2",
|
|
43
46
|
"@herbertgao/pi-inline-skills": "1.0.5",
|
|
@@ -45,21 +48,26 @@
|
|
|
45
48
|
"@herbertgao/pi-preferred-thinking": "0.3.0",
|
|
46
49
|
"@herbertgao/pi-recap": "0.4.4",
|
|
47
50
|
"@herbertgao/pi-rename": "0.4.2",
|
|
48
|
-
"@herbertgao/pi-stash": "0.1.
|
|
49
|
-
"@herbertgao/pi-subagents": "0.15.
|
|
51
|
+
"@herbertgao/pi-stash": "0.1.1",
|
|
52
|
+
"@herbertgao/pi-subagents": "0.15.2",
|
|
50
53
|
"@herbertgao/pi-titlebar-spinner": "0.1.3",
|
|
51
54
|
"@juicesharp/rpiv-ask-user-question": "2.4.0",
|
|
52
55
|
"@juicesharp/rpiv-config": "^2.4.0",
|
|
56
|
+
"@modelcontextprotocol/client": "2.0.0",
|
|
57
|
+
"@modelcontextprotocol/core": "2.0.0",
|
|
53
58
|
"@modelcontextprotocol/ext-apps": "^1.2.2",
|
|
54
|
-
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
55
59
|
"@mozilla/readability": "^0.6.0",
|
|
56
60
|
"@napi-rs/keyring": "^1.3.0",
|
|
61
|
+
"@narumitw/pi-btw": "0.50.0",
|
|
62
|
+
"@narumitw/pi-tui-kit": "^0.49.1",
|
|
63
|
+
"@pi-plugins/fast-mode": "0.1.9",
|
|
57
64
|
"@shikijs/cli": "^4.0.2",
|
|
58
65
|
"@sinclair/typebox": "^0.34.49",
|
|
59
66
|
"ajv": "^8.17.1",
|
|
60
67
|
"ajv-formats": "^3.0.1",
|
|
61
68
|
"croner": "^10.0.1",
|
|
62
69
|
"cross-spawn": "^7.0.6",
|
|
70
|
+
"effect": "4.0.0-beta.103",
|
|
63
71
|
"grok-mermaid": "^0.2.2",
|
|
64
72
|
"jiti": "^2.7.0",
|
|
65
73
|
"js-yaml": "^5.2.2",
|
|
@@ -69,7 +77,7 @@
|
|
|
69
77
|
"open": "^10.2.0",
|
|
70
78
|
"p-limit": "^6.1.0",
|
|
71
79
|
"pi-lens": "3.8.74",
|
|
72
|
-
"pi-mcp-adapter": "2.
|
|
80
|
+
"pi-mcp-adapter": "2.21.2",
|
|
73
81
|
"pi-web-access": "0.18.0",
|
|
74
82
|
"pidusage": "^4.0.1",
|
|
75
83
|
"promise.try": "^2.0.1",
|
|
@@ -87,6 +95,7 @@
|
|
|
87
95
|
},
|
|
88
96
|
"bundledDependencies": [
|
|
89
97
|
"@czottmann/pi-automode",
|
|
98
|
+
"@dietrichgebert/ponytail",
|
|
90
99
|
"@herbertgao/pi-cc-extensions",
|
|
91
100
|
"@herbertgao/pi-copy-response",
|
|
92
101
|
"@herbertgao/pi-handoff",
|
|
@@ -99,6 +108,8 @@
|
|
|
99
108
|
"@herbertgao/pi-subagents",
|
|
100
109
|
"@herbertgao/pi-titlebar-spinner",
|
|
101
110
|
"@juicesharp/rpiv-ask-user-question",
|
|
111
|
+
"@narumitw/pi-btw",
|
|
112
|
+
"@pi-plugins/fast-mode",
|
|
102
113
|
"pi-lens",
|
|
103
114
|
"pi-mcp-adapter",
|
|
104
115
|
"pi-web-access"
|
|
@@ -108,6 +119,7 @@
|
|
|
108
119
|
},
|
|
109
120
|
"pi": {
|
|
110
121
|
"extensions": [
|
|
122
|
+
"./node_modules/@dietrichgebert/ponytail/pi-extension/index.js",
|
|
111
123
|
"./node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts",
|
|
112
124
|
"./node_modules/@herbertgao/pi-copy-response/src/index.ts",
|
|
113
125
|
"./node_modules/@herbertgao/pi-handoff/src/index.ts",
|
|
@@ -121,6 +133,8 @@
|
|
|
121
133
|
"./node_modules/@herbertgao/pi-subagents/src/index.ts",
|
|
122
134
|
"./node_modules/@herbertgao/pi-titlebar-spinner/src/index.ts",
|
|
123
135
|
"./node_modules/@juicesharp/rpiv-ask-user-question/index.ts",
|
|
136
|
+
"./node_modules/@narumitw/pi-btw/src/index.ts",
|
|
137
|
+
"./node_modules/@pi-plugins/fast-mode/dist/index.mjs",
|
|
124
138
|
"./node_modules/pi-mcp-adapter/index.ts",
|
|
125
139
|
"./node_modules/pi-lens/dist/index.js",
|
|
126
140
|
"./node_modules/pi-web-access/index.ts",
|
|
@@ -131,6 +145,7 @@
|
|
|
131
145
|
"./node_modules/@herbertgao/pi-cc-extensions/themes/cc-light.json"
|
|
132
146
|
],
|
|
133
147
|
"skills": [
|
|
148
|
+
"./node_modules/@dietrichgebert/ponytail/skills",
|
|
134
149
|
"./node_modules/pi-mcp-adapter/skills",
|
|
135
150
|
"./node_modules/pi-lens/skills"
|
|
136
151
|
]
|