@oh-my-pi/pi-coding-agent 17.2.9 → 17.2.11
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/CHANGELOG.md +83 -0
- package/dist/{CHANGELOG-8yy8h3bc.md → CHANGELOG-d9xenpn9.md} +83 -0
- package/dist/cli.js +10208 -12596
- package/dist/types/capability/mcp.d.ts +11 -0
- package/dist/types/capability/skill.d.ts +6 -0
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/command-help.d.ts +3 -0
- package/dist/types/commands/share.d.ts +25 -0
- package/dist/types/commit/agentic/index.d.ts +3 -1
- package/dist/types/commit/execute.d.ts +32 -0
- package/dist/types/commit/index.d.ts +2 -1
- package/dist/types/commit/pipeline.d.ts +7 -1
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/model-resolver.d.ts +13 -0
- package/dist/types/config/settings-schema.d.ts +1 -34
- package/dist/types/discovery/agent-plugin-format.d.ts +143 -0
- package/dist/types/discovery/agent-plugins.d.ts +1 -0
- package/dist/types/discovery/contained-path.d.ts +34 -0
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/extensibility/custom-commands/types.d.ts +5 -5
- package/dist/types/extensibility/custom-tools/types.d.ts +3 -3
- package/dist/types/extensibility/extensions/loader.d.ts +9 -2
- package/dist/types/extensibility/extensions/types.d.ts +45 -8
- package/dist/types/extensibility/hooks/types.d.ts +5 -5
- package/dist/types/extensibility/shared-events.d.ts +3 -2
- package/dist/types/extensibility/skills.d.ts +5 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/main.d.ts +1 -1
- package/dist/types/mcp/transports/header-policy.d.ts +45 -0
- package/dist/types/mcp/types.d.ts +15 -0
- package/dist/types/modes/acp/acp-agent.d.ts +1 -1
- package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
- package/dist/types/modes/acp/acp-event-mapper.d.ts +1 -1
- package/dist/types/modes/acp/acp-mode.d.ts +1 -1
- package/dist/types/modes/components/agent-hub-projection.d.ts +38 -0
- package/dist/types/modes/components/agent-hub-renderer.d.ts +41 -0
- package/dist/types/modes/components/agent-hub.d.ts +14 -5
- package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
- package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -2
- package/dist/types/modes/components/model-browser.d.ts +9 -1
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/interactive-mode.d.ts +8 -1
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +3 -2
- package/dist/types/registry/agent-registry.d.ts +47 -0
- package/dist/types/registry/persisted-agents.d.ts +3 -1
- package/dist/types/secrets/index.d.ts +23 -1
- package/dist/types/session/agent-session-events.d.ts +2 -2
- package/dist/types/session/agent-session.d.ts +4 -2
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/prewalk.d.ts +3 -1
- package/dist/types/session/session-entries.d.ts +10 -0
- package/dist/types/session/session-loader.d.ts +11 -1
- package/dist/types/session/session-manager.d.ts +9 -1
- package/dist/types/session/session-tools.d.ts +18 -1
- package/dist/types/session/turn-recovery.d.ts +7 -2
- package/dist/types/slash-commands/acp-builtins.d.ts +1 -1
- package/dist/types/slash-commands/available-commands.d.ts +1 -1
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/index.d.ts +4 -3
- package/dist/types/task/read-only-policy.d.ts +3 -0
- package/dist/types/task/structured-subagent.d.ts +2 -0
- package/dist/types/task/types.d.ts +4 -0
- package/dist/types/tools/bash.d.ts +3 -3
- package/dist/types/tools/browser/launch.d.ts +2 -1
- package/dist/types/tools/browser/tab-worker.d.ts +2 -2
- package/dist/types/tools/path-utils.d.ts +1 -0
- package/dist/types/tools/read.d.ts +1 -5
- package/dist/types/tools/run-scope.d.ts +27 -1
- package/dist/types/tools/shell-tokenize.d.ts +16 -0
- package/dist/types/tools/terminal-output.d.ts +1 -1
- package/dist/types/utils/turndown.d.ts +1 -1
- package/dist/types/vibe/runtime.d.ts +2 -3
- package/dist/types/web/scrapers/readthedocs.d.ts +0 -3
- package/dist/types/web/search/providers/browser-headers.d.ts +2 -3
- package/examples/custom-tools/README.md +1 -1
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/api-demo.ts +5 -6
- package/examples/extensions/chalk-logger.ts +1 -1
- package/examples/extensions/hello.ts +2 -2
- package/examples/extensions/reload-runtime.ts +1 -1
- package/examples/extensions/tools.ts +2 -2
- package/examples/extensions/with-deps/index.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/package.json +15 -31
- package/scripts/bundle-dist.ts +1 -11
- package/scripts/legacy-pi-virtual-module.ts +4 -1
- package/src/advisor/runtime.ts +54 -0
- package/src/capability/mcp.ts +11 -0
- package/src/capability/skill.ts +6 -0
- package/src/cli/agents-cli.ts +1 -1
- package/src/cli/args.ts +24 -2
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/auth-gateway-cli.ts +1 -1
- package/src/cli/bench-cli.ts +1 -1
- package/src/cli/claude-trace-cli.ts +1 -1
- package/src/cli/command-help.ts +4 -0
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/file-processor.ts +1 -1
- package/src/cli/flag-tables.ts +4 -0
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli/grep-cli.ts +1 -1
- package/src/cli/grievances-cli.ts +1 -1
- package/src/cli/help-extra.ts +1 -1
- package/src/cli/models-cli.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/read-cli.ts +1 -1
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli/shell-cli.ts +1 -1
- package/src/cli/ssh-cli.ts +1 -1
- package/src/cli/stats-cli.ts +1 -1
- package/src/cli/tiny-models-cli.ts +1 -1
- package/src/cli/ttsr-cli.ts +1 -1
- package/src/cli/update-cli.ts +1 -1
- package/src/cli/usage-cli.ts +1 -1
- package/src/cli/web-search-cli.ts +1 -1
- package/src/cli/worktree-cli.ts +1 -1
- package/src/cli-commands.ts +5 -0
- package/src/commands/commit.ts +16 -3
- package/src/commands/say.ts +1 -1
- package/src/commands/share.ts +71 -0
- package/src/commands/token.ts +1 -1
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/agentic/index.ts +39 -21
- package/src/commit/cli.ts +1 -1
- package/src/commit/execute.ts +56 -0
- package/src/commit/index.ts +2 -1
- package/src/commit/pipeline.ts +20 -7
- package/src/config/model-discovery.ts +1 -1
- package/src/config/model-registry.ts +21 -1
- package/src/config/model-resolver.ts +54 -7
- package/src/config/settings-schema.ts +2 -33
- package/src/config/settings.ts +46 -0
- package/src/discovery/agent-plugin-format.ts +551 -0
- package/src/discovery/agent-plugins.ts +341 -0
- package/src/discovery/agents.ts +4 -2
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +5 -4
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +1 -1
- package/src/discovery/claude-plugins.ts +21 -5
- package/src/discovery/contained-path.ts +78 -0
- package/src/discovery/helpers.ts +23 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/omp-plugins.ts +20 -7
- package/src/eval/py/runner.py +38 -1
- package/src/exec/non-interactive-env.ts +1 -0
- package/src/extensibility/custom-commands/loader.ts +4 -4
- package/src/extensibility/custom-commands/types.ts +5 -5
- package/src/extensibility/custom-tools/loader.ts +4 -4
- package/src/extensibility/custom-tools/types.ts +3 -3
- package/src/extensibility/extensions/loader.ts +85 -20
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +51 -8
- package/src/extensibility/hooks/loader.ts +4 -5
- package/src/extensibility/hooks/types.ts +5 -5
- package/src/extensibility/plugins/legacy-pi-compat.ts +53 -0
- package/src/extensibility/plugins/marketplace/manager.ts +2 -1
- package/src/extensibility/shared-events.ts +3 -2
- package/src/extensibility/skills.ts +9 -0
- package/src/index.ts +2 -2
- package/src/internal-urls/memory-protocol.ts +5 -1
- package/src/internal-urls/skill-protocol.ts +14 -0
- package/src/internal-urls/vault-protocol.ts +2 -2
- package/src/launch/client.ts +11 -1
- package/src/launch/protocol.ts +7 -2
- package/src/launch/terminal-output.ts +1 -1
- package/src/main.ts +88 -23
- package/src/markit/converters/docx.ts +1 -1
- package/src/markit/converters/epub.ts +1 -1
- package/src/markit/converters/pptx.ts +1 -1
- package/src/markit/converters/xlsx.ts +1 -1
- package/src/mcp/config.ts +3 -0
- package/src/mcp/manager.ts +12 -9
- package/src/mcp/transports/header-policy.ts +95 -0
- package/src/mcp/transports/http.ts +35 -52
- package/src/mcp/transports/sse.ts +20 -27
- package/src/mcp/types.ts +15 -0
- package/src/modes/acp/acp-agent.ts +20 -9
- package/src/modes/acp/acp-client-bridge.ts +1 -1
- package/src/modes/acp/acp-event-mapper.ts +1 -1
- package/src/modes/acp/acp-mode.ts +1 -1
- package/src/modes/components/agent-dashboard.ts +2 -0
- package/src/modes/components/agent-hub-projection.ts +248 -0
- package/src/modes/components/agent-hub-renderer.ts +194 -0
- package/src/modes/components/agent-hub.ts +674 -207
- package/src/modes/components/agent-transcript-viewer.ts +3 -0
- package/src/modes/components/assistant-message.ts +1 -1
- package/src/modes/components/chat-transcript-builder.ts +3 -0
- package/src/modes/components/custom-editor.ts +0 -9
- package/src/modes/components/extensions/inspector-panel.ts +11 -6
- package/src/modes/components/footer.ts +1 -3
- package/src/modes/components/model-browser.ts +11 -3
- package/src/modes/components/model-hub.ts +4 -1
- package/src/modes/components/status-line/component.ts +1 -0
- package/src/modes/components/status-line/segments.ts +10 -7
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/tool-execution.ts +13 -16
- package/src/modes/components/tree-selector.ts +62 -3
- package/src/modes/controllers/command-controller.ts +4 -1
- package/src/modes/controllers/event-controller.ts +44 -20
- package/src/modes/controllers/extension-ui-controller.ts +2 -2
- package/src/modes/controllers/input-controller.ts +20 -2
- package/src/modes/controllers/mcp-command-controller.ts +81 -20
- package/src/modes/controllers/selector-controller.ts +41 -49
- package/src/modes/controllers/streaming-reveal.ts +1 -1
- package/src/modes/interactive-mode.ts +13 -2
- package/src/modes/rpc/rpc-mode.ts +2 -2
- package/src/modes/runtime-init.ts +1 -1
- package/src/modes/theme/theme.ts +2 -2
- package/src/modes/utils/transcript-render-helpers.ts +4 -2
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/tools/computer.md +1 -1
- package/src/registry/agent-lifecycle.ts +14 -7
- package/src/registry/agent-registry.ts +65 -2
- package/src/registry/persisted-agents.ts +341 -16
- package/src/sdk.ts +6 -49
- package/src/secrets/index.ts +52 -1
- package/src/session/agent-session-events.ts +2 -2
- package/src/session/agent-session.ts +54 -16
- package/src/session/messages.ts +1 -0
- package/src/session/prewalk.ts +57 -17
- package/src/session/session-context.ts +3 -5
- package/src/session/session-entries.ts +10 -0
- package/src/session/session-handoff.ts +5 -1
- package/src/session/session-listing.ts +1 -1
- package/src/session/session-loader.ts +74 -8
- package/src/session/session-manager.ts +22 -2
- package/src/session/session-paths.ts +57 -9
- package/src/session/session-tools.ts +65 -4
- package/src/session/todo-tracker.ts +11 -1
- package/src/session/turn-recovery.ts +96 -74
- package/src/slash-commands/acp-builtins.ts +1 -1
- package/src/slash-commands/available-commands.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +12 -8
- package/src/task/executor.ts +70 -20
- package/src/task/index.ts +30 -34
- package/src/task/isolation-runner.ts +24 -11
- package/src/task/persisted-revive.ts +12 -5
- package/src/task/read-only-policy.ts +27 -0
- package/src/task/structured-subagent.ts +14 -4
- package/src/task/types.ts +4 -0
- package/src/tools/auto-generated-guard.ts +1 -1
- package/src/tools/bash-interactive.ts +4 -4
- package/src/tools/bash-skill-urls.ts +15 -0
- package/src/tools/bash.ts +16 -17
- package/src/tools/browser/cmux/cmux-tab.ts +66 -18
- package/src/tools/browser/launch.ts +41 -12
- package/src/tools/browser/readable.ts +9 -9
- package/src/tools/browser/tab-supervisor.ts +12 -3
- package/src/tools/browser/tab-worker-entry.ts +1 -1
- package/src/tools/browser/tab-worker.ts +101 -12
- package/src/tools/debug.ts +1 -1
- package/src/tools/fetch.ts +1 -1
- package/src/tools/jtd-to-json-schema.ts +123 -21
- package/src/tools/path-utils.ts +9 -2
- package/src/tools/read.ts +21 -9
- package/src/tools/run-scope.ts +290 -4
- package/src/tools/shell-tokenize.ts +98 -0
- package/src/tools/terminal-output.ts +1 -1
- package/src/tools/todo.ts +1 -1
- package/src/utils/clipboard.ts +10 -2
- package/src/utils/external-editor.ts +4 -2
- package/src/utils/jj.ts +1 -1
- package/src/utils/turndown.ts +1 -2
- package/src/vibe/runtime.ts +5 -5
- package/src/web/scrapers/arxiv.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +1 -1
- package/src/web/scrapers/iacr.ts +1 -1
- package/src/web/scrapers/readthedocs.ts +2 -1
- package/src/web/scrapers/twitter.ts +2 -2
- package/src/web/scrapers/types.ts +1 -1
- package/src/web/scrapers/wikipedia.ts +1 -1
- package/src/web/search/providers/browser-headers.ts +12 -39
- package/src/web/search/providers/codex.ts +3 -26
- package/src/web/search/providers/ecosia.ts +1 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/google.ts +1 -1
- package/src/web/search/providers/mojeek.ts +1 -1
- package/src/web/search/providers/startpage.ts +1 -1
- package/src/markit/converters/mammoth.d.ts +0 -24
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP header precedence and redirect-origin policy for remote MCP transports.
|
|
3
|
+
*
|
|
4
|
+
* Two invariants, applied at every transport fetch:
|
|
5
|
+
*
|
|
6
|
+
* 1. Client-generated headers (protocol headers like `Content-Type`, `Accept`,
|
|
7
|
+
* `Mcp-Session-Id`, and authorization) take precedence over configured
|
|
8
|
+
* headers with the same case-insensitive name. Configured headers can never
|
|
9
|
+
* corrupt the MCP wire protocol via casing tricks.
|
|
10
|
+
* 2. Origin-locked servers (Agent Plugins §7.2.1) never forward configured
|
|
11
|
+
* headers to a different origin: redirects are followed manually and
|
|
12
|
+
* configured headers are attached only when the hop targets the configured
|
|
13
|
+
* origin. Method-changing redirects of non-GET requests are refused.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Header buckets for one MCP HTTP request. */
|
|
17
|
+
export interface MCPHeaderSources {
|
|
18
|
+
/** Client-generated HTTP/MCP/authorization headers; win case-insensitively. */
|
|
19
|
+
generated: Record<string, string>;
|
|
20
|
+
/** Configured headers from the server entry (package or user config). */
|
|
21
|
+
configured?: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Merge configured headers under client-generated ones: a configured entry is
|
|
26
|
+
* dropped when a generated header with the same case-insensitive name exists.
|
|
27
|
+
*/
|
|
28
|
+
export function mergeMCPHeaders({ generated, configured }: MCPHeaderSources): Record<string, string> {
|
|
29
|
+
if (!configured) return { ...generated };
|
|
30
|
+
const generatedNames = new Set<string>();
|
|
31
|
+
for (const name in generated) generatedNames.add(name.toLowerCase());
|
|
32
|
+
const merged: Record<string, string> = {};
|
|
33
|
+
for (const name in configured) {
|
|
34
|
+
if (!generatedNames.has(name.toLowerCase())) merged[name] = configured[name];
|
|
35
|
+
}
|
|
36
|
+
return { ...merged, ...generated };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Set a client-generated header, removing any existing entry with the same
|
|
41
|
+
* case-insensitive name so the generated value is the only one sent.
|
|
42
|
+
*/
|
|
43
|
+
export function setGeneratedHeader(headers: Record<string, string>, name: string, value: string): void {
|
|
44
|
+
const lower = name.toLowerCase();
|
|
45
|
+
for (const existing in headers) {
|
|
46
|
+
if (existing.toLowerCase() === lower) delete headers[existing];
|
|
47
|
+
}
|
|
48
|
+
headers[name] = value;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const REDIRECT_STATUSES: Record<number, true> = { 301: true, 302: true, 303: true, 307: true, 308: true };
|
|
52
|
+
const MAX_REDIRECT_HOPS = 5;
|
|
53
|
+
|
|
54
|
+
export interface MCPFetchInit {
|
|
55
|
+
method: "GET" | "POST" | "DELETE";
|
|
56
|
+
body?: string;
|
|
57
|
+
signal?: AbortSignal;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Fetch an MCP endpoint with header precedence and, for origin-locked servers,
|
|
62
|
+
* manual redirect handling that strips configured headers on cross-origin hops.
|
|
63
|
+
*
|
|
64
|
+
* Non-locked servers keep the platform default redirect behavior. Locked
|
|
65
|
+
* non-GET requests only follow 307/308 (method-preserving); a 301/302/303
|
|
66
|
+
* redirect of a JSON-RPC POST is a connection error, never a silent GET.
|
|
67
|
+
*/
|
|
68
|
+
export async function mcpFetch(
|
|
69
|
+
url: string,
|
|
70
|
+
init: MCPFetchInit,
|
|
71
|
+
sources: MCPHeaderSources,
|
|
72
|
+
originLocked: boolean,
|
|
73
|
+
): Promise<Response> {
|
|
74
|
+
if (!originLocked) {
|
|
75
|
+
return fetch(url, { ...init, headers: mergeMCPHeaders(sources) });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const configuredOrigin = new URL(url).origin;
|
|
79
|
+
let currentUrl = url;
|
|
80
|
+
for (let hop = 0; hop <= MAX_REDIRECT_HOPS; hop++) {
|
|
81
|
+
const attachConfigured = new URL(currentUrl).origin === configuredOrigin;
|
|
82
|
+
const headers = mergeMCPHeaders(attachConfigured ? sources : { generated: sources.generated });
|
|
83
|
+
const response = await fetch(currentUrl, { ...init, headers, redirect: "manual" });
|
|
84
|
+
if (!REDIRECT_STATUSES[response.status]) return response;
|
|
85
|
+
|
|
86
|
+
const location = response.headers.get("Location");
|
|
87
|
+
if (!location) return response;
|
|
88
|
+
await response.body?.cancel();
|
|
89
|
+
if (init.method !== "GET" && response.status !== 307 && response.status !== 308) {
|
|
90
|
+
throw new Error(`HTTP ${response.status}: server redirected a ${init.method} request; refusing to follow`);
|
|
91
|
+
}
|
|
92
|
+
currentUrl = new URL(location, currentUrl).href;
|
|
93
|
+
}
|
|
94
|
+
throw new Error(`Too many redirects (> ${MAX_REDIRECT_HOPS}) fetching ${url}`);
|
|
95
|
+
}
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
import { toJsonRpcError } from "../../mcp/types";
|
|
20
20
|
import { RequestIdAllocator } from "../request-id";
|
|
21
21
|
import { createMCPTimeout, getNeverAbortSignal, isMCPTimeoutEnabled, resolveMCPTimeoutMs } from "../timeout";
|
|
22
|
+
import { type MCPFetchInit, mcpFetch } from "./header-policy";
|
|
22
23
|
|
|
23
24
|
const HTTP_SSE_CONNECT_TIMEOUT_MS = 1_000;
|
|
24
25
|
/**
|
|
@@ -54,6 +55,16 @@ export class HttpTransport implements MCPTransport {
|
|
|
54
55
|
|
|
55
56
|
constructor(private config: MCPHttpServerConfig | MCPSseServerConfig) {}
|
|
56
57
|
|
|
58
|
+
/** Fetch the configured endpoint with header precedence and origin policy. */
|
|
59
|
+
#fetch(init: MCPFetchInit, generated: Record<string, string>): Promise<Response> {
|
|
60
|
+
return mcpFetch(
|
|
61
|
+
this.config.url,
|
|
62
|
+
init,
|
|
63
|
+
{ generated, configured: this.config.headers },
|
|
64
|
+
this.config.headerPolicy === "origin-locked",
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
57
68
|
get connected(): boolean {
|
|
58
69
|
return this.#connected;
|
|
59
70
|
}
|
|
@@ -81,13 +92,12 @@ export class HttpTransport implements MCPTransport {
|
|
|
81
92
|
if (this.#sseConnection) return;
|
|
82
93
|
|
|
83
94
|
this.#sseConnection = new AbortController();
|
|
84
|
-
const
|
|
95
|
+
const generated: Record<string, string> = {
|
|
85
96
|
Accept: "text/event-stream",
|
|
86
|
-
...this.config.headers,
|
|
87
97
|
};
|
|
88
98
|
|
|
89
99
|
if (this.#sessionId) {
|
|
90
|
-
|
|
100
|
+
generated["Mcp-Session-Id"] = this.#sessionId;
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
let response: Response | null;
|
|
@@ -95,11 +105,7 @@ export class HttpTransport implements MCPTransport {
|
|
|
95
105
|
let startupFinished = false;
|
|
96
106
|
const connection = this.#sseConnection;
|
|
97
107
|
const startupTimeoutMs = resolveSSEConnectTimeoutMs(this.config.timeout);
|
|
98
|
-
const fetchPromise = fetch(
|
|
99
|
-
method: "GET",
|
|
100
|
-
headers,
|
|
101
|
-
signal: connection.signal,
|
|
102
|
-
});
|
|
108
|
+
const fetchPromise = this.#fetch({ method: "GET", signal: connection.signal }, generated);
|
|
103
109
|
const timeoutPromise =
|
|
104
110
|
startupTimeoutMs > 0
|
|
105
111
|
? new Promise<null>(resolve => {
|
|
@@ -219,26 +225,23 @@ export class HttpTransport implements MCPTransport {
|
|
|
219
225
|
params: params ?? {},
|
|
220
226
|
};
|
|
221
227
|
|
|
222
|
-
const
|
|
228
|
+
const generated: Record<string, string> = {
|
|
223
229
|
"Content-Type": "application/json",
|
|
224
230
|
Accept: "application/json, text/event-stream",
|
|
225
|
-
...this.config.headers,
|
|
226
231
|
};
|
|
227
232
|
|
|
228
233
|
if (this.#sessionId) {
|
|
229
|
-
|
|
234
|
+
generated["Mcp-Session-Id"] = this.#sessionId;
|
|
230
235
|
}
|
|
231
236
|
|
|
232
237
|
const timeout = resolveMCPTimeoutMs(this.config.timeout);
|
|
233
238
|
const operation = createMCPTimeout(timeout, options?.signal);
|
|
234
239
|
|
|
235
240
|
try {
|
|
236
|
-
const response = await fetch(
|
|
237
|
-
method: "POST",
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
signal: operation.signal,
|
|
241
|
-
});
|
|
241
|
+
const response = await this.#fetch(
|
|
242
|
+
{ method: "POST", body: JSON.stringify(body), signal: operation.signal },
|
|
243
|
+
generated,
|
|
244
|
+
);
|
|
242
245
|
|
|
243
246
|
// Check for session ID in response
|
|
244
247
|
const newSessionId = response.headers.get("Mcp-Session-Id");
|
|
@@ -365,23 +368,18 @@ export class HttpTransport implements MCPTransport {
|
|
|
365
368
|
const body = error
|
|
366
369
|
? { jsonrpc: "2.0" as const, id, error }
|
|
367
370
|
: { jsonrpc: "2.0" as const, id, result: result ?? {} };
|
|
368
|
-
const
|
|
371
|
+
const generated: Record<string, string> = {
|
|
369
372
|
"Content-Type": "application/json",
|
|
370
373
|
Accept: "application/json, text/event-stream",
|
|
371
|
-
...this.config.headers,
|
|
372
374
|
};
|
|
373
375
|
if (this.#sessionId) {
|
|
374
|
-
|
|
376
|
+
generated["Mcp-Session-Id"] = this.#sessionId;
|
|
375
377
|
}
|
|
378
|
+
const payload = JSON.stringify(body);
|
|
376
379
|
const timeout = resolveMCPTimeoutMs(this.config.timeout);
|
|
377
380
|
const operation = createMCPTimeout(timeout);
|
|
378
381
|
try {
|
|
379
|
-
const resp = await fetch(
|
|
380
|
-
method: "POST",
|
|
381
|
-
headers,
|
|
382
|
-
body: JSON.stringify(body),
|
|
383
|
-
signal: operation.signal,
|
|
384
|
-
});
|
|
382
|
+
const resp = await this.#fetch({ method: "POST", body: payload, signal: operation.signal }, generated);
|
|
385
383
|
// Retry once on auth failure if onAuthError is wired
|
|
386
384
|
if (this.onAuthError && (resp.status === 401 || resp.status === 403)) {
|
|
387
385
|
await resp.body?.cancel();
|
|
@@ -389,16 +387,13 @@ export class HttpTransport implements MCPTransport {
|
|
|
389
387
|
if (newHeaders) {
|
|
390
388
|
this.config.headers ??= {};
|
|
391
389
|
Object.assign(this.config.headers, newHeaders);
|
|
392
|
-
Object.assign(headers, newHeaders);
|
|
393
390
|
operation.clear();
|
|
394
391
|
const retryOperation = createMCPTimeout(timeout);
|
|
395
392
|
try {
|
|
396
|
-
const retry = await fetch(
|
|
397
|
-
method: "POST",
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
signal: retryOperation.signal,
|
|
401
|
-
});
|
|
393
|
+
const retry = await this.#fetch(
|
|
394
|
+
{ method: "POST", body: payload, signal: retryOperation.signal },
|
|
395
|
+
generated,
|
|
396
|
+
);
|
|
402
397
|
await retry.body?.cancel();
|
|
403
398
|
} finally {
|
|
404
399
|
retryOperation.clear();
|
|
@@ -425,26 +420,23 @@ export class HttpTransport implements MCPTransport {
|
|
|
425
420
|
params: params ?? {},
|
|
426
421
|
};
|
|
427
422
|
|
|
428
|
-
const
|
|
423
|
+
const generated: Record<string, string> = {
|
|
429
424
|
"Content-Type": "application/json",
|
|
430
425
|
Accept: "application/json, text/event-stream",
|
|
431
|
-
...this.config.headers,
|
|
432
426
|
};
|
|
433
427
|
|
|
434
428
|
if (this.#sessionId) {
|
|
435
|
-
|
|
429
|
+
generated["Mcp-Session-Id"] = this.#sessionId;
|
|
436
430
|
}
|
|
437
431
|
|
|
438
432
|
const timeout = resolveMCPTimeoutMs(this.config.timeout);
|
|
439
433
|
const operation = createMCPTimeout(timeout);
|
|
440
434
|
|
|
441
435
|
try {
|
|
442
|
-
const response = await fetch(
|
|
443
|
-
method: "POST",
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
signal: operation.signal,
|
|
447
|
-
});
|
|
436
|
+
const response = await this.#fetch(
|
|
437
|
+
{ method: "POST", body: JSON.stringify(body), signal: operation.signal },
|
|
438
|
+
generated,
|
|
439
|
+
);
|
|
448
440
|
|
|
449
441
|
// 202 Accepted is success for notifications
|
|
450
442
|
if (!response.ok && response.status !== 202) {
|
|
@@ -492,16 +484,7 @@ export class HttpTransport implements MCPTransport {
|
|
|
492
484
|
const timeout = resolveMCPTimeoutMs(this.config.timeout);
|
|
493
485
|
const operation = createMCPTimeout(timeout);
|
|
494
486
|
try {
|
|
495
|
-
|
|
496
|
-
...this.config.headers,
|
|
497
|
-
"Mcp-Session-Id": this.#sessionId,
|
|
498
|
-
};
|
|
499
|
-
|
|
500
|
-
await fetch(this.config.url, {
|
|
501
|
-
method: "DELETE",
|
|
502
|
-
headers,
|
|
503
|
-
signal: operation.signal,
|
|
504
|
-
});
|
|
487
|
+
await this.#fetch({ method: "DELETE", signal: operation.signal }, { "Mcp-Session-Id": this.#sessionId });
|
|
505
488
|
operation.clear();
|
|
506
489
|
} catch {
|
|
507
490
|
operation.clear();
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
import { toJsonRpcError } from "../../mcp/types";
|
|
13
13
|
import { RequestIdAllocator } from "../request-id";
|
|
14
14
|
import { createMCPTimeout, getNeverAbortSignal, resolveMCPTimeoutMs } from "../timeout";
|
|
15
|
+
import { type MCPFetchInit, mcpFetch } from "./header-policy";
|
|
15
16
|
|
|
16
17
|
interface MCPTimeoutOperation {
|
|
17
18
|
signal?: AbortSignal;
|
|
@@ -46,6 +47,16 @@ export class LegacySseTransport implements MCPTransport {
|
|
|
46
47
|
this.#config = config;
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
/** Fetch an endpoint with header precedence and origin policy. */
|
|
51
|
+
#fetch(url: string, init: MCPFetchInit, generated: Record<string, string>): Promise<Response> {
|
|
52
|
+
return mcpFetch(
|
|
53
|
+
url,
|
|
54
|
+
init,
|
|
55
|
+
{ generated, configured: this.#config.headers },
|
|
56
|
+
this.#config.headerPolicy === "origin-locked",
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
49
60
|
get connected(): boolean {
|
|
50
61
|
return this.#connected;
|
|
51
62
|
}
|
|
@@ -65,14 +76,11 @@ export class LegacySseTransport implements MCPTransport {
|
|
|
65
76
|
this.#sseConnection = connection;
|
|
66
77
|
|
|
67
78
|
try {
|
|
68
|
-
const response = await fetch(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
},
|
|
74
|
-
signal: operation.signal,
|
|
75
|
-
});
|
|
79
|
+
const response = await this.#fetch(
|
|
80
|
+
this.#config.url,
|
|
81
|
+
{ method: "GET", signal: operation.signal },
|
|
82
|
+
{ Accept: "text/event-stream" },
|
|
83
|
+
);
|
|
76
84
|
|
|
77
85
|
if (!response.ok) {
|
|
78
86
|
const text = await response.text();
|
|
@@ -286,17 +294,12 @@ export class LegacySseTransport implements MCPTransport {
|
|
|
286
294
|
): Promise<Response> {
|
|
287
295
|
const endpointUrl = this.#endpointUrl;
|
|
288
296
|
if (!endpointUrl) throw new Error("Transport not connected");
|
|
289
|
-
|
|
297
|
+
const generated: Record<string, string> = {
|
|
290
298
|
"Content-Type": "application/json",
|
|
291
299
|
Accept: "application/json, text/event-stream",
|
|
292
|
-
...this.#config.headers,
|
|
293
300
|
};
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
headers,
|
|
297
|
-
body: JSON.stringify(body),
|
|
298
|
-
signal,
|
|
299
|
-
});
|
|
301
|
+
const payload = JSON.stringify(body);
|
|
302
|
+
let response = await this.#fetch(endpointUrl, { method: "POST", body: payload, signal }, generated);
|
|
300
303
|
const status = AIError.status(response);
|
|
301
304
|
if (!this.onAuthError || (status !== 401 && status !== 403)) return response;
|
|
302
305
|
|
|
@@ -304,17 +307,7 @@ export class LegacySseTransport implements MCPTransport {
|
|
|
304
307
|
if (!refreshedHeaders) return response;
|
|
305
308
|
await response.body?.cancel();
|
|
306
309
|
this.#config.headers = refreshedHeaders;
|
|
307
|
-
|
|
308
|
-
"Content-Type": "application/json",
|
|
309
|
-
Accept: "application/json, text/event-stream",
|
|
310
|
-
...this.#config.headers,
|
|
311
|
-
};
|
|
312
|
-
response = await fetch(endpointUrl, {
|
|
313
|
-
method: "POST",
|
|
314
|
-
headers,
|
|
315
|
-
body: JSON.stringify(body),
|
|
316
|
-
signal,
|
|
317
|
-
});
|
|
310
|
+
response = await this.#fetch(endpointUrl, { method: "POST", body: payload, signal }, generated);
|
|
318
311
|
return response;
|
|
319
312
|
}
|
|
320
313
|
|
package/src/mcp/types.ts
CHANGED
|
@@ -100,6 +100,13 @@ export interface MCPStdioServerConfig extends MCPServerConfigBase {
|
|
|
100
100
|
command: string;
|
|
101
101
|
args?: string[];
|
|
102
102
|
env?: Record<string, string>;
|
|
103
|
+
/**
|
|
104
|
+
* `literal`: env values are opaque plugin package data (Agent Plugins
|
|
105
|
+
* §§4.1/9.2) — no env-name lookup, no `!command` execution, no dropping of
|
|
106
|
+
* empty values. The provider already applied the only permitted expansion
|
|
107
|
+
* (`${PLUGIN_ROOT}`/`${PLUGIN_DATA}`).
|
|
108
|
+
*/
|
|
109
|
+
envPolicy?: "literal";
|
|
103
110
|
cwd?: string;
|
|
104
111
|
}
|
|
105
112
|
|
|
@@ -108,6 +115,12 @@ export interface MCPHttpServerConfig extends MCPServerConfigBase {
|
|
|
108
115
|
type: "http";
|
|
109
116
|
url: string;
|
|
110
117
|
headers?: Record<string, string>;
|
|
118
|
+
/**
|
|
119
|
+
* `origin-locked`: configured headers are literal package data pinned to the
|
|
120
|
+
* configured URL's origin (Agent Plugins §7.2.1) — never expanded, never
|
|
121
|
+
* forwarded cross-origin, and client-generated headers win case-insensitively.
|
|
122
|
+
*/
|
|
123
|
+
headerPolicy?: "origin-locked";
|
|
111
124
|
}
|
|
112
125
|
|
|
113
126
|
/** SSE server configuration (deprecated, use HTTP) */
|
|
@@ -115,6 +128,8 @@ export interface MCPSseServerConfig extends MCPServerConfigBase {
|
|
|
115
128
|
type: "sse";
|
|
116
129
|
url: string;
|
|
117
130
|
headers?: Record<string, string>;
|
|
131
|
+
/** See {@link MCPHttpServerConfig.headerPolicy}. */
|
|
132
|
+
headerPolicy?: "origin-locked";
|
|
118
133
|
}
|
|
119
134
|
|
|
120
135
|
export type MCPServerConfig = MCPStdioServerConfig | MCPHttpServerConfig | MCPSseServerConfig;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
4
|
+
import type { AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
|
5
|
+
import { getBlobsDir, isEnoent, logger, type postmortem, VERSION } from "@oh-my-pi/pi-utils";
|
|
3
6
|
import {
|
|
4
7
|
type Agent,
|
|
5
8
|
type AgentSideConnection,
|
|
@@ -39,10 +42,7 @@ import {
|
|
|
39
42
|
type SetSessionModeRequest,
|
|
40
43
|
type SetSessionModeResponse,
|
|
41
44
|
type Usage,
|
|
42
|
-
} from "@
|
|
43
|
-
import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
44
|
-
import type { AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
|
45
|
-
import { getBlobsDir, isEnoent, logger, type postmortem, VERSION } from "@oh-my-pi/pi-utils";
|
|
45
|
+
} from "@oh-my-pi/pi-utils/acp";
|
|
46
46
|
import { disableProvider, enableProvider, reset as resetCapabilities } from "../../capability";
|
|
47
47
|
import { Settings } from "../../config/settings";
|
|
48
48
|
import { clearPluginRootsAndCaches, resolveActiveProjectRegistryPath } from "../../discovery/helpers";
|
|
@@ -70,6 +70,7 @@ import { SessionManager } from "../../session/session-manager";
|
|
|
70
70
|
import { executeAcpBuiltinSlashCommand } from "../../slash-commands/acp-builtins";
|
|
71
71
|
import { buildAvailableSlashCommands, toAcpAvailableCommands } from "../../slash-commands/available-commands";
|
|
72
72
|
import { DEFAULT_STT_MODEL_KEY, STT_MODEL_OPTIONS } from "../../stt/models";
|
|
73
|
+
import { refreshAgentDiscovery } from "../../task";
|
|
73
74
|
import { AUTO_THINKING, parseConfiguredThinkingLevel } from "../../thinking";
|
|
74
75
|
import { normalizeLocalScheme } from "../../tools/path-utils";
|
|
75
76
|
import { ToolError } from "../../tools/tool-errors";
|
|
@@ -1930,14 +1931,15 @@ export class AcpAgent implements Agent {
|
|
|
1930
1931
|
/**
|
|
1931
1932
|
* Reload plugin/registry state for an ACP session. Mirrors the interactive
|
|
1932
1933
|
* `/reload-plugins` and `/move` flows: invalidates the plugin-roots cache,
|
|
1933
|
-
* resets the capability cache, refreshes the
|
|
1934
|
-
* then re-advertises commands so the client
|
|
1935
|
-
* plugins.
|
|
1934
|
+
* refreshes task agents, resets the capability cache, refreshes the
|
|
1935
|
+
* session's slash-command state, then re-advertises commands so the client
|
|
1936
|
+
* sees newly installed/disabled plugins.
|
|
1936
1937
|
*/
|
|
1937
1938
|
async #reloadPluginState(record: ManagedSessionRecord): Promise<void> {
|
|
1938
1939
|
const cwd = record.session.sessionManager.getCwd();
|
|
1939
1940
|
const projectPath = await resolveActiveProjectRegistryPath(cwd);
|
|
1940
1941
|
clearPluginRootsAndCaches(projectPath ? [projectPath] : undefined);
|
|
1942
|
+
await refreshAgentDiscovery(cwd);
|
|
1941
1943
|
resetCapabilities();
|
|
1942
1944
|
await record.session.refreshSkills();
|
|
1943
1945
|
const fileCommands = await loadSlashCommands({ cwd });
|
|
@@ -2019,7 +2021,16 @@ export class AcpAgent implements Agent {
|
|
|
2019
2021
|
|
|
2020
2022
|
async #findStoredSession(sessionId: string, cwd: string): Promise<StoredSessionInfo | undefined> {
|
|
2021
2023
|
const sessions = await this.#listStoredSessions(cwd);
|
|
2022
|
-
|
|
2024
|
+
const scoped = sessions.find(session => session.id === sessionId);
|
|
2025
|
+
if (scoped) {
|
|
2026
|
+
return scoped;
|
|
2027
|
+
}
|
|
2028
|
+
// The cwd-derived directory only covers sessions stored under the current
|
|
2029
|
+
// naming scheme. Sessions written under a legacy/hashed project directory
|
|
2030
|
+
// (the 17.2.5+ scheme reverted in #7656) live elsewhere, so fall back to a
|
|
2031
|
+
// global by-id scan: the session id is globally unique, and
|
|
2032
|
+
// #openStoredSession reopens the file with the request cwd. See #7779.
|
|
2033
|
+
return this.#findStoredSessionById(sessionId);
|
|
2023
2034
|
}
|
|
2024
2035
|
|
|
2025
2036
|
async #findStoredSessionById(sessionId: string): Promise<StoredSessionInfo | undefined> {
|
|
@@ -2348,7 +2359,7 @@ export class AcpAgent implements Agent {
|
|
|
2348
2359
|
record.session.sessionManager.appendLabelChange(targetId, label);
|
|
2349
2360
|
},
|
|
2350
2361
|
getActiveTools: () => record.session.getEnabledToolNames(),
|
|
2351
|
-
getAllTools: () => record.session.
|
|
2362
|
+
getAllTools: () => record.session.getAllToolInfos(),
|
|
2352
2363
|
setActiveTools: toolNames => record.session.setActiveToolsByName(toolNames),
|
|
2353
2364
|
getCommands: () => getSessionSlashCommands(record.session),
|
|
2354
2365
|
setModel: async model => {
|
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
ToolCallContent,
|
|
6
6
|
ToolCallLocation,
|
|
7
7
|
ToolKind,
|
|
8
|
-
} from "@
|
|
8
|
+
} from "@oh-my-pi/pi-utils/acp";
|
|
9
9
|
import { parseXdUrl } from "../../internal-urls/xd-protocol";
|
|
10
10
|
import type { AgentSessionEvent } from "../../session/agent-session";
|
|
11
11
|
import { resolveToCwd } from "../../tools/path-utils";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as stream from "node:stream";
|
|
2
|
-
import { AgentSideConnection, ndJsonStream, type Stream } from "@agentclientprotocol/sdk";
|
|
3
2
|
import { postmortem } from "@oh-my-pi/pi-utils";
|
|
3
|
+
import { AgentSideConnection, ndJsonStream, type Stream } from "@oh-my-pi/pi-utils/acp";
|
|
4
4
|
import type { AgentSession } from "../../session/agent-session";
|
|
5
5
|
import { AcpAgent } from "./acp-agent";
|
|
6
6
|
|
|
@@ -48,6 +48,7 @@ import { Settings } from "../../config/settings";
|
|
|
48
48
|
import agentCreationArchitectPrompt from "../../prompts/system/agent-creation-architect.md" with { type: "text" };
|
|
49
49
|
import agentCreationUserPrompt from "../../prompts/system/agent-creation-user.md" with { type: "text" };
|
|
50
50
|
import { createAgentSession } from "../../sdk";
|
|
51
|
+
import { refreshAgentDiscovery } from "../../task";
|
|
51
52
|
import { discoverAgents } from "../../task/discovery";
|
|
52
53
|
import { resolveAgentPrewalkDefault } from "../../task/prewalk";
|
|
53
54
|
import type { AgentDefinition, AgentSource } from "../../task/types";
|
|
@@ -824,6 +825,7 @@ export class AgentDashboard extends Container {
|
|
|
824
825
|
).trimEnd();
|
|
825
826
|
const content = `---\n${frontmatter}\n---\n\n${spec.systemPrompt.trim()}\n`;
|
|
826
827
|
await Bun.write(filePath, content);
|
|
828
|
+
await refreshAgentDiscovery(this.cwd);
|
|
827
829
|
await this.#reloadData();
|
|
828
830
|
this.#clearCreateFlow();
|
|
829
831
|
this.#notice = `Created agent ${spec.identifier} at ${shortenPath(filePath)}`;
|