@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
|
@@ -5,6 +5,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
5
5
|
import { parse as parseToml } from "smol-toml";
|
|
6
6
|
import stripJsonComments from "strip-json-comments";
|
|
7
7
|
import { getAgentPath } from "./agent-dir.ts";
|
|
8
|
+
import { getAgentPluginSummaries, loadAgentPluginConfigs, type AgentPluginSummary } from "./agent-plugin-loader.ts";
|
|
8
9
|
import { isServerDisabled, type HostConfigDiscovery, type McpConfig, type ServerEntry, type McpSettings, type ImportKind, type ServerProvenance } from "./types.ts";
|
|
9
10
|
import { toStringRecord } from "./utils.ts";
|
|
10
11
|
|
|
@@ -32,31 +33,31 @@ export const KNOWN_SERVER_PRESETS: readonly KnownServerPreset[] = [
|
|
|
32
33
|
id: "deepwiki",
|
|
33
34
|
name: "DeepWiki",
|
|
34
35
|
summary: "Ask questions about public GitHub repositories.",
|
|
35
|
-
entry: { url: "https://mcp.deepwiki.com/mcp" },
|
|
36
|
+
entry: { url: "https://mcp.deepwiki.com/mcp", protocolVersion: "auto" },
|
|
36
37
|
},
|
|
37
38
|
{
|
|
38
39
|
id: "context7",
|
|
39
40
|
name: "Context7",
|
|
40
41
|
summary: "Look up current library documentation and examples.",
|
|
41
|
-
entry: { url: "https://mcp.context7.com/mcp" },
|
|
42
|
+
entry: { url: "https://mcp.context7.com/mcp", protocolVersion: "auto" },
|
|
42
43
|
},
|
|
43
44
|
{
|
|
44
45
|
id: "notion",
|
|
45
46
|
name: "Notion",
|
|
46
47
|
summary: "Search and work with your Notion workspace.",
|
|
47
|
-
entry: { url: "https://mcp.notion.com/mcp", auth: "oauth" },
|
|
48
|
+
entry: { url: "https://mcp.notion.com/mcp", auth: "oauth", protocolVersion: "auto" },
|
|
48
49
|
},
|
|
49
50
|
{
|
|
50
51
|
id: "github",
|
|
51
52
|
name: "GitHub",
|
|
52
53
|
summary: "Work with GitHub through your Copilot account.",
|
|
53
|
-
entry: { url: "https://api.githubcopilot.com/mcp", auth: "oauth" },
|
|
54
|
+
entry: { url: "https://api.githubcopilot.com/mcp", auth: "oauth", protocolVersion: "auto" },
|
|
54
55
|
},
|
|
55
56
|
{
|
|
56
57
|
id: "chrome-devtools",
|
|
57
58
|
name: "Chrome DevTools",
|
|
58
59
|
summary: "Inspect and automate a local Chrome browser.",
|
|
59
|
-
entry: { command: "npx", args: ["-y", "chrome-devtools-mcp@
|
|
60
|
+
entry: { command: "npx", args: ["-y", "chrome-devtools-mcp@1.6.0"] },
|
|
60
61
|
},
|
|
61
62
|
];
|
|
62
63
|
|
|
@@ -137,6 +138,7 @@ export interface McpDiscoverySummary {
|
|
|
137
138
|
imports: ImportConfigSummary[];
|
|
138
139
|
hostConfigs: HostConfigSummary[];
|
|
139
140
|
hostConfigDiscovery: HostConfigDiscovery;
|
|
141
|
+
agentPlugins: AgentPluginSummary[];
|
|
140
142
|
conflicts: McpConfigConflict[];
|
|
141
143
|
hasAnyConfig: boolean;
|
|
142
144
|
hasAnyDetectedPaths: boolean;
|
|
@@ -147,6 +149,12 @@ export interface McpDiscoverySummary {
|
|
|
147
149
|
repoPrompt: RepoPromptDiscovery;
|
|
148
150
|
}
|
|
149
151
|
|
|
152
|
+
export interface McpStandardConfigSummary {
|
|
153
|
+
sources: ConfigDiscoverySource[];
|
|
154
|
+
hasSharedServers: boolean;
|
|
155
|
+
fingerprint: string;
|
|
156
|
+
}
|
|
157
|
+
|
|
150
158
|
export interface ConfigWritePreview {
|
|
151
159
|
path: string;
|
|
152
160
|
existed: boolean;
|
|
@@ -193,9 +201,8 @@ export function findAvailableImportConfigs(cwd = process.cwd()): DiscoveredImpor
|
|
|
193
201
|
return discovered;
|
|
194
202
|
}
|
|
195
203
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const sources = sourceSpecs.map((source) => {
|
|
204
|
+
function getConfigSourceSummaries(sourceSpecs: ConfigSourceSpec[]): ConfigDiscoverySource[] {
|
|
205
|
+
return sourceSpecs.map((source) => {
|
|
199
206
|
const loaded = readValidatedConfig(source.readPath, `MCP config from ${source.readPath}`);
|
|
200
207
|
return {
|
|
201
208
|
id: source.id,
|
|
@@ -207,24 +214,47 @@ export function getMcpDiscoverySummary(overridePath?: string, cwd = process.cwd(
|
|
|
207
214
|
serverCount: loaded ? Object.keys(loaded.mcpServers).length : 0,
|
|
208
215
|
} satisfies ConfigDiscoverySource;
|
|
209
216
|
});
|
|
217
|
+
}
|
|
210
218
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
219
|
+
export function getMcpStandardConfigSummary(overridePath?: string, cwd = process.cwd()): McpStandardConfigSummary {
|
|
220
|
+
const sources = getConfigSourceSummaries(getConfigSources(overridePath, cwd));
|
|
221
|
+
return {
|
|
222
|
+
sources,
|
|
223
|
+
hasSharedServers: sources.some((source) => source.kind === "shared" && source.serverCount > 0),
|
|
224
|
+
fingerprint: JSON.stringify({ sources: sources.map((source) => [source.id, source.exists, source.serverCount]) }),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function getMcpDiscoverySummary(
|
|
229
|
+
overridePath?: string,
|
|
230
|
+
cwd = process.cwd(),
|
|
231
|
+
options: { includeHostConfigs?: boolean } = {},
|
|
232
|
+
): McpDiscoverySummary {
|
|
233
|
+
const sourceSpecs = getConfigSources(overridePath, cwd);
|
|
234
|
+
const sources = getConfigSourceSummaries(sourceSpecs);
|
|
235
|
+
const includeHostConfigs = options.includeHostConfigs !== false;
|
|
236
|
+
|
|
237
|
+
const imports = includeHostConfigs
|
|
238
|
+
? (Object.keys(IMPORT_PATHS) as ImportKind[])
|
|
239
|
+
.map((kind) => {
|
|
240
|
+
const imported = loadImportedConfig(kind, cwd, `Failed to inspect imported MCP config from ${kind}:`);
|
|
241
|
+
if (!imported) return null;
|
|
242
|
+
return {
|
|
243
|
+
kind,
|
|
244
|
+
path: imported.path,
|
|
245
|
+
serverCount: Object.keys(extractServers(imported.value, kind)).length,
|
|
246
|
+
} satisfies ImportConfigSummary;
|
|
247
|
+
})
|
|
248
|
+
.filter((value): value is ImportConfigSummary => value !== null)
|
|
249
|
+
: [];
|
|
222
250
|
const hostConfigDiscovery = getConfiguredHostConfigDiscovery(overridePath, cwd);
|
|
223
251
|
const hostConfigs = imports.map((entry) => ({ ...entry, active: hostConfigDiscovery === "on" }));
|
|
224
|
-
const
|
|
225
|
-
const
|
|
252
|
+
const settings = getMergedSettings(overridePath, cwd);
|
|
253
|
+
const agentPlugins = getAgentPluginSummaries(settings?.agentPluginPaths, cwd);
|
|
254
|
+
const totalServerCount = sources.reduce((sum, source) => sum + source.serverCount, 0) + agentPlugins.reduce((sum, plugin) => sum + plugin.serverCount, 0);
|
|
255
|
+
const hasSharedServers = sources.some((source) => source.kind === "shared" && source.serverCount > 0) || agentPlugins.some(plugin => plugin.serverCount > 0);
|
|
226
256
|
const hasPiOwnedServers = sources.some((source) => source.kind === "pi" && source.serverCount > 0);
|
|
227
|
-
const hasAnyDetectedPaths = sources.some((source) => source.exists) || imports.length > 0;
|
|
257
|
+
const hasAnyDetectedPaths = sources.some((source) => source.exists) || imports.length > 0 || agentPlugins.length > 0;
|
|
228
258
|
const hasAnyConfig = totalServerCount > 0 || imports.some((entry) => entry.serverCount > 0) || hasAnyDetectedPaths;
|
|
229
259
|
|
|
230
260
|
const summaryWithoutRepoPrompt = {
|
|
@@ -232,6 +262,7 @@ export function getMcpDiscoverySummary(overridePath?: string, cwd = process.cwd(
|
|
|
232
262
|
imports,
|
|
233
263
|
hostConfigs,
|
|
234
264
|
hostConfigDiscovery,
|
|
265
|
+
agentPlugins,
|
|
235
266
|
conflicts: getConfigConflicts(sourceSpecs, imports, cwd),
|
|
236
267
|
hasAnyConfig,
|
|
237
268
|
hasAnyDetectedPaths,
|
|
@@ -243,6 +274,7 @@ export function getMcpDiscoverySummary(overridePath?: string, cwd = process.cwd(
|
|
|
243
274
|
const fingerprint = JSON.stringify({
|
|
244
275
|
sources: sources.map((source) => [source.id, source.exists, source.serverCount]),
|
|
245
276
|
imports: imports.map((entry) => [entry.kind, entry.path, entry.serverCount]),
|
|
277
|
+
agentPlugins: agentPlugins.map((entry) => [entry.path, entry.name, entry.serverCount]),
|
|
246
278
|
hostConfigDiscovery,
|
|
247
279
|
conflicts: summaryWithoutRepoPrompt.conflicts,
|
|
248
280
|
});
|
|
@@ -274,16 +306,24 @@ export function loadMcpConfig(overridePath?: string, cwd = process.cwd()): McpCo
|
|
|
274
306
|
config = mergeConfigs(config, expandImports(loaded, cwd));
|
|
275
307
|
}
|
|
276
308
|
|
|
277
|
-
|
|
309
|
+
const pluginConfig = loadAgentPluginConfigs(config.settings?.agentPluginPaths, cwd);
|
|
310
|
+
return mergeConfigs(pluginConfig, config);
|
|
278
311
|
}
|
|
279
312
|
|
|
280
|
-
function
|
|
281
|
-
let
|
|
313
|
+
function getMergedSettings(overridePath?: string, cwd = process.cwd()): McpSettings | undefined {
|
|
314
|
+
let settings: McpSettings | undefined;
|
|
282
315
|
for (const source of getConfigSources(overridePath, cwd)) {
|
|
283
316
|
const loaded = readValidatedConfig(source.readPath, `MCP config from ${source.readPath}`);
|
|
284
|
-
|
|
285
|
-
if (value === "off" || value === "prompt" || value === "on") configured = value;
|
|
317
|
+
if (loaded?.settings) settings = { ...settings, ...loaded.settings };
|
|
286
318
|
}
|
|
319
|
+
return settings;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function getConfiguredHostConfigDiscovery(overridePath?: string, cwd = process.cwd()): HostConfigDiscovery {
|
|
323
|
+
let configured: HostConfigDiscovery = "off";
|
|
324
|
+
const settings = getMergedSettings(overridePath, cwd);
|
|
325
|
+
const value = settings?.hostConfigDiscovery;
|
|
326
|
+
if (value === "off" || value === "prompt" || value === "on") configured = value;
|
|
287
327
|
return configured;
|
|
288
328
|
}
|
|
289
329
|
|
|
@@ -417,10 +457,12 @@ function getConfigSources(overridePath?: string, cwd = process.cwd()): ConfigSou
|
|
|
417
457
|
}
|
|
418
458
|
|
|
419
459
|
function mergeConfigs(base: McpConfig, next: McpConfig): McpConfig {
|
|
460
|
+
const imports = mergeImports(base.imports, next.imports);
|
|
461
|
+
const settings = next.settings ? { ...base.settings, ...next.settings } : base.settings;
|
|
420
462
|
return {
|
|
421
463
|
mcpServers: mergeServerMaps(base.mcpServers, next.mcpServers),
|
|
422
|
-
imports
|
|
423
|
-
settings
|
|
464
|
+
...(imports !== undefined ? { imports } : {}),
|
|
465
|
+
...(settings !== undefined ? { settings } : {}),
|
|
424
466
|
};
|
|
425
467
|
}
|
|
426
468
|
|
|
@@ -499,7 +541,7 @@ function expandImports(config: McpConfig, cwd = process.cwd()): McpConfig {
|
|
|
499
541
|
|
|
500
542
|
return {
|
|
501
543
|
imports: config.imports,
|
|
502
|
-
settings: config.settings,
|
|
544
|
+
...(config.settings !== undefined ? { settings: config.settings } : {}),
|
|
503
545
|
mcpServers: mergeServerMaps(importedServers, config.mcpServers),
|
|
504
546
|
};
|
|
505
547
|
}
|
|
@@ -596,22 +638,32 @@ function readValidatedConfig(path: string, label: string): McpConfig | null {
|
|
|
596
638
|
}
|
|
597
639
|
|
|
598
640
|
function validateConfig(raw: unknown): McpConfig {
|
|
599
|
-
if (!raw
|
|
641
|
+
if (!isRecord(raw)) {
|
|
600
642
|
return { mcpServers: {} };
|
|
601
643
|
}
|
|
602
644
|
|
|
603
|
-
|
|
604
|
-
|
|
645
|
+
return {
|
|
646
|
+
mcpServers: toServerEntries(raw.mcpServers ?? raw["mcp-servers"]),
|
|
647
|
+
...(Array.isArray(raw.imports) ? { imports: raw.imports as ImportKind[] } : {}),
|
|
648
|
+
...(raw.settings !== undefined ? { settings: raw.settings as McpSettings } : {}),
|
|
649
|
+
};
|
|
650
|
+
}
|
|
605
651
|
|
|
606
|
-
|
|
607
|
-
|
|
652
|
+
function toServerEntries(servers: unknown): Record<string, ServerEntry> {
|
|
653
|
+
if (!isRecord(servers)) return {};
|
|
654
|
+
const entries: Record<string, ServerEntry> = {};
|
|
655
|
+
for (const [name, entry] of Object.entries(servers)) {
|
|
656
|
+
if (isServerEntry(entry)) entries[name] = entry;
|
|
608
657
|
}
|
|
658
|
+
return entries;
|
|
659
|
+
}
|
|
609
660
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
661
|
+
function isServerEntry(value: unknown): value is ServerEntry {
|
|
662
|
+
return isRecord(value);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
666
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
615
667
|
}
|
|
616
668
|
|
|
617
669
|
function mergeOpenCodeConfigs(base: Record<string, unknown>, next: Record<string, unknown>): Record<string, unknown> {
|
|
@@ -708,8 +760,10 @@ function extractServers(config: unknown, kind: ImportKind): Record<string, Serve
|
|
|
708
760
|
|
|
709
761
|
if (raw.type === "local" && Array.isArray(raw.command) && raw.command.length > 0 && raw.command.every((value): value is string => typeof value === "string")) {
|
|
710
762
|
const env = toStringRecord(raw.environment);
|
|
763
|
+
const command = raw.command[0];
|
|
764
|
+
if (command === undefined) continue;
|
|
711
765
|
const mapped: ServerEntry = {
|
|
712
|
-
command
|
|
766
|
+
command,
|
|
713
767
|
args: raw.command.slice(1),
|
|
714
768
|
...(env ? { env } : {}),
|
|
715
769
|
...(typeof raw.cwd === "string" ? { cwd: raw.cwd } : {}),
|
|
@@ -733,6 +787,9 @@ function extractServers(config: unknown, kind: ImportKind): Record<string, Serve
|
|
|
733
787
|
...(typeof oauth.clientId === "string" ? { clientId: oauth.clientId } : {}),
|
|
734
788
|
...(typeof oauth.clientSecret === "string" ? { clientSecret: oauth.clientSecret } : {}),
|
|
735
789
|
...(typeof oauth.scope === "string" ? { scope: oauth.scope } : {}),
|
|
790
|
+
...(typeof oauth.skipIssuerMetadataValidation === "boolean"
|
|
791
|
+
? { skipIssuerMetadataValidation: oauth.skipIssuerMetadataValidation }
|
|
792
|
+
: {}),
|
|
736
793
|
};
|
|
737
794
|
}
|
|
738
795
|
mappedServers[name] = mapped;
|
|
@@ -740,12 +797,13 @@ function extractServers(config: unknown, kind: ImportKind): Record<string, Serve
|
|
|
740
797
|
continue;
|
|
741
798
|
}
|
|
742
799
|
|
|
743
|
-
if (
|
|
744
|
-
|
|
800
|
+
if (!isRecord(entry)) continue;
|
|
801
|
+
if (kind !== "codex") {
|
|
802
|
+
mappedServers[name] = entry;
|
|
745
803
|
continue;
|
|
746
804
|
}
|
|
747
805
|
|
|
748
|
-
const mapped = { ...
|
|
806
|
+
const mapped = { ...entry };
|
|
749
807
|
const bearerTokenEnv = mapped.bearer_token_env_var;
|
|
750
808
|
const httpHeaders = mapped.http_headers;
|
|
751
809
|
const envHttpHeaders = mapped.env_http_headers;
|
|
@@ -789,9 +847,12 @@ function buildUnifiedDiff(beforeText: string, afterText: string): string {
|
|
|
789
847
|
|
|
790
848
|
for (let i = rows - 1; i >= 0; i--) {
|
|
791
849
|
for (let j = cols - 1; j >= 0; j--) {
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
850
|
+
const row = lcs[i];
|
|
851
|
+
const nextRow = lcs[i + 1];
|
|
852
|
+
if (!row || !nextRow) continue;
|
|
853
|
+
row[j] = before[i] === after[j]
|
|
854
|
+
? (nextRow[j + 1] ?? 0) + 1
|
|
855
|
+
: Math.max(nextRow[j] ?? 0, row[j + 1] ?? 0);
|
|
795
856
|
}
|
|
796
857
|
}
|
|
797
858
|
|
|
@@ -805,7 +866,7 @@ function buildUnifiedDiff(beforeText: string, afterText: string): string {
|
|
|
805
866
|
j++;
|
|
806
867
|
continue;
|
|
807
868
|
}
|
|
808
|
-
if (j < cols && (i === rows || lcs[i][j + 1] >= lcs[i + 1][j])) {
|
|
869
|
+
if (j < cols && (i === rows || (lcs[i]?.[j + 1] ?? 0) >= (lcs[i + 1]?.[j] ?? 0))) {
|
|
809
870
|
lines.push(`+ ${after[j]}`);
|
|
810
871
|
j++;
|
|
811
872
|
continue;
|
|
@@ -1108,7 +1169,7 @@ export function getServerProvenance(overridePath?: string, cwd = process.cwd()):
|
|
|
1108
1169
|
provenance.set(name, {
|
|
1109
1170
|
path: source.writePath,
|
|
1110
1171
|
kind: source.kind,
|
|
1111
|
-
importKind: source.importKind,
|
|
1172
|
+
...(source.importKind !== undefined ? { importKind: source.importKind } : {}),
|
|
1112
1173
|
});
|
|
1113
1174
|
}
|
|
1114
1175
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentToolResult, AgentToolUpdateCallback, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { UrlElicitationRequiredError } from "@modelcontextprotocol/
|
|
2
|
+
import { UrlElicitationRequiredError, type Client } from "@modelcontextprotocol/client";
|
|
3
3
|
import type { McpExtensionState } from "./state.ts";
|
|
4
4
|
import type { DirectToolSpec, McpConfig, McpContent, ToolPrefix } from "./types.ts";
|
|
5
5
|
import type { MetadataCache } from "./metadata-cache.ts";
|
|
@@ -19,6 +19,8 @@ import { SessionRecoveryAuthRequiredError, withSessionRecovery } from "./session
|
|
|
19
19
|
import { combineAbortSignals, isAbortError } from "./runtime-owner.ts";
|
|
20
20
|
import { ensureToolCallApproved } from "./tool-approval.ts";
|
|
21
21
|
|
|
22
|
+
type ClientCallToolResult = Awaited<ReturnType<Client["callTool"]>>;
|
|
23
|
+
|
|
22
24
|
const BUILTIN_NAMES = new Set(["read", "bash", "edit", "write", "grep", "find", "ls", "mcp"]);
|
|
23
25
|
const INSTRUCTIONS_SNIPPET_LENGTH = 150;
|
|
24
26
|
export const DIRECT_TOOLS_ADVISORY_THRESHOLD = 75;
|
|
@@ -92,7 +94,10 @@ async function attemptDirectAutoAuth(
|
|
|
92
94
|
: { authStorageOptions: state.authStorageOptions, runtime: state.oauthRuntime },
|
|
93
95
|
);
|
|
94
96
|
} else {
|
|
95
|
-
await authenticate(serverName, serverUrl, definition, {
|
|
97
|
+
await authenticate(serverName, serverUrl, definition, {
|
|
98
|
+
...(signal ? { signal } : {}),
|
|
99
|
+
runtime: state.oauthRuntime,
|
|
100
|
+
});
|
|
96
101
|
}
|
|
97
102
|
return { status: "success" };
|
|
98
103
|
} catch (error) {
|
|
@@ -162,9 +167,9 @@ export function resolveDirectTools(
|
|
|
162
167
|
originalName: tool.name,
|
|
163
168
|
prefixedName,
|
|
164
169
|
description: tool.description ?? "",
|
|
165
|
-
inputSchema: tool.inputSchema,
|
|
166
|
-
uiResourceUri: tool.uiResourceUri,
|
|
167
|
-
uiStreamMode: tool.uiStreamMode,
|
|
170
|
+
...(tool.inputSchema !== undefined ? { inputSchema: tool.inputSchema } : {}),
|
|
171
|
+
...(tool.uiResourceUri !== undefined ? { uiResourceUri: tool.uiResourceUri } : {}),
|
|
172
|
+
...(tool.uiStreamMode !== undefined ? { uiStreamMode: tool.uiStreamMode } : {}),
|
|
168
173
|
});
|
|
169
174
|
}
|
|
170
175
|
|
|
@@ -207,7 +212,7 @@ export function buildProxyDescription(
|
|
|
207
212
|
directSpecs: DirectToolSpec[],
|
|
208
213
|
): string {
|
|
209
214
|
const prefix = config.settings?.toolPrefix ?? "server";
|
|
210
|
-
let desc = `MCP gateway — server status, tool search/describe, auth, and single MCP tool calls. When one request needs several MCP calls with logic between them, use
|
|
215
|
+
let desc = `MCP gateway — server status, tool search/describe, auth, and single MCP tool calls. When one request needs several MCP calls with logic between them, use mcpScript. Non-MCP Pi tools should be called directly, not through mcp.\n`;
|
|
211
216
|
|
|
212
217
|
const directByServer = new Map<string, number>();
|
|
213
218
|
for (const spec of directSpecs) {
|
|
@@ -223,7 +228,7 @@ export function buildProxyDescription(
|
|
|
223
228
|
const serverSummaries: string[] = [];
|
|
224
229
|
for (const serverName of Object.keys(config.mcpServers)) {
|
|
225
230
|
const definition = config.mcpServers[serverName];
|
|
226
|
-
if (isServerDisabled(definition)) continue;
|
|
231
|
+
if (!definition || isServerDisabled(definition)) continue;
|
|
227
232
|
const entry = cache?.servers?.[serverName];
|
|
228
233
|
const effectivePrefix = resolveToolPrefix(definition, prefix);
|
|
229
234
|
const toolCount = (entry?.tools ?? []).filter(
|
|
@@ -377,11 +382,11 @@ export function createDirectToolExecutor(
|
|
|
377
382
|
name: spec.prefixedName,
|
|
378
383
|
originalName: spec.originalName,
|
|
379
384
|
description: spec.description,
|
|
380
|
-
inputSchema: spec.inputSchema,
|
|
381
|
-
resourceUri: spec.resourceUri,
|
|
382
|
-
uiResourceUri: spec.uiResourceUri,
|
|
383
|
-
uiStreamMode: spec.uiStreamMode,
|
|
384
|
-
}, params, ownedSignal);
|
|
385
|
+
...(spec.inputSchema !== undefined ? { inputSchema: spec.inputSchema } : {}),
|
|
386
|
+
...(spec.resourceUri !== undefined ? { resourceUri: spec.resourceUri } : {}),
|
|
387
|
+
...(spec.uiResourceUri !== undefined ? { uiResourceUri: spec.uiResourceUri } : {}),
|
|
388
|
+
...(spec.uiStreamMode !== undefined ? { uiStreamMode: spec.uiStreamMode } : {}),
|
|
389
|
+
}, params, ownedSignal, spec.resourceUri ? "resource" : "direct");
|
|
385
390
|
if (approval.ok === false) {
|
|
386
391
|
const denied = approval.reason === "denied";
|
|
387
392
|
const message = denied
|
|
@@ -431,7 +436,12 @@ export function createDirectToolExecutor(
|
|
|
431
436
|
|
|
432
437
|
if (spec.resourceUri) {
|
|
433
438
|
const result = await withSessionRecovery(
|
|
434
|
-
{
|
|
439
|
+
{
|
|
440
|
+
manager: state.manager,
|
|
441
|
+
config: state.config,
|
|
442
|
+
...(ownedSignal ? { signal: ownedSignal } : {}),
|
|
443
|
+
onNeedsAuth: recoverAuthConnection,
|
|
444
|
+
},
|
|
435
445
|
spec.serverName,
|
|
436
446
|
(conn) => conn.client.readResource({ uri: spec.resourceUri! }, requestOptions),
|
|
437
447
|
);
|
|
@@ -453,22 +463,27 @@ export function createDirectToolExecutor(
|
|
|
453
463
|
toolName: spec.originalName,
|
|
454
464
|
toolArgs: params ?? {},
|
|
455
465
|
uiResourceUri: spec.uiResourceUri!,
|
|
456
|
-
streamMode: spec.uiStreamMode,
|
|
457
|
-
signal,
|
|
466
|
+
...(spec.uiStreamMode !== undefined ? { streamMode: spec.uiStreamMode } : {}),
|
|
467
|
+
...(signal ? { signal } : {}),
|
|
458
468
|
onNeedsAuth: recoverAuthConnection,
|
|
459
469
|
})
|
|
460
470
|
: null;
|
|
461
471
|
|
|
462
|
-
const result = await withSessionRecovery(
|
|
463
|
-
{
|
|
472
|
+
const result = await withSessionRecovery<ClientCallToolResult>(
|
|
473
|
+
{
|
|
474
|
+
manager: state.manager,
|
|
475
|
+
config: state.config,
|
|
476
|
+
...(ownedSignal ? { signal: ownedSignal } : {}),
|
|
477
|
+
onNeedsAuth: recoverAuthConnection,
|
|
478
|
+
},
|
|
464
479
|
spec.serverName,
|
|
465
480
|
(conn) => abortable(conn.client.callTool({
|
|
466
481
|
name: spec.originalName,
|
|
467
482
|
arguments: params ?? {},
|
|
468
483
|
_meta: uiSession?.requestMeta,
|
|
469
|
-
},
|
|
484
|
+
}, requestOptions), ownedSignal),
|
|
470
485
|
);
|
|
471
|
-
uiSession?.sendToolResult(result as unknown as import("@modelcontextprotocol/
|
|
486
|
+
uiSession?.sendToolResult(result as unknown as import("@modelcontextprotocol/client").CallToolResult);
|
|
472
487
|
|
|
473
488
|
if (result.isError) {
|
|
474
489
|
const mcpContent = (result.content ?? []) as McpContent[];
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import type { ExtensionUIContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type { Client } from "@modelcontextprotocol/
|
|
2
|
+
import type { Client } from "@modelcontextprotocol/client";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
McpError,
|
|
4
|
+
ProtocolError,
|
|
5
|
+
ProtocolErrorCode,
|
|
7
6
|
type ElicitRequest,
|
|
8
7
|
type ElicitRequestFormParams,
|
|
9
8
|
type ElicitRequestURLParams,
|
|
10
9
|
type ElicitResult,
|
|
11
|
-
} from "@modelcontextprotocol/
|
|
12
|
-
import { AjvJsonSchemaValidator } from "@modelcontextprotocol/
|
|
13
|
-
import type { JsonSchemaType } from "@modelcontextprotocol/
|
|
10
|
+
} from "@modelcontextprotocol/client";
|
|
11
|
+
import { AjvJsonSchemaValidator } from "@modelcontextprotocol/client/validators/ajv";
|
|
12
|
+
import type { JsonSchemaType } from "@modelcontextprotocol/client";
|
|
14
13
|
import open from "open";
|
|
15
14
|
|
|
16
15
|
export type ElicitationValue = string | number | boolean | string[] | undefined;
|
|
17
16
|
type FormProperty = ElicitRequestFormParams["requestedSchema"]["properties"][string];
|
|
17
|
+
type FieldCollectionResult = { status: "cancelled" } | { status: "collected"; value: ElicitationValue };
|
|
18
18
|
|
|
19
|
-
export type ElicitationUIContext = ExtensionUIContext
|
|
19
|
+
export type ElicitationUIContext = Pick<ExtensionUIContext, "select" | "input" | "notify">;
|
|
20
20
|
|
|
21
21
|
export interface ElicitationHandlerOptions {
|
|
22
22
|
serverName: string;
|
|
@@ -28,7 +28,7 @@ export interface ElicitationHandlerOptions {
|
|
|
28
28
|
export type ServerElicitationConfig = Omit<ElicitationHandlerOptions, "serverName" | "onUrlAccepted">;
|
|
29
29
|
|
|
30
30
|
export function registerElicitationHandler(client: Client, options: ElicitationHandlerOptions): void {
|
|
31
|
-
client.setRequestHandler(
|
|
31
|
+
client.setRequestHandler("elicitation/create", request =>
|
|
32
32
|
handleElicitationRequest(options, request));
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -45,18 +45,19 @@ export async function handleFormElicitation(
|
|
|
45
45
|
options: ElicitationHandlerOptions,
|
|
46
46
|
params: ElicitRequestFormParams,
|
|
47
47
|
): Promise<ElicitResult> {
|
|
48
|
+
const properties = Object.entries(params.requestedSchema.properties);
|
|
48
49
|
const decision = await options.ui.select(
|
|
49
50
|
`MCP Input Request\nServer: ${options.serverName}\n\n${params.message}`,
|
|
50
51
|
["Continue", "Decline"],
|
|
51
52
|
);
|
|
52
53
|
if (decision === undefined) return { action: "cancel" };
|
|
53
54
|
if (decision === "Decline") return { action: "decline" };
|
|
55
|
+
if (properties.length === 0) return { action: "accept", content: {} };
|
|
54
56
|
|
|
55
57
|
const values: Record<string, ElicitationValue> = {};
|
|
56
|
-
const properties = Object.entries(params.requestedSchema.properties);
|
|
57
58
|
for (const [name, schema] of properties) {
|
|
58
59
|
const value = await collectValidField(options.ui, params, name, schema);
|
|
59
|
-
if (
|
|
60
|
+
if (value.status === "cancelled") return { action: "cancel" };
|
|
60
61
|
values[name] = value.value;
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -64,7 +65,7 @@ export async function handleFormElicitation(
|
|
|
64
65
|
const content = coerceAndValidateFormValues(params, values);
|
|
65
66
|
const action = await options.ui.select(
|
|
66
67
|
formatReview(options.serverName, properties, content),
|
|
67
|
-
|
|
68
|
+
["Submit", "Edit", "Decline"],
|
|
68
69
|
);
|
|
69
70
|
if (action === undefined) return { action: "cancel" };
|
|
70
71
|
if (action === "Decline") return { action: "decline" };
|
|
@@ -77,7 +78,7 @@ export async function handleFormElicitation(
|
|
|
77
78
|
if (!property) continue;
|
|
78
79
|
const [name, schema] = property;
|
|
79
80
|
const value = await collectValidField(options.ui, params, name, schema, values[name]);
|
|
80
|
-
if (
|
|
81
|
+
if (value.status === "cancelled") return { action: "cancel" };
|
|
81
82
|
values[name] = value.value;
|
|
82
83
|
}
|
|
83
84
|
}
|
|
@@ -88,11 +89,11 @@ async function collectValidField(
|
|
|
88
89
|
name: string,
|
|
89
90
|
schema: FormProperty,
|
|
90
91
|
current?: ElicitationValue,
|
|
91
|
-
): Promise<
|
|
92
|
+
): Promise<FieldCollectionResult> {
|
|
92
93
|
const required = params.requestedSchema.required?.includes(name) === true;
|
|
93
94
|
while (true) {
|
|
94
95
|
const result = await collectField(ui, params, name, schema, current);
|
|
95
|
-
if (
|
|
96
|
+
if (result.status === "cancelled") return result;
|
|
96
97
|
try {
|
|
97
98
|
coerceAndValidateFormValues({
|
|
98
99
|
...params,
|
|
@@ -116,7 +117,7 @@ async function collectField(
|
|
|
116
117
|
name: string,
|
|
117
118
|
schema: FormProperty,
|
|
118
119
|
current?: ElicitationValue,
|
|
119
|
-
): Promise<
|
|
120
|
+
): Promise<FieldCollectionResult> {
|
|
120
121
|
const required = params.requestedSchema.required?.includes(name) === true;
|
|
121
122
|
const title = [schema.title ?? humanizeName(name), required ? "(required)" : "", schema.description]
|
|
122
123
|
.filter(Boolean)
|
|
@@ -136,10 +137,10 @@ async function collectField(
|
|
|
136
137
|
const omit = required ? undefined : uniqueAction("Omit", actions);
|
|
137
138
|
if (omit) actions.push(omit);
|
|
138
139
|
const action = await ui.select(title, actions);
|
|
139
|
-
if (action === undefined) return {
|
|
140
|
-
if (action === useDefault) return {
|
|
141
|
-
if (action === omit) return {
|
|
142
|
-
return {
|
|
140
|
+
if (action === undefined) return { status: "cancelled" };
|
|
141
|
+
if (action === useDefault) return { status: "collected", value: schema.default };
|
|
142
|
+
if (action === omit) return { status: "collected", value: undefined };
|
|
143
|
+
return { status: "collected", value: choices[displays.indexOf(action)]?.value };
|
|
143
144
|
}
|
|
144
145
|
|
|
145
146
|
if (schema.type === "boolean") {
|
|
@@ -147,10 +148,10 @@ async function collectField(
|
|
|
147
148
|
if (schema.default !== undefined) actions.push("Use default");
|
|
148
149
|
if (!required) actions.push("Omit");
|
|
149
150
|
const action = await ui.select(title, actions);
|
|
150
|
-
if (action === undefined) return {
|
|
151
|
-
if (action === "Use default") return {
|
|
152
|
-
if (action === "Omit") return {
|
|
153
|
-
return {
|
|
151
|
+
if (action === undefined) return { status: "cancelled" };
|
|
152
|
+
if (action === "Use default") return { status: "collected", value: schema.default };
|
|
153
|
+
if (action === "Omit") return { status: "collected", value: undefined };
|
|
154
|
+
return { status: "collected", value: action === "Yes" };
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
if (schema.type === "array") {
|
|
@@ -158,9 +159,9 @@ async function collectField(
|
|
|
158
159
|
if (schema.default !== undefined) actions.push("Use default");
|
|
159
160
|
if (!required) actions.push("Omit");
|
|
160
161
|
const action = await ui.select(title, actions);
|
|
161
|
-
if (action === undefined) return {
|
|
162
|
-
if (action === "Use default") return {
|
|
163
|
-
if (action === "Omit") return {
|
|
162
|
+
if (action === undefined) return { status: "cancelled" };
|
|
163
|
+
if (action === "Use default") return { status: "collected", value: schema.default };
|
|
164
|
+
if (action === "Omit") return { status: "collected", value: undefined };
|
|
164
165
|
|
|
165
166
|
const choices = extractMultiSelectOptions(schema);
|
|
166
167
|
const selected = new Set(Array.isArray(current) ? current : []);
|
|
@@ -168,8 +169,8 @@ async function collectField(
|
|
|
168
169
|
const displays = uniqueLabels(choices.map(choice => selected.has(choice.value) ? `✓ ${choice.display}` : choice.display));
|
|
169
170
|
const done = uniqueAction("Done", displays);
|
|
170
171
|
const picked = await ui.select(title, [...displays, done]);
|
|
171
|
-
if (picked === undefined) return {
|
|
172
|
-
if (picked === done) return {
|
|
172
|
+
if (picked === undefined) return { status: "cancelled" };
|
|
173
|
+
if (picked === done) return { status: "collected", value: [...selected] };
|
|
173
174
|
const choice = choices[displays.indexOf(picked)];
|
|
174
175
|
if (!choice) continue;
|
|
175
176
|
if (selected.has(choice.value)) selected.delete(choice.value);
|
|
@@ -181,11 +182,11 @@ async function collectField(
|
|
|
181
182
|
if (schema.default !== undefined) actions.push("Use default");
|
|
182
183
|
if (!required) actions.push("Omit");
|
|
183
184
|
const action = await ui.select(title, actions);
|
|
184
|
-
if (action === undefined) return {
|
|
185
|
-
if (action === "Use default") return {
|
|
186
|
-
if (action === "Omit") return {
|
|
185
|
+
if (action === undefined) return { status: "cancelled" };
|
|
186
|
+
if (action === "Use default") return { status: "collected", value: schema.default };
|
|
187
|
+
if (action === "Omit") return { status: "collected", value: undefined };
|
|
187
188
|
const entered = await ui.input(title, current === undefined ? undefined : String(current));
|
|
188
|
-
return entered === undefined ? {
|
|
189
|
+
return entered === undefined ? { status: "cancelled" } : { status: "collected", value: entered };
|
|
189
190
|
}
|
|
190
191
|
|
|
191
192
|
export function coerceAndValidateFormValues(
|
|
@@ -305,16 +306,16 @@ export async function handleUrlElicitation(
|
|
|
305
306
|
options: ElicitationHandlerOptions,
|
|
306
307
|
params: ElicitRequestURLParams,
|
|
307
308
|
): Promise<ElicitResult> {
|
|
308
|
-
if (!options.allowUrl) throw new
|
|
309
|
+
if (!options.allowUrl) throw new ProtocolError(ProtocolErrorCode.InvalidParams, "URL elicitation is not supported");
|
|
309
310
|
|
|
310
311
|
let parsed: URL;
|
|
311
312
|
try {
|
|
312
313
|
parsed = new URL(params.url);
|
|
313
314
|
} catch {
|
|
314
|
-
throw new
|
|
315
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, "URL elicitation supplied an invalid URL");
|
|
315
316
|
}
|
|
316
317
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
317
|
-
throw new
|
|
318
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, "URL elicitation only supports HTTP and HTTPS URLs");
|
|
318
319
|
}
|
|
319
320
|
|
|
320
321
|
const decision = await options.ui.select([
|