@herbertgao/pi-extensions 2026.9.11 → 2026.9.13
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 +4 -4
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +1 -1
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +1 -1
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +5 -4
- package/node_modules/@herbertgao/pi-subagents/src/mention-clone.ts +39 -16
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@narumitw/pi-btw/README.md +21 -4
- package/node_modules/@narumitw/pi-btw/dist/index.ts +1668 -958
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +4 -4
- package/node_modules/@narumitw/pi-btw/docs/workflows.md +10 -2
- package/node_modules/@narumitw/pi-btw/package.json +1 -1
- package/node_modules/@narumitw/pi-btw/src/btw.ts +17 -79
- package/node_modules/@narumitw/pi-btw/src/conversation-context.ts +74 -0
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +196 -7
- package/node_modules/@narumitw/pi-btw/src/main-thread-updates.ts +40 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -2
- package/node_modules/@narumitw/pi-btw/src/settings.ts +49 -0
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +9 -1
- package/node_modules/@narumitw/pi-btw/src/workspace-layout.ts +559 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +62 -0
- package/node_modules/pi-mcp-adapter/README.md +75 -7
- package/node_modules/pi-mcp-adapter/bearer-command-resolver.ts +193 -0
- package/node_modules/pi-mcp-adapter/cli.js +56 -5
- package/node_modules/pi-mcp-adapter/commands.ts +96 -5
- package/node_modules/pi-mcp-adapter/config.ts +73 -6
- package/node_modules/pi-mcp-adapter/direct-tool-surface.ts +22 -28
- package/node_modules/pi-mcp-adapter/direct-tools.ts +13 -5
- package/node_modules/pi-mcp-adapter/dist/abort.d.ts +2 -0
- package/node_modules/pi-mcp-adapter/dist/abort.js +36 -0
- package/node_modules/pi-mcp-adapter/dist/abort.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/bearer-command-resolver.d.ts +6 -0
- package/node_modules/pi-mcp-adapter/dist/bearer-command-resolver.js +194 -0
- package/node_modules/pi-mcp-adapter/dist/bearer-command-resolver.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/config.d.ts +5 -0
- package/node_modules/pi-mcp-adapter/dist/config.js +75 -6
- package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/consent-manager.d.ts +18 -0
- package/node_modules/pi-mcp-adapter/dist/consent-manager.js +88 -0
- package/node_modules/pi-mcp-adapter/dist/consent-manager.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/elicitation-handler.d.ts +17 -0
- package/node_modules/pi-mcp-adapter/dist/elicitation-handler.js +316 -0
- package/node_modules/pi-mcp-adapter/dist/elicitation-handler.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/errors.d.ts +131 -0
- package/node_modules/pi-mcp-adapter/dist/errors.js +278 -0
- package/node_modules/pi-mcp-adapter/dist/errors.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/http-ca.d.ts +8 -0
- package/node_modules/pi-mcp-adapter/dist/http-ca.js +88 -0
- package/node_modules/pi-mcp-adapter/dist/http-ca.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/jev-client.d.ts +15 -0
- package/node_modules/pi-mcp-adapter/dist/jev-client.js +340 -0
- package/node_modules/pi-mcp-adapter/dist/jev-client.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/jev-contracts.d.ts +77 -0
- package/node_modules/pi-mcp-adapter/dist/jev-contracts.js +2 -0
- package/node_modules/pi-mcp-adapter/dist/jev-contracts.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/jev-key-store.d.ts +22 -0
- package/node_modules/pi-mcp-adapter/dist/jev-key-store.js +85 -0
- package/node_modules/pi-mcp-adapter/dist/jev-key-store.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/json-schema-validator.d.ts +2 -0
- package/node_modules/pi-mcp-adapter/dist/json-schema-validator.js +55 -0
- package/node_modules/pi-mcp-adapter/dist/json-schema-validator.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/lifecycle.d.ts +56 -0
- package/node_modules/pi-mcp-adapter/dist/lifecycle.js +410 -0
- package/node_modules/pi-mcp-adapter/dist/lifecycle.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/logger.d.ts +51 -0
- package/node_modules/pi-mcp-adapter/dist/logger.js +131 -0
- package/node_modules/pi-mcp-adapter/dist/logger.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-fetch.d.ts +26 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-fetch.js +103 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-fetch.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-flow.d.ts +118 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-flow.js +1098 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-flow.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.d.ts +168 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.js +1117 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js +7 -140
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-callback-server.d.ts +53 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-callback-server.js +441 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-callback-server.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-oauth-provider.d.ts +148 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-oauth-provider.js +689 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-oauth-provider.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-probe.d.ts +6 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-probe.js +166 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-probe.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-tasks.d.ts +102 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-tasks.js +369 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-tasks.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-trace.d.ts +95 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-trace.js +242 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-trace.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.js +2 -11
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/npx-resolver.d.ts +6 -0
- package/node_modules/pi-mcp-adapter/dist/npx-resolver.js +505 -0
- package/node_modules/pi-mcp-adapter/dist/npx-resolver.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/request-headers-command.d.ts +10 -0
- package/node_modules/pi-mcp-adapter/dist/request-headers-command.js +312 -0
- package/node_modules/pi-mcp-adapter/dist/request-headers-command.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/runtime-owner.d.ts +13 -0
- package/node_modules/pi-mcp-adapter/dist/runtime-owner.js +97 -0
- package/node_modules/pi-mcp-adapter/dist/runtime-owner.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/sampling-handler.d.ts +17 -0
- package/node_modules/pi-mcp-adapter/dist/sampling-handler.js +203 -0
- package/node_modules/pi-mcp-adapter/dist/sampling-handler.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/secure-keyring.d.ts +11 -0
- package/node_modules/pi-mcp-adapter/dist/secure-keyring.js +172 -0
- package/node_modules/pi-mcp-adapter/dist/secure-keyring.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/server-manager.d.ts +157 -0
- package/node_modules/pi-mcp-adapter/dist/server-manager.js +1743 -0
- package/node_modules/pi-mcp-adapter/dist/server-manager.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/session-approvals.d.ts +34 -0
- package/node_modules/pi-mcp-adapter/dist/session-approvals.js +140 -0
- package/node_modules/pi-mcp-adapter/dist/session-approvals.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/session-recovery.d.ts +26 -0
- package/node_modules/pi-mcp-adapter/dist/session-recovery.js +142 -0
- package/node_modules/pi-mcp-adapter/dist/session-recovery.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/state.d.ts +73 -0
- package/node_modules/pi-mcp-adapter/dist/state.js +2 -0
- package/node_modules/pi-mcp-adapter/dist/state.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/types.d.ts +36 -0
- package/node_modules/pi-mcp-adapter/dist/types.js +18 -0
- package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/ui-resource-handler.d.ts +17 -0
- package/node_modules/pi-mcp-adapter/dist/ui-resource-handler.js +219 -0
- package/node_modules/pi-mcp-adapter/dist/ui-resource-handler.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/unix-socket-transport.d.ts +15 -0
- package/node_modules/pi-mcp-adapter/dist/unix-socket-transport.js +85 -0
- package/node_modules/pi-mcp-adapter/dist/unix-socket-transport.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/utils.d.ts +1 -0
- package/node_modules/pi-mcp-adapter/dist/utils.js +13 -0
- package/node_modules/pi-mcp-adapter/dist/utils.js.map +1 -1
- package/node_modules/pi-mcp-adapter/elicitation-handler.ts +29 -19
- package/node_modules/pi-mcp-adapter/examples/jev-accessibility-loop.mjs +85 -0
- package/node_modules/pi-mcp-adapter/examples/jev-semantic-filter.mjs +34 -0
- package/node_modules/pi-mcp-adapter/index.ts +129 -6
- package/node_modules/pi-mcp-adapter/init.ts +6 -19
- package/node_modules/pi-mcp-adapter/jev-client.ts +273 -0
- package/node_modules/pi-mcp-adapter/jev-contracts.ts +53 -0
- package/node_modules/pi-mcp-adapter/jev-key-store.ts +79 -0
- package/node_modules/pi-mcp-adapter/lifecycle.ts +14 -3
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +7 -142
- package/node_modules/pi-mcp-adapter/mcp-code.ts +119 -11
- package/node_modules/pi-mcp-adapter/mcp-output-guard.ts +4 -0
- package/node_modules/pi-mcp-adapter/mcp-references.ts +5 -3
- package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +5 -0
- package/node_modules/pi-mcp-adapter/mcp-tasks.ts +467 -0
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +2 -13
- package/node_modules/pi-mcp-adapter/namespace-tools.ts +1 -1
- package/node_modules/pi-mcp-adapter/package.json +17 -9
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +319 -166
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +6 -3
- package/node_modules/pi-mcp-adapter/search-ranking.ts +38 -7
- package/node_modules/pi-mcp-adapter/secure-keyring.ts +170 -0
- package/node_modules/pi-mcp-adapter/semantic-search.ts +186 -0
- package/node_modules/pi-mcp-adapter/server-manager.ts +293 -50
- package/node_modules/pi-mcp-adapter/session-approvals.ts +14 -0
- package/node_modules/pi-mcp-adapter/skills/mcp-scripting/SKILL.md +2 -0
- package/node_modules/pi-mcp-adapter/state.ts +3 -1
- package/node_modules/pi-mcp-adapter/tool-approval.ts +26 -4
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +6 -14
- package/node_modules/pi-mcp-adapter/tool-registrar.ts +8 -6
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +4 -1
- package/node_modules/pi-mcp-adapter/types.ts +53 -0
- package/node_modules/pi-mcp-adapter/ui-server.ts +17 -9
- package/node_modules/pi-mcp-adapter/utils.ts +16 -0
- package/node_modules/pi-multi-account/CHANGELOG.md +23 -0
- package/node_modules/pi-multi-account/README.md +38 -11
- package/node_modules/pi-multi-account/index.ts +371 -96
- package/node_modules/pi-multi-account/package.json +5 -4
- package/node_modules/pi-multi-account/provider-payload-stream.ts +36 -28
- package/node_modules/pi-multi-account/usage.ts +31 -2
- package/node_modules/pi-typesafe/README.md +3 -1
- package/node_modules/pi-typesafe/dist/auth.d.ts +10 -3
- package/node_modules/pi-typesafe/dist/auth.js +13 -7
- package/node_modules/pi-typesafe/dist/backends.d.ts +31 -0
- package/node_modules/pi-typesafe/dist/backends.js +33 -0
- package/node_modules/pi-typesafe/dist/client.d.ts +3 -9
- package/node_modules/pi-typesafe/dist/client.js +63 -39
- package/node_modules/pi-typesafe/dist/credentials.d.ts +11 -5
- package/node_modules/pi-typesafe/dist/credentials.js +15 -8
- package/node_modules/pi-typesafe/dist/extension.js +4 -1
- package/node_modules/pi-typesafe/dist/index.d.ts +1 -1
- package/node_modules/pi-typesafe/dist/index.js +1 -1
- package/node_modules/pi-typesafe/dist/login.d.ts +13 -7
- package/node_modules/pi-typesafe/dist/login.js +17 -8
- package/node_modules/pi-typesafe/dist/schema.js +19 -13
- package/node_modules/pi-typesafe/package.json +1 -1
- package/package.json +11 -10
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getToolUiResourceUri } from "./ui-app-bridge-helpers.ts";
|
|
2
2
|
import type { McpExtensionState } from "./state.ts";
|
|
3
3
|
import type { ToolMetadata, McpTool, McpResource, ServerEntry, ToolPrefix } from "./types.ts";
|
|
4
|
-
import { createToolSelectorCandidateIndex, formatToolName, getToolNameCandidates, isToolAllowed, resolveToolPrefix } from "./types.ts";
|
|
4
|
+
import { createToolSelectorCandidateIndex, formatToolName, getToolNameCandidates, isToolAllowed, resolveToolPrefix, resolveUniqueNameOwnership } from "./types.ts";
|
|
5
5
|
import { resourceNameToToolName } from "./resource-tools.ts";
|
|
6
6
|
import { extractToolUiStreamMode } from "./utils.ts";
|
|
7
7
|
import { extractUiToolVisibility, isUiToolVisibleToModel } from "./ui-tool-visibility.ts";
|
|
@@ -18,7 +18,6 @@ export function buildToolMetadata(
|
|
|
18
18
|
): { metadata: ToolMetadata[]; failedTools: string[] } {
|
|
19
19
|
const metadata: ToolMetadata[] = [];
|
|
20
20
|
const failedTools: string[] = [];
|
|
21
|
-
const seenNames = new Set<string>();
|
|
22
21
|
const effectivePrefix = resolveToolPrefix(definition, prefix);
|
|
23
22
|
const hasToolFilters =
|
|
24
23
|
(Array.isArray(definition.includeTools) && definition.includeTools.length > 0) ||
|
|
@@ -86,16 +85,10 @@ export function buildToolMetadata(
|
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
const name = formatToolName(tool.name, serverName, effectivePrefix);
|
|
89
|
-
if (seenNames.has(name)) {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
88
|
const uiVisibility = extractUiToolVisibility(tool._meta);
|
|
94
89
|
if (!isUiToolVisibleToModel(uiVisibility)) {
|
|
95
90
|
continue;
|
|
96
91
|
}
|
|
97
|
-
seenNames.add(name);
|
|
98
|
-
|
|
99
92
|
let uiResourceUri: string | undefined;
|
|
100
93
|
try {
|
|
101
94
|
uiResourceUri = getToolUiResourceUri({ _meta: tool._meta });
|
|
@@ -123,11 +116,6 @@ export function buildToolMetadata(
|
|
|
123
116
|
}
|
|
124
117
|
|
|
125
118
|
const name = formatToolName(baseName, serverName, effectivePrefix);
|
|
126
|
-
if (seenNames.has(name)) {
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
seenNames.add(name);
|
|
130
|
-
|
|
131
119
|
metadata.push({
|
|
132
120
|
name,
|
|
133
121
|
originalName: baseName,
|
|
@@ -137,7 +125,11 @@ export function buildToolMetadata(
|
|
|
137
125
|
}
|
|
138
126
|
}
|
|
139
127
|
|
|
140
|
-
|
|
128
|
+
const ownership = resolveUniqueNameOwnership(metadata, (tool) => tool.name);
|
|
129
|
+
for (const colliding of ownership.collisions.values()) {
|
|
130
|
+
failedTools.push(...colliding.map((tool) => tool.originalName));
|
|
131
|
+
}
|
|
132
|
+
return { metadata: ownership.unique, failedTools };
|
|
141
133
|
}
|
|
142
134
|
|
|
143
135
|
export function getToolNames(state: McpExtensionState, serverName: string): string[] {
|
|
@@ -230,18 +230,20 @@ export function transformMcpContent(content: McpContent[], scope?: object): Cont
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
/**
|
|
233
|
-
* Resolve a tool result's content blocks,
|
|
234
|
-
* when content is empty.
|
|
233
|
+
* Resolve a tool result's content blocks, appending structuredContent when it
|
|
234
|
+
* coexists with ordinary content and falling back to it when content is empty.
|
|
235
235
|
*/
|
|
236
236
|
export function resolveMcpResultContent(result: Record<string, unknown>, scope?: object): ContentBlock[] {
|
|
237
237
|
const blocks = transformMcpContent((Array.isArray(result.content) ? result.content : []) as McpContent[], scope);
|
|
238
|
-
if (blocks.length > 0) return blocks;
|
|
239
|
-
|
|
240
238
|
if (result.structuredContent !== undefined && result.structuredContent !== null) {
|
|
241
|
-
|
|
239
|
+
const structured = stringifyStructuredContent(result.structuredContent);
|
|
240
|
+
if (blocks.length > 0) {
|
|
241
|
+
return [...blocks, { type: "text" as const, text: `structuredContent:\n${structured}` }];
|
|
242
|
+
}
|
|
243
|
+
return [{ type: "text" as const, text: structured }];
|
|
242
244
|
}
|
|
243
245
|
|
|
244
|
-
return
|
|
246
|
+
return blocks;
|
|
245
247
|
}
|
|
246
248
|
|
|
247
249
|
function stringifyStructuredContent(value: unknown): string {
|
|
@@ -225,7 +225,10 @@ export function formatMcpProxyToolCallLines(
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
if (args.connect) return [`mcp connect ${args.connect}`];
|
|
228
|
-
if (args.describe)
|
|
228
|
+
if (args.describe) {
|
|
229
|
+
const target = args.server ? `${args.describe} @ ${args.server}` : args.describe;
|
|
230
|
+
return [`mcp describe ${target}`];
|
|
231
|
+
}
|
|
229
232
|
|
|
230
233
|
if (args.search) {
|
|
231
234
|
let line = `mcp search ${args.search}`;
|
|
@@ -498,6 +498,15 @@ export interface ServerEntry {
|
|
|
498
498
|
* with no fallback. `auto` and `2026-07-28` must be set explicitly.
|
|
499
499
|
*/
|
|
500
500
|
protocolVersion?: "legacy" | "auto" | "2026-07-28";
|
|
501
|
+
/**
|
|
502
|
+
* MCP Tasks extension (io.modelcontextprotocol/tasks, SEP-2663) support.
|
|
503
|
+
* On 2026-07-28 connections where the server advertises the extension, tool
|
|
504
|
+
* calls that return a task handle are transparently polled to completion,
|
|
505
|
+
* task-time elicitation is routed through the normal elicitation UI, and
|
|
506
|
+
* aborting a call cancels the remote task. Enabled by default; set to
|
|
507
|
+
* false to keep the plain synchronous call path.
|
|
508
|
+
*/
|
|
509
|
+
tasks?: boolean;
|
|
501
510
|
// Keep configuration visible without allowing connections or execution.
|
|
502
511
|
disabled?: boolean;
|
|
503
512
|
}
|
|
@@ -576,6 +585,8 @@ export interface McpToolApprovalRequest {
|
|
|
576
585
|
claim(handler: McpToolApprovalHandler): boolean;
|
|
577
586
|
}
|
|
578
587
|
|
|
588
|
+
export type { JevAnswer, JevErrorCode, JevEvaluateInput, JevEvaluationData, JevEvaluationEnvelope, JevJson, JevQuestion } from "./jev-contracts.ts";
|
|
589
|
+
|
|
579
590
|
export interface McpSettings {
|
|
580
591
|
toolPrefix?: ToolPrefix;
|
|
581
592
|
/** Show the plug prefix in MCP status and connection text (default: true). Set to false to disable it. */
|
|
@@ -593,6 +604,8 @@ export interface McpSettings {
|
|
|
593
604
|
idleTimeout?: number; // minutes, default 10, 0 to disable
|
|
594
605
|
requestTimeoutMs?: number; // milliseconds, overrides the SDK request timeout when > 0
|
|
595
606
|
directTools?: boolean | "search";
|
|
607
|
+
/** Register per-server mcp__<server> namespace proxies. Defaults to true. */
|
|
608
|
+
namespaceProxyTools?: boolean;
|
|
596
609
|
/**
|
|
597
610
|
* Validate direct-tool inputs against the advertised schema after recovering
|
|
598
611
|
* one JSON string layer for object and array properties. Defaults to false.
|
|
@@ -607,6 +620,25 @@ export interface McpSettings {
|
|
|
607
620
|
warnOnLargeDirectTools?: boolean;
|
|
608
621
|
/** Register the trusted MCP-only JavaScript scripting tool. Defaults to true; set false to hide it. */
|
|
609
622
|
scriptMode?: boolean;
|
|
623
|
+
/** Optional TypeSafe Jev integrations. A valid key enables semantic search; script evaluation remains disabled by default. */
|
|
624
|
+
jev?: false | {
|
|
625
|
+
semanticSearch?: boolean;
|
|
626
|
+
scriptEvaluation?: boolean;
|
|
627
|
+
/** Restrict semantic-search metadata and allow script-evaluation sources. Semantic search defaults to every enabled server. */
|
|
628
|
+
allowedServers?: string[];
|
|
629
|
+
model?: string;
|
|
630
|
+
requestTimeoutMs?: number;
|
|
631
|
+
maxRetries?: number;
|
|
632
|
+
maxStateBytes?: number;
|
|
633
|
+
maxQuestionsPerRequest?: number;
|
|
634
|
+
maxEvaluationsPerScript?: number;
|
|
635
|
+
maxEvaluationBytesPerScript?: number;
|
|
636
|
+
/** Cumulative provider-reported input plus output tokens per script. Defaults to 32768. */
|
|
637
|
+
maxEvaluationTokensPerScript?: number;
|
|
638
|
+
/** Maximum semantic candidates per request. Defaults to 127; range 2..127. */
|
|
639
|
+
semanticCandidateLimit?: number;
|
|
640
|
+
semanticMinProbability?: number;
|
|
641
|
+
};
|
|
610
642
|
/** Render MCP tool results as compact self-rendered rows by default, or as the legacy boxed row. */
|
|
611
643
|
toolResultRendering?: "compact" | "boxed";
|
|
612
644
|
/** Number of result text lines to show before expansion. Supports 1, 2, or 3. Defaults to 1 in compact mode and 3 in boxed mode. */
|
|
@@ -815,6 +847,9 @@ export function formatToolName(
|
|
|
815
847
|
): string {
|
|
816
848
|
const p = getServerPrefix(serverName, prefix);
|
|
817
849
|
const sanitized = toolName.replace(/\./g, "_");
|
|
850
|
+
if (p && sanitized.startsWith(`${p}_`) && sanitized.length > p.length + 1) {
|
|
851
|
+
return sanitized;
|
|
852
|
+
}
|
|
818
853
|
return p ? `${p}_${sanitized}` : sanitized;
|
|
819
854
|
}
|
|
820
855
|
|
|
@@ -825,6 +860,24 @@ export function resolveToolPrefix(
|
|
|
825
860
|
return definition?.toolPrefix ?? globalPrefix ?? "server";
|
|
826
861
|
}
|
|
827
862
|
|
|
863
|
+
/** A canonical name has an owner only when exactly one eligible entry produces it. */
|
|
864
|
+
export function resolveUniqueNameOwnership<T>(
|
|
865
|
+
entries: readonly T[],
|
|
866
|
+
getName: (entry: T) => string,
|
|
867
|
+
): { unique: T[]; collisions: Map<string, T[]> } {
|
|
868
|
+
const owners = new Map<string, T[]>();
|
|
869
|
+
for (const entry of entries) {
|
|
870
|
+
const name = getName(entry);
|
|
871
|
+
const named = owners.get(name) ?? [];
|
|
872
|
+
named.push(entry);
|
|
873
|
+
owners.set(name, named);
|
|
874
|
+
}
|
|
875
|
+
const collisions = new Map([...owners].filter(([, named]) => named.length > 1));
|
|
876
|
+
return {
|
|
877
|
+
unique: entries.filter((entry) => !collisions.has(getName(entry))),
|
|
878
|
+
collisions,
|
|
879
|
+
};
|
|
880
|
+
}
|
|
828
881
|
|
|
829
882
|
/**
|
|
830
883
|
* Resolve a configured MCP server name from a prefixed tool name.
|
|
@@ -19,10 +19,11 @@ import {
|
|
|
19
19
|
SANDBOX_RESOURCE_PATH_PREFIX,
|
|
20
20
|
} from "./sandbox-proxy-template.ts";
|
|
21
21
|
import { logger } from "./logger.ts";
|
|
22
|
-
import type { McpServerManager } from "./server-manager.ts";
|
|
22
|
+
import type { McpServerManager, ServerConnection } from "./server-manager.ts";
|
|
23
23
|
import type { McpExtensionState } from "./state.ts";
|
|
24
24
|
import { SessionRecoveryAuthRequiredError, withSessionRecovery, type SessionRecoveryDeps } from "./session-recovery.ts";
|
|
25
25
|
import { ensureToolCallApproved, isToolCallApprovalRequired } from "./tool-approval.ts";
|
|
26
|
+
import { callToolViaTaskSession } from "./mcp-tasks.ts";
|
|
26
27
|
import { extractUiToolVisibility, isUiToolCallableByApp, isUiToolVisibleToModel } from "./ui-tool-visibility.ts";
|
|
27
28
|
import { resourceNameToToolName } from "./resource-tools.ts";
|
|
28
29
|
import {
|
|
@@ -530,6 +531,19 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
530
531
|
try {
|
|
531
532
|
options.manager.touch(options.serverName);
|
|
532
533
|
options.manager.incrementInFlight(options.serverName);
|
|
534
|
+
const callTool = async (conn: ServerConnection) => {
|
|
535
|
+
await options.manager.ensureListen?.(options.serverName, conn);
|
|
536
|
+
const requestOptions = options.manager.getRequestOptions?.(options.serverName);
|
|
537
|
+
if (conn.taskSession) {
|
|
538
|
+
return callToolViaTaskSession(conn.taskSession, {
|
|
539
|
+
name: callArgs.name,
|
|
540
|
+
args: callArgs.arguments,
|
|
541
|
+
requestTimeoutMs: requestOptions?.timeout,
|
|
542
|
+
signal: requestOptions?.signal,
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
return conn.client.callTool(callArgs, requestOptions);
|
|
546
|
+
};
|
|
533
547
|
const result = options.config
|
|
534
548
|
? await withSessionRecovery(
|
|
535
549
|
{
|
|
@@ -538,15 +552,9 @@ export async function startUiServer(options: UiServerOptions): Promise<UiServerH
|
|
|
538
552
|
...(options.onNeedsAuth ? { onNeedsAuth: options.onNeedsAuth } : {}),
|
|
539
553
|
},
|
|
540
554
|
options.serverName,
|
|
541
|
-
|
|
542
|
-
await options.manager.ensureListen?.(options.serverName, conn);
|
|
543
|
-
return conn.client.callTool(callArgs, options.manager.getRequestOptions?.(options.serverName));
|
|
544
|
-
},
|
|
555
|
+
callTool,
|
|
545
556
|
)
|
|
546
|
-
: await (
|
|
547
|
-
await options.manager.ensureListen?.(options.serverName, connection);
|
|
548
|
-
return connection.client.callTool(callArgs, options.manager.getRequestOptions?.(options.serverName));
|
|
549
|
-
})();
|
|
557
|
+
: await callTool(connection);
|
|
550
558
|
sendJson(res, 200, { ok: true, result });
|
|
551
559
|
} finally {
|
|
552
560
|
options.manager.decrementInFlight(options.serverName);
|
|
@@ -426,6 +426,22 @@ export function formatMcpStatus(config: Pick<McpConfig, "settings">, message: st
|
|
|
426
426
|
return `${config.settings?.showStatusIcon === false ? "MCP: " : "🔌 MCP: "}${message}`;
|
|
427
427
|
}
|
|
428
428
|
|
|
429
|
+
export function formatMcpFooterStatus(
|
|
430
|
+
config: Pick<McpConfig, "settings">,
|
|
431
|
+
enabledCount: number,
|
|
432
|
+
disabledCount: number,
|
|
433
|
+
connectedCount: number,
|
|
434
|
+
): string | undefined {
|
|
435
|
+
if (enabledCount + disabledCount === 0 || config.settings?.mcpFooterStatus === "off") return undefined;
|
|
436
|
+
const footerStatus = config.settings?.mcpFooterStatus ?? "full";
|
|
437
|
+
if (footerStatus === "compact") return `MCP ${connectedCount}/${enabledCount}`;
|
|
438
|
+
|
|
439
|
+
let status = `${enabledCount} ${enabledCount === 1 ? "server" : "servers"} enabled`;
|
|
440
|
+
if (connectedCount > 0) status += ` (${connectedCount} connected)`;
|
|
441
|
+
if (disabledCount > 0) status += ` (${disabledCount} disabled)`;
|
|
442
|
+
return formatMcpStatus(config, status);
|
|
443
|
+
}
|
|
444
|
+
|
|
429
445
|
/**
|
|
430
446
|
* Extract the adapter-owned UI stream mode from tool metadata.
|
|
431
447
|
*/
|
|
@@ -5,6 +5,29 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.23.0] — 2026-09-23
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `/multi-account pick` offers only the current account's models without filtering the shared registry or changing Pi core. `/multi-account save-default` saves the current model and effective thinking level together for new sessions, without making automatic failover overwrite startup preferences.
|
|
13
|
+
- Added `resumeAfterAllAccountsRecover` (default `true`) as a separate live-session liveness control. A real quota/rate-limit wall across every compatible account remains armed even when immediate `autoContinue` is disabled, polls recovery independently of footer visibility, and resumes on the first account that is genuinely usable.
|
|
14
|
+
|
|
15
|
+
### Compatibility
|
|
16
|
+
|
|
17
|
+
- Pi and pi-ai are peer dependencies in the tested range >=0.85.1, <0.88.0. Transport imports bind to the running host rather than an extension-local 0.85 copy. CI covers Pi 0.85.1, 0.86.1 and 0.87.1; standalone SDK callers must use their installed pi-ai context format.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Native provider wrappers preserve system instructions and tool definitions on Pi's transcript-based context, avoiding tool-free Anthropic requests and old token-estimator crashes (#65, #66).
|
|
22
|
+
- Partial Codex usage headers preserve account/plan display metadata for the same credential without refreshing old serviceability, credits or missing quota windows. Includes a routing regression for stale `serviceable: true` plus fresh 100% quota (adapted from #67, thanks @HerbertGao).
|
|
23
|
+
- Kimi OAuth and unauthenticated spare slots remain in the login picker but are no longer published as unusable static aliases. Only authenticated API-key aliases are published; existing user models.json entries remain untouched (#68).
|
|
24
|
+
- Updated the Anthropic billing-header Claude Code version to 2.1.280 (#64).
|
|
25
|
+
- A continuation that exhausted its newly selected fallback no longer stops after one hop: the next quota-driven switch receives its own continuation.
|
|
26
|
+
- Asynchronous rejection of Pi's injected follow-up no longer loses the interrupted task; the selected fallback remains armed for a bounded retry.
|
|
27
|
+
- `neverFailoverProviders` now also bypasses foreground startup/input preflights for unmanaged providers. Stale cooldowns, invalidations, or unknown auth no longer silently replace an opted-out route before its request. Automatic switch and pending-resume boundaries enforce the same exemption; explicit manual switches remain available.
|
|
28
|
+
- A bodyless `429 status code (no body)` from an unmanaged provider now retries the same route instead of being treated as provider-wide credit exhaustion. The retry honors `Retry-After` when available and otherwise uses the transient delay, avoiding both an unrelated-provider failover and a false six-hour bench for providers such as Cerebras.
|
|
29
|
+
- Numbered Codex OAuth slots no longer fall back to the public ChatGPT endpoint while the child proxy is enabled. A numbered alias is always registered against the loopback route that swaps in the real credential, from extension load onward, so the child-facing placeholder can never be sent to a provider that cannot read it (`Could not parse your authentication token`). Registration now requires an explicit route, so a future call site cannot silently reintroduce the public fallback.
|
|
30
|
+
|
|
8
31
|
## [1.22.0] — 2026-09-17
|
|
9
32
|
|
|
10
33
|
### Compatibility
|
|
@@ -11,9 +11,9 @@ When the account you are using hits a quota or rate limit, `pi-multi-account` tr
|
|
|
11
11
|
- **Auto-discovers new Codex models per account.** At session start (and on `reload` / `rediscover`) it reads OpenAI's authenticated model catalog, mirrors each account's actually available models onto its Pi alias, and follows OpenAI's server priority. A new flagship can therefore win immediately without an extension release or a hard-coded model id.
|
|
12
12
|
- **Handles auth failures without poisoning healthy OAuth accounts.** A generic final 401 briefly cools down a refreshable account and moves the current task forward. Explicit provider verdicts such as `authentication token has been invalidated` force an early refresh; if the refresh token is dead too, the slot is removed and Pi prints the interactive `/login` recovery steps.
|
|
13
13
|
- **Fails over on quota / rate-limit** (429 / 402 / 403 and friends): the exhausted account goes on cooldown (parsed from the provider's own reset metadata when available) and Pi first tries another account with the same model. If it must leave the family, it preserves the model's quality band — Sol/Opus/other frontier flagships stay frontier; Terra/Sonnet stay balanced; Luna/Haiku stay fast — and keeps the session's thinking level. A fresh provider verdict of `blocked` or 100% is skipped automatically instead of wasting the turn; manual `next` remains an explicit one-attempt override for stale telemetry.
|
|
14
|
-
- **Optional auto-continue**: resumes the interrupted turn after a switch from the last safe point. Temporary 5xx/overload errors and Cursor stalls retry the exact selected provider/account/model, without marking its quota exhausted. After four failed attempts (or an earlier recovery breaker), automatic retries stop with an explicit explanation; temporary errors never authorize a switch. Same-route retries are labelled as retries, not fake switches. A failed or cancelled automatic compaction still continues the task instead of leaving the session parked in Working.
|
|
14
|
+
- **Optional immediate auto-continue**: resumes the interrupted turn after a switch from the last safe point. A continuation that exhausts its newly selected account keeps advancing instead of stopping after the first hop. Temporary 5xx/overload errors and Cursor stalls retry the exact selected provider/account/model, without marking its quota exhausted. After four failed attempts (or an earlier recovery breaker), automatic retries stop with an explicit explanation; temporary errors never authorize a switch. Same-route retries are labelled as retries, not fake switches. A rejected Pi follow-up remains armed instead of losing the task. A failed or cancelled automatic compaction still continues the task instead of leaving the session parked in Working.
|
|
15
15
|
- **Never hides a fresh user message in a private cooldown queue.** If no account is ready, the message remains in Pi's visible transcript and Pi owns its normal delivery/retry path.
|
|
16
|
-
- **Session-bound overnight resume**: if every account is cooling down, the live Pi session
|
|
16
|
+
- **Session-bound overnight resume, independently configurable**: if every compatible account is cooling down, the live Pi session keeps the task armed by default even when ordinary post-switch `autoContinue` is off. It polls every account (quota monitoring remains active when the footer is hidden), reacts to fresh provider recovery evidence, and continues on the first genuinely usable route. A new user message, `/multi-account stop`, session exit, or Esc during a running turn cancels the chain.
|
|
17
17
|
- **Deduplicates provably identical accounts** so duplicate Codex workspace memberships and identical credentials do not consume multiple rotation slots or get separate cooldowns. Different users in one Team/Business workspace remain distinct. New provable duplicate logins are rejected before the redundant slot is saved.
|
|
18
18
|
- **Keeps YOUR reasoning level across automatic switches.** Whatever the session runs at — your Pi default, `/thinking`, or a per-agent `--thinking low` — is preserved and restored after every account/model switch, so it never drifts downward when a weaker fallback model clamps it. Manual `/model` selections adopt Pi's per-model thinking default in `auto` mode; an explicit CLI `--thinking` or forced `reasoningLevel` remains authoritative. The extension does not otherwise override your level, and extreme levels such as `xhigh` / Max / Ultra are never forced.
|
|
19
19
|
- **Shows live limits for the active account** in Pi's footer: remaining 5-hour/session and weekly allowance plus reset countdowns for Codex, Anthropic, and Ollama Cloud accounts, SuperGrok / xAI subscription credit usage for `/login xai` OAuth (not Cursor Grok, not `XAI_API_KEY`), and GLM Coding Plan CN 5-hour/weekly credit quota for `zai-coding-cn`. GLM CN uses the raw Coding Plan API key only at `https://open.bigmodel.cn/api/monitor/usage/quota/limit`; global `zai` keys are not sent there. Missing or malformed quota is unknown, never assumed unused.
|
|
@@ -26,11 +26,17 @@ pi install npm:pi-multi-account
|
|
|
26
26
|
|
|
27
27
|
Restart Pi or run `/reload` after installation.
|
|
28
28
|
|
|
29
|
-
Requires Node 22+ and
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
Requires Node 22+ and Agent Pi / pi-ai **>=0.85.1, <0.88.0** (CI covers 0.85.1, 0.86.1 and 0.87.1).
|
|
30
|
+
Pi core packages are peers, not a private pinned transport dependency. Provider wrappers use Pi's
|
|
31
|
+
host-bound imports, so the request adapter and host context format stay together; a stale nested
|
|
32
|
+
pi-ai cannot silently remove tools or system instructions. Standalone SDK callers must pair their
|
|
33
|
+
context with the installed pi-ai version. The OAuth/catalog bridge remains a separate, best-effort
|
|
34
|
+
compatibility layer: unavailable subscription login does not prevent API-key account discovery.
|
|
35
|
+
|
|
36
|
+
Kimi OAuth spares remain available in `/login`, but are not written to `models.json`: without a
|
|
37
|
+
Kimi child OAuth proxy those aliases would be resolvable but unauthenticated. Only real API-key
|
|
38
|
+
Kimi slots are published for extension-free children. Existing user entries are not deleted;
|
|
39
|
+
a previously generated unused Kimi alias can be removed from `models.json` and will not reappear.
|
|
34
40
|
|
|
35
41
|
> **Anthropic (Claude Pro/Max) works out of the box.** OAuth login and request
|
|
36
42
|
> shaping for the base `anthropic` provider and every `anthropic-account-*` alias
|
|
@@ -98,7 +104,9 @@ All three names are aliases for the same command: `/multi-account`, `/provider-f
|
|
|
98
104
|
| `add [anthropic\|codex\|kimi\|cursor\|ollama\|qwen]` | Print the next free account slot to select from the interactive `/login` picker. Subscription families (Anthropic, Codex, Kimi, Cursor) are logged in through `/login`; API-key families are filled in `auth.json`. |
|
|
99
105
|
| `remove [anthropic\|codex\|kimi\|cursor\|ollama\|qwen\|<provider-id>]` | Remove an account from `auth.json` and rotation. Family name drops the highest numbered alias slot; a full provider id removes that exact slot. Aliases: `rm`, `delete`. |
|
|
100
106
|
| `next` | Manually switch to the next compatible-quality fallback, deliberately overriding recorded cooldowns for one attempt. Use explicit `switch` to select a different tier. |
|
|
101
|
-
| `
|
|
107
|
+
| `pick` | Open a model picker for the current account only. Uses native model selection and its thinking default; never removes models from the shared registry. Requires an idle interactive session. Built-in `/model` remains unchanged. |
|
|
108
|
+
| `save-default` | Save the current model and effective thinking level together as global startup defaults for new sessions. Preserves other models' thinking preferences. Project overrides, explicit CLI options, and resumed-session settings still take precedence. Automatic rotation never invokes this. |
|
|
109
|
+
| `only-active [on\|off]` | Legacy picker preference; use `pick` for a current-account-only menu. The complete Pi model registry remains available to all clients. Alias: `focus`. |
|
|
102
110
|
| `stop` | Abort and cancel automatic failover/resume for the current task. |
|
|
103
111
|
| `reset` | Clear all cooldowns, invalidations and any pending auto-resume. |
|
|
104
112
|
| `reload` | Reload config from disk and re-discover accounts. |
|
|
@@ -125,12 +133,13 @@ A default config is created at `~/.pi/agent/provider-failover.json` on first run
|
|
|
125
133
|
| Key | Default | Description |
|
|
126
134
|
|---|---|---|
|
|
127
135
|
| `enabled` | `true` | Master switch. |
|
|
128
|
-
| `autoContinue` | `true` |
|
|
136
|
+
| `autoContinue` | `true` | Immediately continue after a successful account/model switch or same-route temporary retry. This does not control the all-accounts-limited wait. |
|
|
137
|
+
| `resumeAfterAllAccountsRecover` | `true` | When a real quota/rate limit leaves every compatible account cooling, keep the interrupted task armed in the current live session and resume on the first account that becomes genuinely usable. Independent of `autoContinue`; set `false` to opt out. |
|
|
129
138
|
| `autoDiscover` | `true` | Auto-discover accounts from `auth.json`. |
|
|
130
139
|
| `autoDiscoverModels` | `true` | Fetch OpenAI's authenticated model catalog for every Codex account and register new models on that account's alias automatically. |
|
|
131
140
|
| `includeQwen` | `true` | Include Qwen / Alibaba accounts. |
|
|
132
141
|
| `includeOllama` | `true` | Include Ollama (local) accounts. |
|
|
133
|
-
| `neverFailoverProviders` | `[]` | Provider ids to never fail away from, e.g. `["my-provider"]`. For **unmanaged** providers that run their own retry logic (typically a companion extension owning retries for that provider) — switching accounts underneath it would fight those retries. Managed accounts still cool and rotate normally. |
|
|
142
|
+
| `neverFailoverProviders` | `[]` | Provider ids to never automatically fail away from, e.g. `["my-provider"]`. Foreground startup/input preflights, pending retries, and compaction leave these routes to Pi even with stale cooldowns. Manual switches remain allowed. For **unmanaged** providers that run their own retry logic (typically a companion extension owning retries for that provider) — switching accounts underneath it would fight those retries. Managed accounts still cool and rotate normally. |
|
|
134
143
|
| `includeCursor` | `true` | Include Cursor subscription accounts. The Cursor provider is a separate, optional repo — until it is cloned this setting does nothing at all: no cursor login slot is offered and no warning is printed. Run `/multi-account add cursor` to get the install instructions. |
|
|
135
144
|
| `childProxy` | `true` | Serve OAuth rotation slots to processes that do not load this extension (a memory extension consolidating its notes, an external CLI, any `pi -p --no-extensions` call) through a loopback route this process owns. A bare child launched without `--model` inherits Pi's **saved global default**, which since Pi 0.84.3 intentionally may differ from this session's live rotation; an explicitly pinned child uses its requested slot. Without a usable publication either form can fail authentication and fall back to whichever provider Pi finds first. The published route carries a non-secret placeholder; the real credential never leaves the parent. |
|
|
136
145
|
| `providerOrder` | `["anthropic","openai-codex","qwen","ollama"]` | Preferred family order in the rotation. |
|
|
@@ -159,7 +168,7 @@ State (cooldowns, invalidations, recent switches, credential-free Codex model ca
|
|
|
159
168
|
|
|
160
169
|
### Session model ownership
|
|
161
170
|
|
|
162
|
-
Requires Agent Pi
|
|
171
|
+
Requires the supported Agent Pi versions listed above. Pi's session branch and explicit SDK/CLI launch model are authoritative. Shared account telemetry and legacy `lastUserModel` / `lastUserThinkingLevel` state cannot replace another pane's live selection, and shutdown does not publish that selection as a global default. Cold-catalog repair uses this session's model history once at startup. Configure Pi's saved default explicitly for new sessions.
|
|
163
172
|
|
|
164
173
|
### Host-owned background completions
|
|
165
174
|
|
|
@@ -187,6 +196,24 @@ A failover is only useful if the agent actually keeps working afterward. These g
|
|
|
187
196
|
|
|
188
197
|
`pi-multi-account` reads credentials through Pi and its account store. Account removal, OAuth refresh and parent-owned proxy publication can update the credential files under Pi-compatible locks. Proxy publication keeps a private recovery copy before replacing a credential with a loopback placeholder, and restores the real credential before deleting that copy. Credentials are never stored in rotation state. Account/token values are reduced to a short irreversible SHA-256 fingerprint for re-login detection and deduplication. Credentials are sent only to their own provider endpoints: usage/account probes (`chatgpt.com/backend-api/wham/usage`, `api.anthropic.com/api/oauth/usage`, `cli-chat-proxy.grok.com/v1/billing`, `open.bigmodel.cn/api/monitor/usage/quota/limit` (CN Coding Plan keys only), Ollama Cloud's `/api/me` and `/api/usage`, or Ollama's loopback-only `http://127.0.0.1:11434/api/me` fallback), OpenAI's authenticated `chatgpt.com/backend-api/codex/models` catalog, and provider OAuth token endpoints when Pi's authentication implementation refreshes a login (for xAI, `auth.x.ai/oauth2/token`). Cached state contains percentages, reset times, plan/credit metadata, model metadata, provider-reported account email/alias, and the fingerprint, never the token. Config, state, and the debug log are written with `0600` permissions. The debug log records only provider/model ids, decisions, and truncated reasons — token-shaped material is redacted defensively — Review logs for private project details before sharing an issue. Disable it with `"debugLog": false` or `/multi-account log off`.
|
|
189
198
|
|
|
199
|
+
## Compatibility validation
|
|
200
|
+
|
|
201
|
+
`npm run release:check` runs TypeScript, the full suite, and package/privacy checks. CI also runs
|
|
202
|
+
the suite on the supported newer Pi versions. Host-binding tests load the real extension wrapper
|
|
203
|
+
through Pi with a deliberately incompatible nested pi-ai, inspect native request bodies, and
|
|
204
|
+
complete a streamed tool-call/result cycle without real credentials or provider traffic.
|
|
205
|
+
|
|
206
|
+
The optional companion Goal integration is selected explicitly; it never assumes a personal path:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
PI_GOAL_TEST_ENTRY=/path/to/pi-goal/dist/index.ts node --test test/goal-quota-recovery.integration.test.ts
|
|
210
|
+
PI_GOAL_TEST_ENTRY=/path/to/pi-goal/dist/index.ts PI_GOAL_EXHAUST_ALL=1 node --test test/goal-quota-recovery.integration.test.ts
|
|
211
|
+
PI_GOAL_TEST_ENTRY=/path/to/pi-goal/dist/index.ts PI_GOAL_EXHAUST_ALL=1 PI_GOAL_RECOVER_ALL=1 node --test test/goal-quota-recovery.integration.test.ts
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
These exercise successful multi-hop continuation, quiet all-account waiting with explicit pause,
|
|
215
|
+
and resumption after quota recovery. The test is explicitly skipped when no companion path is supplied.
|
|
216
|
+
|
|
190
217
|
## License
|
|
191
218
|
|
|
192
219
|
[MIT](./LICENSE)
|