@oh-my-pi/pi-coding-agent 17.1.5 → 17.1.7
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 +54 -0
- package/dist/{CHANGELOG-qs3vd6xf.md → CHANGELOG-5k4dq4g6.md} +54 -0
- package/dist/cli.js +3342 -3274
- package/dist/types/capability/index.d.ts +1 -1
- package/dist/types/capability/types.d.ts +23 -1
- package/dist/types/config/settings-schema.d.ts +39 -2
- package/dist/types/cursor.d.ts +2 -1
- package/dist/types/extensibility/extensions/runner.d.ts +4 -0
- package/dist/types/extensibility/shared-events.d.ts +18 -1
- package/dist/types/internal-urls/mcp-protocol.d.ts +3 -2
- package/dist/types/internal-urls/parse.d.ts +12 -0
- package/dist/types/internal-urls/router.d.ts +6 -0
- package/dist/types/internal-urls/types.d.ts +6 -0
- package/dist/types/lsp/config.d.ts +1 -0
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/mcp/manager.d.ts +5 -0
- package/dist/types/mcp/tool-bridge.d.ts +13 -0
- package/dist/types/modes/components/custom-editor.d.ts +5 -0
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
- package/dist/types/modes/rpc/rpc-mode.d.ts +2 -0
- package/dist/types/sdk.d.ts +3 -1
- package/dist/types/session/agent-session-types.d.ts +8 -6
- package/dist/types/session/agent-session.d.ts +28 -1
- package/dist/types/session/model-controls.d.ts +4 -1
- package/dist/types/session/session-advisors.d.ts +7 -1
- package/dist/types/session/session-tools.d.ts +44 -6
- package/dist/types/session/streaming-output.d.ts +7 -2
- package/dist/types/session/tool-choice-queue.d.ts +6 -4
- package/dist/types/session/turn-recovery.d.ts +5 -3
- package/dist/types/task/index.d.ts +1 -1
- package/dist/types/task/types.d.ts +3 -11
- package/dist/types/thinking.d.ts +21 -2
- package/dist/types/tools/index.d.ts +8 -3
- package/dist/types/tools/output-meta.d.ts +5 -0
- package/dist/types/tools/read.d.ts +9 -1
- package/dist/types/tools/xdev.d.ts +53 -67
- package/dist/types/utils/cpuprofile.d.ts +51 -0
- package/dist/types/utils/inspect-image-mode.d.ts +29 -0
- package/dist/types/utils/profile-tree.d.ts +47 -0
- package/dist/types/utils/sample-profile.d.ts +67 -0
- package/dist/types/utils/title-generator.d.ts +17 -16
- package/package.json +12 -12
- package/src/capability/index.ts +43 -12
- package/src/capability/mcp.ts +21 -0
- package/src/capability/types.ts +20 -1
- package/src/cli/read-cli.ts +44 -2
- package/src/config/settings-schema.ts +42 -2
- package/src/config/settings.ts +35 -0
- package/src/cursor.ts +4 -3
- package/src/discovery/builtin-rules/index.ts +2 -0
- package/src/discovery/builtin-rules/ts-no-local-is-record.md +48 -0
- package/src/eval/py/runner.py +16 -2
- package/src/extensibility/extensions/runner.ts +117 -5
- package/src/extensibility/extensions/types.ts +0 -1
- package/src/extensibility/extensions/wrapper.ts +74 -42
- package/src/extensibility/hooks/tool-wrapper.ts +11 -4
- package/src/extensibility/hooks/types.ts +0 -1
- package/src/extensibility/shared-events.ts +18 -1
- package/src/internal-urls/mcp-protocol.ts +17 -3
- package/src/internal-urls/parse.ts +31 -0
- package/src/internal-urls/router.ts +24 -4
- package/src/internal-urls/types.ts +6 -0
- package/src/live/transport.ts +2 -2
- package/src/lsp/client.ts +2 -2
- package/src/lsp/config.ts +4 -0
- package/src/lsp/types.ts +2 -0
- package/src/mcp/config.ts +26 -14
- package/src/mcp/manager.ts +26 -9
- package/src/mcp/tool-bridge.ts +52 -1
- package/src/memories/index.ts +25 -6
- package/src/modes/components/custom-editor.ts +39 -16
- package/src/modes/components/status-line/segments.ts +3 -1
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/tool-execution.ts +8 -7
- package/src/modes/controllers/command-controller.ts +10 -10
- package/src/modes/controllers/extension-ui-controller.ts +7 -7
- package/src/modes/controllers/selector-controller.ts +7 -2
- package/src/modes/rpc/rpc-mode.ts +60 -47
- package/src/prompts/steering/user-interjection.md +2 -5
- package/src/prompts/tools/task.md +4 -2
- package/src/sdk.ts +64 -58
- package/src/session/agent-session-types.ts +8 -5
- package/src/session/agent-session.ts +144 -11
- package/src/session/model-controls.ts +48 -12
- package/src/session/session-advisors.ts +30 -14
- package/src/session/session-listing.ts +66 -4
- package/src/session/session-tools.ts +162 -54
- package/src/session/streaming-output.ts +18 -6
- package/src/session/tool-choice-queue.ts +19 -4
- package/src/session/turn-recovery.ts +25 -6
- package/src/slash-commands/builtin-registry.ts +69 -0
- package/src/task/executor.ts +11 -3
- package/src/task/index.ts +43 -32
- package/src/task/types.ts +12 -17
- package/src/thinking.ts +68 -5
- package/src/tools/bash.ts +16 -9
- package/src/tools/index.ts +36 -16
- package/src/tools/output-meta.ts +20 -0
- package/src/tools/read.ts +89 -15
- package/src/tools/write.ts +16 -7
- package/src/tools/xdev.ts +198 -210
- package/src/utils/cpuprofile.ts +235 -0
- package/src/utils/inspect-image-mode.ts +39 -0
- package/src/utils/profile-tree.ts +111 -0
- package/src/utils/sample-profile.ts +437 -0
- package/src/utils/title-generator.ts +88 -34
- package/dist/types/advisor/__tests__/advisor.test.d.ts +0 -1
- package/dist/types/advisor/__tests__/config.test.d.ts +0 -1
- package/dist/types/advisor/__tests__/emission-guard.test.d.ts +0 -1
- package/dist/types/cli/__tests__/auth-gateway-catalog.test.d.ts +0 -1
- package/dist/types/cli/update-cli.test.d.ts +0 -1
- package/dist/types/config/__tests__/model-registry.test.d.ts +0 -1
- package/dist/types/eval/__tests__/agent-bridge.test.d.ts +0 -1
- package/dist/types/eval/__tests__/bridge-timeout.test.d.ts +0 -1
- package/dist/types/eval/__tests__/budget-bridge.test.d.ts +0 -1
- package/dist/types/eval/__tests__/completion-bridge.test.d.ts +0 -1
- package/dist/types/eval/__tests__/helpers-local-roots.test.d.ts +0 -1
- package/dist/types/eval/__tests__/idle-timeout.test.d.ts +0 -1
- package/dist/types/eval/__tests__/js-context-manager.test.d.ts +0 -1
- package/dist/types/eval/__tests__/julia-prelude.test.d.ts +0 -1
- package/dist/types/eval/__tests__/kernel-spawn.test.d.ts +0 -1
- package/dist/types/eval/__tests__/prelude-agent.test.d.ts +0 -1
- package/dist/types/eval/__tests__/process-entry-import.test.d.ts +0 -1
- package/dist/types/eval/py/__tests__/prelude.test.d.ts +0 -1
- package/dist/types/eval/py/__tests__/runner-shell-output.test.d.ts +0 -1
- package/dist/types/hindsight/client.test.d.ts +0 -1
- package/dist/types/internal-urls/__tests__/agent-protocol-nested.test.d.ts +0 -1
- package/dist/types/internal-urls/__tests__/ssh-protocol.test.d.ts +0 -1
- package/dist/types/launch/broker-list-order.test.d.ts +0 -1
- package/dist/types/launch/broker-output-snapshot.test.d.ts +0 -1
- package/dist/types/launch/protocol.test.d.ts +0 -1
- package/dist/types/launch/spawn-options.test.d.ts +0 -1
- package/dist/types/launch/terminal-output.test.d.ts +0 -1
- package/dist/types/live/protocol.test.d.ts +0 -1
- package/dist/types/mcp/config-writer.test.d.ts +0 -1
- package/dist/types/mcp/smithery-auth.test.d.ts +0 -1
- package/dist/types/mcp/smithery-registry.test.d.ts +0 -1
- package/dist/types/mcp/transports/stdio.test.d.ts +0 -1
- package/dist/types/modes/components/__tests__/dynamic-border.test.d.ts +0 -1
- package/dist/types/modes/components/__tests__/move-overlay.test.d.ts +0 -1
- package/dist/types/modes/components/__tests__/pause-screen.test.d.ts +0 -1
- package/dist/types/modes/components/__tests__/skill-message.test.d.ts +0 -1
- package/dist/types/modes/components/custom-editor-plugin-ctor.test.d.ts +0 -1
- package/dist/types/modes/components/custom-editor.test.d.ts +0 -1
- package/dist/types/modes/components/login-dialog.test.d.ts +0 -1
- package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +0 -1
- package/dist/types/modes/components/status-line/component.test.d.ts +0 -1
- package/dist/types/modes/components/tool-execution.test.d.ts +0 -1
- package/dist/types/modes/controllers/extension-ui-controller.test.d.ts +0 -1
- package/dist/types/modes/noninteractive-dispose.test.d.ts +0 -1
- package/dist/types/modes/print-mode.test.d.ts +0 -1
- package/dist/types/modes/session-teardown.test.d.ts +0 -1
- package/dist/types/modes/theme/mermaid-rendering.test.d.ts +0 -1
- package/dist/types/modes/utils/transcript-render-helpers.test.d.ts +0 -1
- package/dist/types/modes/warp-events.test.d.ts +0 -1
- package/dist/types/plan-mode/approved-plan-prompt.test.d.ts +0 -1
- package/dist/types/plan-mode/model-transition.test.d.ts +0 -1
- package/dist/types/plan-mode/reentry-prompt.test.d.ts +0 -1
- package/dist/types/session/agent-session-error-log.test.d.ts +0 -1
- package/dist/types/session/blob-store.test.d.ts +0 -1
- package/dist/types/session/messages.test.d.ts +0 -1
- package/dist/types/session/session-context.test.d.ts +0 -1
- package/dist/types/ssh/__tests__/connection-manager-args.test.d.ts +0 -1
- package/dist/types/ssh/__tests__/connection-manager-timeout.test.d.ts +0 -1
- package/dist/types/ssh/__tests__/file-transfer-posix-guard.test.d.ts +0 -1
- package/dist/types/ssh/__tests__/sshfs-mount.test.d.ts +0 -1
- package/dist/types/system-prompt.test.d.ts +0 -1
- package/dist/types/task/render.test.d.ts +0 -1
- package/dist/types/task/spawn-policy.test.d.ts +0 -1
- package/dist/types/tools/__tests__/eval-description.test.d.ts +0 -1
- package/dist/types/tools/__tests__/glob.test.d.ts +0 -1
- package/dist/types/tools/__tests__/json-tree.test.d.ts +0 -1
- package/dist/types/tools/__tests__/vibe-render.test.d.ts +0 -1
- package/dist/types/tools/hub/launch-compat.test.d.ts +0 -1
- package/dist/types/vibe/__tests__/token-rate.test.d.ts +0 -1
- package/src/advisor/__tests__/advisor.test.ts +0 -4889
- package/src/advisor/__tests__/config.test.ts +0 -349
- package/src/advisor/__tests__/emission-guard.test.ts +0 -147
- package/src/cli/__tests__/auth-gateway-catalog.test.ts +0 -111
- package/src/cli/update-cli.test.ts +0 -28
- package/src/config/__tests__/model-registry.test.ts +0 -182
- package/src/eval/__tests__/agent-bridge.test.ts +0 -1509
- package/src/eval/__tests__/bridge-timeout.test.ts +0 -170
- package/src/eval/__tests__/budget-bridge.test.ts +0 -80
- package/src/eval/__tests__/completion-bridge.test.ts +0 -412
- package/src/eval/__tests__/helpers-local-roots.test.ts +0 -55
- package/src/eval/__tests__/idle-timeout.test.ts +0 -80
- package/src/eval/__tests__/js-context-manager.test.ts +0 -456
- package/src/eval/__tests__/julia-prelude.test.ts +0 -66
- package/src/eval/__tests__/kernel-spawn.test.ts +0 -115
- package/src/eval/__tests__/prelude-agent.test.ts +0 -156
- package/src/eval/__tests__/process-entry-import.test.ts +0 -137
- package/src/eval/py/__tests__/prelude.test.ts +0 -104
- package/src/eval/py/__tests__/runner-shell-output.test.ts +0 -157
- package/src/hindsight/client.test.ts +0 -75
- package/src/internal-urls/__tests__/agent-protocol-nested.test.ts +0 -141
- package/src/internal-urls/__tests__/ssh-protocol.test.ts +0 -331
- package/src/launch/broker-list-order.test.ts +0 -89
- package/src/launch/broker-output-snapshot.test.ts +0 -126
- package/src/launch/protocol.test.ts +0 -59
- package/src/launch/spawn-options.test.ts +0 -31
- package/src/launch/terminal-output.test.ts +0 -107
- package/src/live/protocol.test.ts +0 -140
- package/src/mcp/config-writer.test.ts +0 -43
- package/src/mcp/smithery-auth.test.ts +0 -29
- package/src/mcp/smithery-registry.test.ts +0 -51
- package/src/mcp/transports/stdio.test.ts +0 -427
- package/src/modes/components/__tests__/dynamic-border.test.ts +0 -55
- package/src/modes/components/__tests__/move-overlay.test.ts +0 -252
- package/src/modes/components/__tests__/pause-screen.test.ts +0 -143
- package/src/modes/components/__tests__/skill-message.test.ts +0 -94
- package/src/modes/components/custom-editor-plugin-ctor.test.ts +0 -36
- package/src/modes/components/custom-editor.test.ts +0 -510
- package/src/modes/components/login-dialog.test.ts +0 -56
- package/src/modes/components/status-line/component.jj-cache.test.ts +0 -229
- package/src/modes/components/status-line/component.test.ts +0 -84
- package/src/modes/components/tool-execution.test.ts +0 -162
- package/src/modes/controllers/extension-ui-controller.test.ts +0 -250
- package/src/modes/noninteractive-dispose.test.ts +0 -73
- package/src/modes/print-mode.test.ts +0 -71
- package/src/modes/session-teardown.test.ts +0 -219
- package/src/modes/theme/mermaid-rendering.test.ts +0 -53
- package/src/modes/utils/transcript-render-helpers.test.ts +0 -38
- package/src/modes/warp-events.test.ts +0 -794
- package/src/plan-mode/approved-plan-prompt.test.ts +0 -36
- package/src/plan-mode/model-transition.test.ts +0 -60
- package/src/plan-mode/reentry-prompt.test.ts +0 -41
- package/src/session/agent-session-error-log.test.ts +0 -59
- package/src/session/blob-store.test.ts +0 -56
- package/src/session/messages.test.ts +0 -282
- package/src/session/session-context.test.ts +0 -384
- package/src/ssh/__tests__/connection-manager-args.test.ts +0 -191
- package/src/ssh/__tests__/connection-manager-timeout.test.ts +0 -61
- package/src/ssh/__tests__/file-transfer-posix-guard.test.ts +0 -105
- package/src/ssh/__tests__/sshfs-mount.test.ts +0 -13
- package/src/system-prompt.test.ts +0 -236
- package/src/task/render.test.ts +0 -290
- package/src/task/spawn-policy.test.ts +0 -62
- package/src/tools/__tests__/eval-description.test.ts +0 -18
- package/src/tools/__tests__/glob.test.ts +0 -37
- package/src/tools/__tests__/json-tree.test.ts +0 -35
- package/src/tools/__tests__/vibe-render.test.ts +0 -210
- package/src/tools/hub/launch-compat.test.ts +0 -40
- package/src/vibe/__tests__/token-rate.test.ts +0 -96
package/src/lsp/config.ts
CHANGED
|
@@ -74,6 +74,8 @@ function normalizeServerConfig(name: string, config: RawServerConfig): ServerCon
|
|
|
74
74
|
const fileTypes =
|
|
75
75
|
normalizeStringArray(config.fileTypes) ?? normalizeExtensionToFileTypes(config.extensionToLanguage);
|
|
76
76
|
const rootMarkers = normalizeStringArray(config.rootMarkers) ?? (config.extensionToLanguage ? ["."] : null);
|
|
77
|
+
const languageId =
|
|
78
|
+
typeof config.languageId === "string" && config.languageId.length > 0 ? config.languageId : undefined;
|
|
77
79
|
|
|
78
80
|
if (!command || !fileTypes || !rootMarkers) {
|
|
79
81
|
logger.warn("Ignoring invalid LSP server config (missing required fields).", { name });
|
|
@@ -95,6 +97,7 @@ function normalizeServerConfig(name: string, config: RawServerConfig): ServerCon
|
|
|
95
97
|
args,
|
|
96
98
|
fileTypes,
|
|
97
99
|
rootMarkers,
|
|
100
|
+
languageId,
|
|
98
101
|
...(initOptions ? { initOptions } : {}),
|
|
99
102
|
};
|
|
100
103
|
}
|
|
@@ -423,6 +426,7 @@ function getConfigSources(cwd: string): ConfigSource[] {
|
|
|
423
426
|
* "command": "/path/to/server",
|
|
424
427
|
* "args": ["--stdio"],
|
|
425
428
|
* "fileTypes": [".xyz"],
|
|
429
|
+
* "languageId": "xyz",
|
|
426
430
|
* "rootMarkers": [".xyz-project"]
|
|
427
431
|
* }
|
|
428
432
|
* }
|
package/src/lsp/types.ts
CHANGED
|
@@ -340,6 +340,8 @@ export interface ServerConfig {
|
|
|
340
340
|
command: string;
|
|
341
341
|
args?: string[];
|
|
342
342
|
fileTypes: string[];
|
|
343
|
+
/** LSP language identifier sent in didOpen; inferred from the file path when omitted. */
|
|
344
|
+
languageId?: string;
|
|
343
345
|
rootMarkers: string[];
|
|
344
346
|
initOptions?: Record<string, unknown>;
|
|
345
347
|
settings?: Record<string, unknown>;
|
package/src/mcp/config.ts
CHANGED
|
@@ -97,14 +97,6 @@ export async function loadAllMCPConfigs(cwd: string, options?: LoadMCPConfigsOpt
|
|
|
97
97
|
const filterExa = options?.filterExa ?? true;
|
|
98
98
|
const filterBrowser = options?.filterBrowser ?? false;
|
|
99
99
|
|
|
100
|
-
// Load MCP servers via capability system
|
|
101
|
-
const result = await loadCapability<MCPServer>(mcpCapability.id, { cwd });
|
|
102
|
-
|
|
103
|
-
// Filter out project-level configs if disabled
|
|
104
|
-
const servers = enableProjectConfig
|
|
105
|
-
? result.items
|
|
106
|
-
: result.items.filter(server => server._source.level !== "project");
|
|
107
|
-
|
|
108
100
|
// Load user-level disable/force-enable lists. The denylist always wins; the
|
|
109
101
|
// allowlist overrides a non-writable source config's `enabled: false`.
|
|
110
102
|
const userPath = getMCPConfigPath("user", cwd);
|
|
@@ -112,14 +104,34 @@ export async function loadAllMCPConfigs(cwd: string, options?: LoadMCPConfigsOpt
|
|
|
112
104
|
readDisabledServers(userPath).then(list => new Set(list)),
|
|
113
105
|
readEnabledServers(userPath).then(list => new Set(list)),
|
|
114
106
|
]);
|
|
115
|
-
|
|
107
|
+
|
|
108
|
+
// Scope exclusions drop entries entirely BEFORE deduplication: with project
|
|
109
|
+
// config disabled, a project entry must not shadow anything.
|
|
110
|
+
const includeServer = (server: MCPServer & { _source: SourceMeta }): boolean =>
|
|
111
|
+
enableProjectConfig || server._source.level !== "project";
|
|
112
|
+
|
|
113
|
+
// Disabled servers are suppressed rather than dropped: they still own their
|
|
114
|
+
// name at key-level dedupe (a disabled project `foo` keeps a same-named,
|
|
115
|
+
// lower-priority user `foo` disabled), but never equivalence-shadow a
|
|
116
|
+
// differently-named enabled server — otherwise the disabled alias would be
|
|
117
|
+
// removed downstream and starve the surviving connection.
|
|
118
|
+
const suppressServer = (server: MCPServer & { _source: SourceMeta }): boolean => {
|
|
119
|
+
if (disabledServers.has(server.name)) return true;
|
|
120
|
+
if (server.enabled === false && !forcedEnabled.has(server.name)) return true;
|
|
121
|
+
return false;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const result = await loadCapability<MCPServer>(mcpCapability.id, {
|
|
125
|
+
cwd,
|
|
126
|
+
filter: includeServer,
|
|
127
|
+
suppress: suppressServer,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Convert to legacy format and preserve source metadata.
|
|
116
131
|
let configs: Record<string, MCPServerConfig> = {};
|
|
117
132
|
let sources: Record<string, SourceMeta> = {};
|
|
118
|
-
for (const server of
|
|
119
|
-
|
|
120
|
-
if (disabledServers.has(server.name)) continue;
|
|
121
|
-
if (config.enabled === false && !forcedEnabled.has(server.name)) continue;
|
|
122
|
-
configs[server.name] = config;
|
|
133
|
+
for (const server of result.items) {
|
|
134
|
+
configs[server.name] = convertToLegacyConfig(server);
|
|
123
135
|
sources[server.name] = server._source;
|
|
124
136
|
}
|
|
125
137
|
|
package/src/mcp/manager.ts
CHANGED
|
@@ -1041,13 +1041,7 @@ export class MCPManager {
|
|
|
1041
1041
|
async #loadServerResourcesAndPrompts(name: string, connection: MCPServerConnection): Promise<void> {
|
|
1042
1042
|
if (serverSupportsResources(connection.capabilities)) {
|
|
1043
1043
|
try {
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
if (this.#notificationsEnabled && connection.capabilities.resources?.subscribe) {
|
|
1047
|
-
const uris = resources.map(r => r.uri);
|
|
1048
|
-
const notificationEpoch = this.#notificationsEpoch;
|
|
1049
|
-
this.#subscribeAndTrack(name, connection, uris, notificationEpoch);
|
|
1050
|
-
}
|
|
1044
|
+
await this.refreshServerResources(name);
|
|
1051
1045
|
} catch (error) {
|
|
1052
1046
|
logger.debug("Failed to load MCP resources", { path: `mcp:${name}`, error });
|
|
1053
1047
|
}
|
|
@@ -1107,8 +1101,20 @@ export class MCPManager {
|
|
|
1107
1101
|
connection.resources = undefined;
|
|
1108
1102
|
connection.resourceTemplates = undefined;
|
|
1109
1103
|
|
|
1110
|
-
// Reload
|
|
1111
|
-
|
|
1104
|
+
// Reload. Template listing failures must not discard a successful
|
|
1105
|
+
// resources/list — let both settle, then continue without templates.
|
|
1106
|
+
const [resourcesResult, templatesResult] = await Promise.allSettled([
|
|
1107
|
+
listResources(connection),
|
|
1108
|
+
listResourceTemplates(connection),
|
|
1109
|
+
]);
|
|
1110
|
+
if (templatesResult.status === "rejected") {
|
|
1111
|
+
logger.debug("Failed to list MCP resource templates", {
|
|
1112
|
+
path: `mcp:${name}`,
|
|
1113
|
+
error: templatesResult.reason,
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
if (resourcesResult.status === "rejected") throw resourcesResult.reason;
|
|
1117
|
+
const resources = resourcesResult.value;
|
|
1112
1118
|
if (this.#notificationsEnabled && connection.capabilities.resources?.subscribe) {
|
|
1113
1119
|
const newUris = new Set(resources.map(r => r.uri));
|
|
1114
1120
|
const oldUris = this.#subscribedResources.get(name);
|
|
@@ -1161,6 +1167,17 @@ export class MCPManager {
|
|
|
1161
1167
|
return promise;
|
|
1162
1168
|
}
|
|
1163
1169
|
|
|
1170
|
+
/**
|
|
1171
|
+
* Wait until a connected server's resource catalog has been loaded.
|
|
1172
|
+
* Coalesces with initial loading and notification-driven refreshes.
|
|
1173
|
+
*/
|
|
1174
|
+
async ensureServerResources(name: string): Promise<void> {
|
|
1175
|
+
const connection = this.#connections.get(name);
|
|
1176
|
+
if (!connection || !serverSupportsResources(connection.capabilities)) return;
|
|
1177
|
+
if (connection.resources !== undefined && connection.resourceTemplates !== undefined) return;
|
|
1178
|
+
await this.refreshServerResources(name);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1164
1181
|
/**
|
|
1165
1182
|
* Refresh prompts from a specific server.
|
|
1166
1183
|
*/
|
package/src/mcp/tool-bridge.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import type { AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
7
7
|
import type { TSchema } from "@oh-my-pi/pi-ai";
|
|
8
8
|
import { normalizeSchemaForMCP } from "@oh-my-pi/pi-ai/utils/schema";
|
|
9
|
-
import { untilAborted } from "@oh-my-pi/pi-utils";
|
|
9
|
+
import { logger, untilAborted } from "@oh-my-pi/pi-utils";
|
|
10
10
|
import { INTENT_FIELD } from "@oh-my-pi/pi-wire";
|
|
11
11
|
import type { SourceMeta } from "../capability/types";
|
|
12
12
|
import type {
|
|
@@ -357,6 +357,57 @@ export function createMCPToolName(serverName: string, toolName: string): string
|
|
|
357
357
|
return `mcp__${sanitizedServerName}_${normalizedToolName}`;
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
+
/**
|
|
361
|
+
* Keeps one MCP tool per minted name and logs collisions between distinct MCP
|
|
362
|
+
* origins. The winner is chosen by a stable origin key (server name + original
|
|
363
|
+
* tool name), NOT array order: MCPManager re-appends a reconnecting server's
|
|
364
|
+
* tools, so insertion order is mutable across reconnects and first-wins would
|
|
365
|
+
* silently flip ownership of the minted name. Non-MCP tools pass through
|
|
366
|
+
* unchanged.
|
|
367
|
+
*/
|
|
368
|
+
export function deduplicateMCPToolsByName<T extends { name: string; mcpServerName?: unknown; mcpToolName?: unknown }>(
|
|
369
|
+
tools: readonly T[],
|
|
370
|
+
): T[] {
|
|
371
|
+
const deduplicated: T[] = [];
|
|
372
|
+
const registered = new Map<string, { tool: T; originKey: string; index: number }>();
|
|
373
|
+
|
|
374
|
+
for (const tool of tools) {
|
|
375
|
+
if (typeof tool.mcpServerName !== "string" || typeof tool.mcpToolName !== "string") {
|
|
376
|
+
deduplicated.push(tool);
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const originKey = `${tool.mcpServerName}\u0000${tool.mcpToolName}`;
|
|
381
|
+
const existing = registered.get(tool.name);
|
|
382
|
+
if (!existing) {
|
|
383
|
+
registered.set(tool.name, { tool, originKey, index: deduplicated.length });
|
|
384
|
+
deduplicated.push(tool);
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (existing.originKey === originKey) continue;
|
|
389
|
+
|
|
390
|
+
// Deterministic winner regardless of encounter order across reconnects.
|
|
391
|
+
const keepExisting = existing.originKey < originKey;
|
|
392
|
+
const winner = keepExisting ? existing.tool : tool;
|
|
393
|
+
const loser = keepExisting ? tool : existing.tool;
|
|
394
|
+
if (!keepExisting) {
|
|
395
|
+
deduplicated[existing.index] = tool;
|
|
396
|
+
existing.tool = tool;
|
|
397
|
+
existing.originKey = originKey;
|
|
398
|
+
}
|
|
399
|
+
logger.warn("MCP tool name collision; keeping stable winner", {
|
|
400
|
+
name: tool.name,
|
|
401
|
+
keptServer: winner.mcpServerName,
|
|
402
|
+
keptTool: winner.mcpToolName,
|
|
403
|
+
ignoredServer: loser.mcpServerName,
|
|
404
|
+
ignoredTool: loser.mcpToolName,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return deduplicated;
|
|
409
|
+
}
|
|
410
|
+
|
|
360
411
|
/**
|
|
361
412
|
* Parse an MCP tool name back to server and tool components.
|
|
362
413
|
*
|
package/src/memories/index.ts
CHANGED
|
@@ -1361,12 +1361,31 @@ async function appendLearnedLine(filePath: string, line: string): Promise<void>
|
|
|
1361
1361
|
} catch (err) {
|
|
1362
1362
|
if (!isEnoent(err)) throw err;
|
|
1363
1363
|
}
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1364
|
+
// Treat the file as an ordered line list so headings, prose, and blank
|
|
1365
|
+
// lines keep their positions relative to the bullets they scope. Managed
|
|
1366
|
+
// operations touch only bullet lines: dedupe removes an existing copy of
|
|
1367
|
+
// the incoming lesson in place, the new lesson enters at the head of the
|
|
1368
|
+
// first bullet run (newest-first, matching the read path and cap docs),
|
|
1369
|
+
// and the cap drops the oldest (bottom-most) bullets. Hand-edited content
|
|
1370
|
+
// outside the list region survives every write byte-for-byte.
|
|
1371
|
+
const lines = existing.split("\n");
|
|
1372
|
+
// A well-formed file ends with "\n"; drop the terminal split artifact so
|
|
1373
|
+
// repeated saves stay idempotent instead of growing a blank line each time.
|
|
1374
|
+
if (lines.at(-1) === "") lines.pop();
|
|
1375
|
+
const isLesson = (l: string) => l.trimStart().startsWith("- ");
|
|
1376
|
+
const out = lines.filter(l => !(isLesson(l) && l.trim() === line));
|
|
1377
|
+
const firstBullet = out.findIndex(isLesson);
|
|
1378
|
+
if (firstBullet === -1) out.push(line);
|
|
1379
|
+
else out.splice(firstBullet, 0, line);
|
|
1380
|
+
let lessonCount = 0;
|
|
1381
|
+
for (const l of out) if (isLesson(l)) lessonCount++;
|
|
1382
|
+
for (let i = out.length - 1; i >= 0 && lessonCount > MAX_LEARNED_LESSONS; i--) {
|
|
1383
|
+
if (isLesson(out[i])) {
|
|
1384
|
+
out.splice(i, 1);
|
|
1385
|
+
lessonCount--;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
await Bun.write(filePath, `${out.join("\n")}\n`);
|
|
1370
1389
|
}
|
|
1371
1390
|
|
|
1372
1391
|
/**
|
|
@@ -71,6 +71,14 @@ function buildMatchKeys(keys: readonly KeyId[]): Set<string> {
|
|
|
71
71
|
return matchKeys;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
function unionOfMatchKeys(matchKeys: ReadonlyMap<ConfigurableEditorAction, ReadonlySet<string>>): Set<string> {
|
|
75
|
+
const union = new Set<string>();
|
|
76
|
+
for (const keys of matchKeys.values()) {
|
|
77
|
+
for (const key of keys) union.add(key);
|
|
78
|
+
}
|
|
79
|
+
return union;
|
|
80
|
+
}
|
|
81
|
+
|
|
74
82
|
const BRACKETED_PASTE_START = "\x1b[200~";
|
|
75
83
|
const BRACKETED_PASTE_END = "\x1b[201~";
|
|
76
84
|
const BRACKETED_IMAGE_PATH_REGEX = /\.(?:png|jpe?g|gif|webp)$/i;
|
|
@@ -419,6 +427,17 @@ export class CustomEditor extends Editor {
|
|
|
419
427
|
this.pendingImageLinks = [];
|
|
420
428
|
}
|
|
421
429
|
|
|
430
|
+
/** Replace the composer draft with a restored historical prompt: sets the text and
|
|
431
|
+
* re-attaches the message's images so positional `[Image #N]` markers resolve on
|
|
432
|
+
* resubmit instead of degrading to literal text (esc-esc branch, `/tree`). Source
|
|
433
|
+
* links are unknown for restored drafts, so every link slot is `undefined`. */
|
|
434
|
+
setDraft(text: string, images?: readonly ImageContent[]): void {
|
|
435
|
+
this.setText(text);
|
|
436
|
+
this.imageLinks = undefined;
|
|
437
|
+
this.pendingImages = images ? [...images] : [];
|
|
438
|
+
this.pendingImageLinks = images ? images.map(() => undefined) : [];
|
|
439
|
+
}
|
|
440
|
+
|
|
422
441
|
/** Treat image/paste markers as indivisible: a stray backspace deletes the whole token
|
|
423
442
|
* instead of corrupting `[Paste #1, +30 lines]` into plain text. */
|
|
424
443
|
override atomicTokenPattern = PLACEHOLDER_REGEX;
|
|
@@ -600,14 +619,14 @@ export class CustomEditor extends Editor {
|
|
|
600
619
|
buildMatchKeys(keys),
|
|
601
620
|
]),
|
|
602
621
|
);
|
|
622
|
+
/** Union of every action's match keys: one probe in `handleInput` decides
|
|
623
|
+
* whether the per-action interception chain can match at all. */
|
|
624
|
+
#actionMatchKeyUnion = unionOfMatchKeys(this.#actionMatchKeys);
|
|
603
625
|
|
|
604
626
|
setActionKeys(action: ConfigurableEditorAction, keys: KeyId[]): void {
|
|
605
627
|
this.#actionKeys.set(action, [...keys]);
|
|
606
|
-
this.#
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
#rebuildActionMatchKeys(action: ConfigurableEditorAction): void {
|
|
610
|
-
this.#actionMatchKeys.set(action, buildMatchKeys(this.#actionKeys.get(action) ?? []));
|
|
628
|
+
this.#actionMatchKeys.set(action, buildMatchKeys(keys));
|
|
629
|
+
this.#actionMatchKeyUnion = unionOfMatchKeys(this.#actionMatchKeys);
|
|
611
630
|
}
|
|
612
631
|
|
|
613
632
|
#rebuildCustomMatchKeys(): void {
|
|
@@ -760,8 +779,10 @@ export class CustomEditor extends Editor {
|
|
|
760
779
|
this.#pendingInput.push(data);
|
|
761
780
|
return;
|
|
762
781
|
}
|
|
763
|
-
|
|
764
|
-
|
|
782
|
+
// textEquals avoids getText()'s O(buffer) join on every keystroke; kitty
|
|
783
|
+
// sequences always start with ESC, so plain bytes skip the native parse.
|
|
784
|
+
const hadBareQueuePrefix = this.textEquals("->") || this.textEquals("=>");
|
|
785
|
+
const kittyParsed = data.charCodeAt(0) === 0x1b ? parseKittySequence(data) : null;
|
|
765
786
|
if (kittyParsed && (kittyParsed.modifier & 64) !== 0 && this.onCapsLock) {
|
|
766
787
|
// Caps Lock is modifier bit 64
|
|
767
788
|
this.onCapsLock();
|
|
@@ -823,7 +844,12 @@ export class CustomEditor extends Editor {
|
|
|
823
844
|
// Space-hold push-to-talk: a sustained space bar starts/stops STT instead of typing spaces.
|
|
824
845
|
if (this.#handleSpaceHold(data, canonical)) return;
|
|
825
846
|
|
|
826
|
-
|
|
847
|
+
// One union probe decides whether any per-action interception below can
|
|
848
|
+
// match — plain typing then skips the ~20 per-action set lookups per key.
|
|
849
|
+
if (
|
|
850
|
+
canonical !== undefined &&
|
|
851
|
+
(this.#actionMatchKeyUnion.has(canonical) || this.#customMatchKeys.has(canonical))
|
|
852
|
+
) {
|
|
827
853
|
// Intercept configured image paste (async - fires and handles result)
|
|
828
854
|
if (this.#matchesAction(canonical, "app.clipboard.pasteImage") && this.onPasteImage) {
|
|
829
855
|
void this.onPasteImage();
|
|
@@ -964,14 +990,11 @@ export class CustomEditor extends Editor {
|
|
|
964
990
|
|
|
965
991
|
// Pass to parent for normal handling
|
|
966
992
|
super.handleInput(data);
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
cursor.col === 2
|
|
973
|
-
) {
|
|
974
|
-
this.insertText("\n");
|
|
993
|
+
if (!hadBareQueuePrefix && (this.textEquals("->") || this.textEquals("=>"))) {
|
|
994
|
+
const cursor = this.getCursor();
|
|
995
|
+
if (cursor.line === 0 && cursor.col === 2) {
|
|
996
|
+
this.insertText("\n");
|
|
997
|
+
}
|
|
975
998
|
}
|
|
976
999
|
}
|
|
977
1000
|
|
|
@@ -433,11 +433,12 @@ const costSegment: StatusLineSegment = {
|
|
|
433
433
|
id: "cost",
|
|
434
434
|
render(ctx) {
|
|
435
435
|
const { cost, premiumRequests } = ctx.usageStats;
|
|
436
|
+
const advisorCost = ctx.session.getAdvisorCost?.() ?? 0;
|
|
436
437
|
const normalizedPremiumRequests = normalizePremiumRequests(premiumRequests);
|
|
437
438
|
const state = ctx.session.state;
|
|
438
439
|
const usingSubscription = state.model ? ctx.session.modelRegistry.isUsingOAuth(state.model) : false;
|
|
439
440
|
|
|
440
|
-
if (!cost && !usingSubscription && !normalizedPremiumRequests) {
|
|
441
|
+
if (!cost && !advisorCost && !usingSubscription && !normalizedPremiumRequests) {
|
|
441
442
|
return { content: "", visible: false };
|
|
442
443
|
}
|
|
443
444
|
|
|
@@ -445,6 +446,7 @@ const costSegment: StatusLineSegment = {
|
|
|
445
446
|
if (cost) billingParts.push(`$${cost.toFixed(2)}`);
|
|
446
447
|
if (normalizedPremiumRequests) billingParts.push(`★ ${formatNumber(normalizedPremiumRequests)}`);
|
|
447
448
|
if (usingSubscription) billingParts.push("(sub)");
|
|
449
|
+
if (advisorCost) billingParts.push(`${billingParts.length ? "+ " : ""}$${advisorCost.toFixed(2)} (adv)`);
|
|
448
450
|
|
|
449
451
|
return { content: theme.fg("statusLineCost", billingParts.join(" ")), visible: true };
|
|
450
452
|
},
|
|
@@ -22,4 +22,5 @@ Press ← ← to drill into a running or finished agent and inspect its tool cal
|
|
|
22
22
|
Hit a Codex rate limit? `/usage reset` spends a saved reset credit to immediately restore your quota
|
|
23
23
|
No native tool_calling? Inference provider botches parsing them? `PI_DIALECT=glm|kimi|anthropic…` rolls it locally for them!
|
|
24
24
|
Turn on `/advisor` to attach a second model that reviews every turn and quietly injects advice
|
|
25
|
-
Try starting your prompt with a ->, and writing a list (1. Do X, 2. Do Y)
|
|
25
|
+
Try starting your prompt with a ->, and writing a list (1. Do X, 2. Do Y)
|
|
26
|
+
Press shift+tab to cycle through reasoning effort levels
|
|
@@ -25,6 +25,7 @@ import { isWaitingPollDetails } from "../../tools/hub";
|
|
|
25
25
|
import { formatStatusIcon, replaceTabs, resolveImageOptions } from "../../tools/render-utils";
|
|
26
26
|
import { type FirstResultViewportRepaint, toolRenderers } from "../../tools/renderers";
|
|
27
27
|
import { TODO_STRIKE_TOTAL_FRAMES, type TodoToolDetails } from "../../tools/todo";
|
|
28
|
+
import type { XdevState } from "../../tools/xdev";
|
|
28
29
|
import { isFramedBlockComponent, markFramedBlockComponent, renderStatusLine, WidthAwareText } from "../../tui";
|
|
29
30
|
import { sanitizeWithOptionalSixelPassthrough } from "../../utils/sixel";
|
|
30
31
|
import { renderDiff } from "./diff";
|
|
@@ -1316,13 +1317,13 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
1316
1317
|
}
|
|
1317
1318
|
context.renderDiff = renderDiff;
|
|
1318
1319
|
} else if (this.#toolName === "write") {
|
|
1319
|
-
// Device-dispatch previews
|
|
1320
|
-
|
|
1321
|
-
const
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1320
|
+
// Device-dispatch previews resolve renderers from the canonical tool map.
|
|
1321
|
+
const writeTool = this.#tool as { session?: { xdev?: XdevState } } | undefined;
|
|
1322
|
+
const xdev = writeTool?.session?.xdev;
|
|
1323
|
+
if (xdev) {
|
|
1324
|
+
context.resolveXdevMounted = (name: string) =>
|
|
1325
|
+
xdev.mountedNames.has(name) ? xdev.tools.get(name) : undefined;
|
|
1326
|
+
}
|
|
1326
1327
|
}
|
|
1327
1328
|
|
|
1328
1329
|
return context;
|
|
@@ -346,7 +346,7 @@ export class CommandController {
|
|
|
346
346
|
}
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
-
this.ctx.
|
|
349
|
+
this.ctx.presentCommandOutput([new Spacer(1), new Text(info, 1, 0)]);
|
|
350
350
|
}
|
|
351
351
|
|
|
352
352
|
static readonly #advisorStatusGlyph: Record<string, string> = {
|
|
@@ -368,7 +368,7 @@ export class CommandController {
|
|
|
368
368
|
async handleAdvisorStatusCommand(): Promise<void> {
|
|
369
369
|
const stats = this.ctx.session.getAdvisorStats();
|
|
370
370
|
if (!stats.configured) {
|
|
371
|
-
this.ctx.
|
|
371
|
+
this.ctx.presentCommandOutput([new Spacer(1), new Text("Advisor is disabled.", 1, 0)]);
|
|
372
372
|
return;
|
|
373
373
|
}
|
|
374
374
|
// Fetch live quota data (cached 5 min by the auth-gateway) so we can show
|
|
@@ -434,7 +434,7 @@ export class CommandController {
|
|
|
434
434
|
info += `${theme.fg("dim", "Tokens:")} ${stats.tokens.total.toLocaleString()}\n`;
|
|
435
435
|
if (stats.cost > 0) info += `${theme.fg("dim", "Cost:")} $${stats.cost.toFixed(4)}\n`;
|
|
436
436
|
}
|
|
437
|
-
this.ctx.
|
|
437
|
+
this.ctx.presentCommandOutput([new Spacer(1), new Text(info, 1, 0)]);
|
|
438
438
|
return;
|
|
439
439
|
}
|
|
440
440
|
// Single active advisor — detailed view.
|
|
@@ -480,7 +480,7 @@ export class CommandController {
|
|
|
480
480
|
info += `${theme.fg("dim", "Cache Read:")} ${stats.tokens.cacheRead.toLocaleString()}\n`;
|
|
481
481
|
}
|
|
482
482
|
if (stats.cost > 0) info += `${theme.fg("dim", "Cost:")} $${stats.cost.toFixed(4)}\n`;
|
|
483
|
-
this.ctx.
|
|
483
|
+
this.ctx.presentCommandOutput([new Spacer(1), new Text(info, 1, 0)]);
|
|
484
484
|
}
|
|
485
485
|
|
|
486
486
|
async handleJobsCommand(): Promise<void> {
|
|
@@ -497,7 +497,7 @@ export class CommandController {
|
|
|
497
497
|
|
|
498
498
|
if (snapshot.running.length === 0 && snapshot.recent.length === 0) {
|
|
499
499
|
info += `\n${theme.fg("dim", "No async jobs yet.")}\n`;
|
|
500
|
-
this.ctx.
|
|
500
|
+
this.ctx.presentCommandOutput([new Spacer(1), new Text(info, 1, 0)]);
|
|
501
501
|
return;
|
|
502
502
|
}
|
|
503
503
|
|
|
@@ -517,7 +517,7 @@ export class CommandController {
|
|
|
517
517
|
}
|
|
518
518
|
}
|
|
519
519
|
|
|
520
|
-
this.ctx.
|
|
520
|
+
this.ctx.presentCommandOutput([new Spacer(1), new Text(info.trimEnd(), 1, 0)]);
|
|
521
521
|
}
|
|
522
522
|
|
|
523
523
|
async handleUsageCommand(reports?: UsageReport[] | null): Promise<void> {
|
|
@@ -558,7 +558,7 @@ export class CommandController {
|
|
|
558
558
|
provider => (provider === currentProvider ? activeAccount : undefined),
|
|
559
559
|
usageModelSelectors,
|
|
560
560
|
);
|
|
561
|
-
this.ctx.
|
|
561
|
+
this.ctx.presentCommandOutput([new Spacer(1), new Text(output, 1, 0)]);
|
|
562
562
|
}
|
|
563
563
|
|
|
564
564
|
async handleChangelogCommand(showFull = false): Promise<void> {
|
|
@@ -578,7 +578,7 @@ export class CommandController {
|
|
|
578
578
|
block.addChild(new Spacer(1));
|
|
579
579
|
block.addChild(new Markdown(changelogMarkdown + hint, 1, 1, getMarkdownTheme()));
|
|
580
580
|
block.addChild(new DynamicBorder());
|
|
581
|
-
this.ctx.
|
|
581
|
+
this.ctx.presentCommandOutput(block);
|
|
582
582
|
}
|
|
583
583
|
|
|
584
584
|
handleHotkeysCommand(): void {
|
|
@@ -607,7 +607,7 @@ export class CommandController {
|
|
|
607
607
|
block.addChild(new Spacer(1));
|
|
608
608
|
block.addChild(new Text(output, 1, 0));
|
|
609
609
|
block.addChild(new DynamicBorder());
|
|
610
|
-
this.ctx.
|
|
610
|
+
this.ctx.presentCommandOutput(block);
|
|
611
611
|
}
|
|
612
612
|
|
|
613
613
|
async handleMemoryCommand(text: string): Promise<void> {
|
|
@@ -628,7 +628,7 @@ export class CommandController {
|
|
|
628
628
|
block.addChild(new Spacer(1));
|
|
629
629
|
block.addChild(new Markdown(payload, 1, 1, getMarkdownTheme()));
|
|
630
630
|
block.addChild(new DynamicBorder());
|
|
631
|
-
this.ctx.
|
|
631
|
+
this.ctx.presentCommandOutput(block);
|
|
632
632
|
return;
|
|
633
633
|
}
|
|
634
634
|
|
|
@@ -86,7 +86,7 @@ export class ExtensionUiController {
|
|
|
86
86
|
const uiContext: ExtensionUIContext = {
|
|
87
87
|
timeoutStartsOnPresentation: true,
|
|
88
88
|
select: (title, options, dialogOptions) => this.showCollabAwareSelector(title, options, dialogOptions),
|
|
89
|
-
confirm: (title, message,
|
|
89
|
+
confirm: (title, message, dialogOptions) => this.showHookConfirm(title, message, dialogOptions),
|
|
90
90
|
input: (title, placeholder, dialogOptions) => this.showHookInput(title, placeholder, dialogOptions),
|
|
91
91
|
askDialog: (questions, dialogOptions) => this.showAskDialog(questions, dialogOptions),
|
|
92
92
|
notify: (message, type) => this.showHookNotify(message, type),
|
|
@@ -246,7 +246,7 @@ export class ExtensionUiController {
|
|
|
246
246
|
// Update UI
|
|
247
247
|
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
248
248
|
await this.ctx.reloadTodos();
|
|
249
|
-
this.ctx.editor.
|
|
249
|
+
this.ctx.editor.setDraft(result.selectedText, result.selectedImages);
|
|
250
250
|
this.ctx.showStatus("Branched to new session");
|
|
251
251
|
|
|
252
252
|
return { cancelled: false };
|
|
@@ -261,7 +261,7 @@ export class ExtensionUiController {
|
|
|
261
261
|
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
262
262
|
await this.ctx.reloadTodos();
|
|
263
263
|
if (result.editorText && !this.ctx.editor.getText().trim()) {
|
|
264
|
-
this.ctx.editor.
|
|
264
|
+
this.ctx.editor.setDraft(result.editorText, result.editorImages);
|
|
265
265
|
}
|
|
266
266
|
this.ctx.showStatus("Navigated to selected point");
|
|
267
267
|
|
|
@@ -476,7 +476,7 @@ export class ExtensionUiController {
|
|
|
476
476
|
// Update UI
|
|
477
477
|
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
478
478
|
await this.ctx.reloadTodos();
|
|
479
|
-
this.ctx.editor.
|
|
479
|
+
this.ctx.editor.setDraft(result.selectedText, result.selectedImages);
|
|
480
480
|
this.ctx.showStatus("Branched to new session");
|
|
481
481
|
|
|
482
482
|
return { cancelled: false };
|
|
@@ -491,7 +491,7 @@ export class ExtensionUiController {
|
|
|
491
491
|
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
492
492
|
await this.ctx.reloadTodos();
|
|
493
493
|
if (result.editorText && !this.ctx.editor.getText().trim()) {
|
|
494
|
-
this.ctx.editor.
|
|
494
|
+
this.ctx.editor.setDraft(result.editorText, result.editorImages);
|
|
495
495
|
}
|
|
496
496
|
this.ctx.showStatus("Navigated to selected point");
|
|
497
497
|
|
|
@@ -941,8 +941,8 @@ export class ExtensionUiController {
|
|
|
941
941
|
/**
|
|
942
942
|
* Show a confirmation dialog for hooks.
|
|
943
943
|
*/
|
|
944
|
-
async showHookConfirm(title: string, message: string): Promise<boolean> {
|
|
945
|
-
const result = await this.showHookSelector(`${title}\n${message}`, ["Yes", "No"]);
|
|
944
|
+
async showHookConfirm(title: string, message: string, dialogOptions?: ExtensionUIDialogOptions): Promise<boolean> {
|
|
945
|
+
const result = await this.showHookSelector(`${title}\n${message}`, ["Yes", "No"], dialogOptions);
|
|
946
946
|
return result === "Yes";
|
|
947
947
|
}
|
|
948
948
|
|
|
@@ -455,6 +455,11 @@ export class SelectorController {
|
|
|
455
455
|
this.ctx.showError(`Failed to apply memory backend: ${err}`);
|
|
456
456
|
});
|
|
457
457
|
break;
|
|
458
|
+
case "inspect_image.mode":
|
|
459
|
+
void this.ctx.session.applyInspectImageModeChange().catch(err => {
|
|
460
|
+
this.ctx.showError(`Failed to apply vision mode: ${err}`);
|
|
461
|
+
});
|
|
462
|
+
break;
|
|
458
463
|
|
|
459
464
|
case "autocompleteMaxVisible":
|
|
460
465
|
this.ctx.editor.setAutocompleteMaxVisible(typeof value === "number" ? value : Number(value));
|
|
@@ -1097,7 +1102,7 @@ export class SelectorController {
|
|
|
1097
1102
|
}
|
|
1098
1103
|
|
|
1099
1104
|
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
1100
|
-
this.ctx.editor.
|
|
1105
|
+
this.ctx.editor.setDraft(result.selectedText, result.selectedImages);
|
|
1101
1106
|
done();
|
|
1102
1107
|
this.ctx.showStatus("Branched to new session");
|
|
1103
1108
|
},
|
|
@@ -1272,7 +1277,7 @@ export class SelectorController {
|
|
|
1272
1277
|
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
1273
1278
|
await this.ctx.reloadTodos();
|
|
1274
1279
|
if (result.editorText && !this.ctx.editor.getText().trim()) {
|
|
1275
|
-
this.ctx.editor.
|
|
1280
|
+
this.ctx.editor.setDraft(result.editorText, result.editorImages);
|
|
1276
1281
|
}
|
|
1277
1282
|
this.ctx.showStatus("Navigated to selected point");
|
|
1278
1283
|
|