@herbertgao/pi-extensions 2026.9.19 → 2026.9.20
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 +1 -8
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +76 -59
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +76 -59
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/preview.webp +0 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +93 -34
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +346 -117
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +6 -36
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/index.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +24 -29
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +330 -360
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/index.ts +56 -108
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/session.ts +97 -15
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/subagent.ts +80 -45
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/flush-docked-bash.ts +36 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer-layout.ts +229 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer.ts +479 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/startup-header.ts +92 -61
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/working-message.ts +58 -38
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +19 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +1080 -229
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +232 -259
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +38 -48
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +8 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +2 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +277 -113
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +15 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +18 -72
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +61 -26
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/ansi-utils.ts +25 -57
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-component.ts +125 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-edit-render.ts +227 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-header.ts +136 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-highlight.ts +115 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-inline.ts +324 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-layout.ts +762 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-limits.ts +152 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-palette.ts +410 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-parse.ts +535 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-presentation.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +21 -3219
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-text.ts +44 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-write-render.ts +568 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +19 -18
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/line-width-safety.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-execution.ts +1 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +142 -192
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/mcp-title.ts +39 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{message-display.ts → tool/message-display.ts} +24 -90
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/names.ts +426 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +90 -132
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{show-more-hint.ts → tool/show-more-hint.ts} +10 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +8 -26
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-color.ts +44 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-text.ts +76 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/component-tree.ts +34 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/format.ts +28 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +51 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/patch-keys.ts +124 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/sgr-mouse.ts +70 -0
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +7 -7
- package/package.json +3 -7
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +0 -101
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.svg +0 -101
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/welcome.webp +0 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/render-utils.ts +0 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +0 -30
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +0 -12
- package/node_modules/pi-footer/CHANGELOG.md +0 -145
- package/node_modules/pi-footer/LICENSE +0 -21
- package/node_modules/pi-footer/README.md +0 -354
- package/node_modules/pi-footer/package.json +0 -68
- package/node_modules/pi-footer/src/cache.ts +0 -106
- package/node_modules/pi-footer/src/colors.ts +0 -237
- package/node_modules/pi-footer/src/config.ts +0 -202
- package/node_modules/pi-footer/src/event-widgets.ts +0 -41
- package/node_modules/pi-footer/src/extension-statuses.ts +0 -102
- package/node_modules/pi-footer/src/git.ts +0 -135
- package/node_modules/pi-footer/src/index.ts +0 -268
- package/node_modules/pi-footer/src/metrics.ts +0 -141
- package/node_modules/pi-footer/src/presets.ts +0 -383
- package/node_modules/pi-footer/src/render.ts +0 -114
- package/node_modules/pi-footer/src/separators.ts +0 -52
- package/node_modules/pi-footer/src/types.ts +0 -122
- package/node_modules/pi-footer/src/ui/color-level-confirm.ts +0 -22
- package/node_modules/pi-footer/src/ui/color-options.ts +0 -195
- package/node_modules/pi-footer/src/ui/config-lifecycle.ts +0 -61
- package/node_modules/pi-footer/src/ui/edit-colors.ts +0 -13
- package/node_modules/pi-footer/src/ui/events.ts +0 -35
- package/node_modules/pi-footer/src/ui/extension-status-picker.ts +0 -60
- package/node_modules/pi-footer/src/ui/extension-statuses.ts +0 -73
- package/node_modules/pi-footer/src/ui/fields.ts +0 -252
- package/node_modules/pi-footer/src/ui/global-menu.ts +0 -126
- package/node_modules/pi-footer/src/ui/helpers.ts +0 -44
- package/node_modules/pi-footer/src/ui/layout.ts +0 -17
- package/node_modules/pi-footer/src/ui/line-list.ts +0 -19
- package/node_modules/pi-footer/src/ui/model.ts +0 -64
- package/node_modules/pi-footer/src/ui/navigation.ts +0 -29
- package/node_modules/pi-footer/src/ui/option-edit.ts +0 -93
- package/node_modules/pi-footer/src/ui/overlay-render.ts +0 -103
- package/node_modules/pi-footer/src/ui/screen-context.ts +0 -18
- package/node_modules/pi-footer/src/ui/screen-controller.ts +0 -27
- package/node_modules/pi-footer/src/ui/screen-render.ts +0 -32
- package/node_modules/pi-footer/src/ui/screen-state.ts +0 -20
- package/node_modules/pi-footer/src/ui/screen.ts +0 -243
- package/node_modules/pi-footer/src/ui/screens/add-widget.ts +0 -92
- package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +0 -52
- package/node_modules/pi-footer/src/ui/screens/controller.ts +0 -12
- package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +0 -68
- package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +0 -115
- package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +0 -61
- package/node_modules/pi-footer/src/ui/screens/global.ts +0 -75
- package/node_modules/pi-footer/src/ui/screens/line-list.ts +0 -108
- package/node_modules/pi-footer/src/ui/screens/main.ts +0 -99
- package/node_modules/pi-footer/src/ui/screens/terminal.ts +0 -121
- package/node_modules/pi-footer/src/ui/screens/widget-list.ts +0 -154
- package/node_modules/pi-footer/src/ui/terminal-menu.ts +0 -32
- package/node_modules/pi-footer/src/ui/theme.ts +0 -48
- package/node_modules/pi-footer/src/ui/title-bar.ts +0 -100
- package/node_modules/pi-footer/src/ui/widget-actions.ts +0 -76
- package/node_modules/pi-footer/src/ui.ts +0 -66
- package/node_modules/pi-footer/src/widgets/context.ts +0 -28
- package/node_modules/pi-footer/src/widgets/core/active-tools.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/context-window.ts +0 -24
- package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +0 -19
- package/node_modules/pi-footer/src/widgets/core/cwd.ts +0 -108
- package/node_modules/pi-footer/src/widgets/core/event.ts +0 -34
- package/node_modules/pi-footer/src/widgets/core/external-status.ts +0 -102
- package/node_modules/pi-footer/src/widgets/core/model-provider.ts +0 -20
- package/node_modules/pi-footer/src/widgets/core/model.ts +0 -35
- package/node_modules/pi-footer/src/widgets/core/provider.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/session-name.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/branch.ts +0 -81
- package/node_modules/pi-footer/src/widgets/git/clean.ts +0 -19
- package/node_modules/pi-footer/src/widgets/git/deletions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/diff.ts +0 -38
- package/node_modules/pi-footer/src/widgets/git/insertions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/remote.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/root.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/sha.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/staged.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/status.ts +0 -19
- package/node_modules/pi-footer/src/widgets/git/unstaged.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/untracked.ts +0 -17
- package/node_modules/pi-footer/src/widgets/instance.ts +0 -118
- package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +0 -18
- package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +0 -32
- package/node_modules/pi-footer/src/widgets/layout/separator.ts +0 -57
- package/node_modules/pi-footer/src/widgets/layout/spacer.ts +0 -31
- package/node_modules/pi-footer/src/widgets/options.ts +0 -122
- package/node_modules/pi-footer/src/widgets/project/runtime.ts +0 -361
- package/node_modules/pi-footer/src/widgets/registry.ts +0 -223
- package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/compactions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/elapsed.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/last-activity.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/messages.ts +0 -19
- package/node_modules/pi-footer/src/widgets/session/session-id.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/session-start.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/tool-results.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/total-messages.ts +0 -19
- package/node_modules/pi-footer/src/widgets/session/total-time.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/user-messages.ts +0 -17
- package/node_modules/pi-footer/src/widgets/store.ts +0 -26
- package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +0 -55
- package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +0 -108
- package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +0 -26
- package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +0 -29
- package/node_modules/pi-footer/src/widgets/tokens/context.ts +0 -29
- package/node_modules/pi-footer/src/widgets/tokens/cost.ts +0 -51
- package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +0 -20
- package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/types.ts +0 -181
- package/node_modules/pi-footer/src/widgets/utils/colors.ts +0 -11
- package/node_modules/pi-footer/src/widgets/utils/context.ts +0 -112
- package/node_modules/pi-footer/src/widgets/utils/session.ts +0 -19
- package/node_modules/pi-footer/src/widgets/utils/token-format.ts +0 -78
package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/line-width-safety.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { normalizeDiffRenderWidth } from "./diff-presentation.ts";
|
|
2
|
-
import { pluralize } from "./
|
|
3
|
-
import { showMoreHintText } from "
|
|
2
|
+
import { pluralize } from "./diff-text.ts";
|
|
3
|
+
import { showMoreHintText } from "../show-more-hint.ts";
|
|
4
4
|
|
|
5
5
|
export interface WidthMeasurementOps {
|
|
6
6
|
measure(text: string): number;
|
package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-execution.ts
CHANGED
|
@@ -94,6 +94,7 @@ export async function executeWriteWithMetadata(
|
|
|
94
94
|
await mkdir(dirname(absolutePath), { recursive: true });
|
|
95
95
|
throwIfAborted();
|
|
96
96
|
await writeFile(absolutePath, params.content, "utf8");
|
|
97
|
+
// 写入后不再检查 abort:文件已变更,必须如实报告成功
|
|
97
98
|
store.set(toolCallId, metadata);
|
|
98
99
|
return {
|
|
99
100
|
content: [
|
|
@@ -7,18 +7,26 @@ import {
|
|
|
7
7
|
type Component,
|
|
8
8
|
} from "@earendil-works/pi-tui";
|
|
9
9
|
import { TOOL_LOADING_INTERVAL_MS, toolLoadingIcon } from "../../utils/tool-loading-icon.ts";
|
|
10
|
-
import {
|
|
10
|
+
import { getToolMouseTui } from "../mouse/scroll.ts";
|
|
11
|
+
import { mcpToolTitle } from "./mcp-title.ts";
|
|
12
|
+
import { collapseHintText, isToolTuiFullscreen, showMoreHintText } from "./show-more-hint.ts";
|
|
13
|
+
import { stripAnsi, stripBackgroundAnsi, stripLeadingStatusIcon } from "../../utils/ansi-text.ts";
|
|
14
|
+
import { walkComponentTree } from "../../utils/component-tree.ts";
|
|
11
15
|
import {
|
|
12
16
|
fitToolCallSummary,
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
humanizeToolLabel,
|
|
18
|
+
renderToolSummary,
|
|
19
|
+
toolCallSummary,
|
|
15
20
|
type ToolCallSummary,
|
|
16
|
-
} from "./
|
|
17
|
-
import {
|
|
21
|
+
} from "./names.ts";
|
|
22
|
+
import { toolViewportWidth } from "./result.ts";
|
|
23
|
+
import {
|
|
24
|
+
patchRegistry,
|
|
25
|
+
TOOL_GROUPING_GENERATION_KEY as GENERATION_KEY,
|
|
26
|
+
TOOL_GROUPING_PARENT_KEY as PARENT_KEY,
|
|
27
|
+
TOOL_GROUPING_PATCH_KEY as PATCH_KEY,
|
|
28
|
+
} from "../../utils/patch-keys.ts";
|
|
18
29
|
|
|
19
|
-
const PATCH_KEY = Symbol.for("pi.ccstyle.tool-grouping-patch");
|
|
20
|
-
const PARENT_KEY = Symbol.for("pi.ccstyle.tool-grouping-parent");
|
|
21
|
-
const GENERATION_KEY = Symbol.for("pi.ccstyle.tool-grouping-generation");
|
|
22
30
|
const NON_GROUPABLE = new Set(["edit", "write", "apply_patch"]);
|
|
23
31
|
|
|
24
32
|
type Patch = {
|
|
@@ -39,7 +47,20 @@ function toolName(tool: any): string {
|
|
|
39
47
|
return String(tool?.toolName ?? tool?.toolDefinition?.name ?? "tool");
|
|
40
48
|
}
|
|
41
49
|
|
|
42
|
-
|
|
50
|
+
/**
|
|
51
|
+
* 标题:MCP 工具用 adapter 暴露的真实工具名,其余回退工具名人性化。
|
|
52
|
+
*/
|
|
53
|
+
function toolTitle(tool: any): string {
|
|
54
|
+
const name = toolName(tool);
|
|
55
|
+
return (
|
|
56
|
+
mcpToolTitle({
|
|
57
|
+
toolName: name,
|
|
58
|
+
definition: tool?.toolDefinition ?? tool?.builtInToolDefinition,
|
|
59
|
+
}) ?? humanizeToolLabel(name)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isGroupable(value: unknown): boolean {
|
|
43
64
|
return value instanceof ToolExecutionComponent && !NON_GROUPABLE.has(toolName(value));
|
|
44
65
|
}
|
|
45
66
|
|
|
@@ -85,37 +106,42 @@ function scheduleGroupAnimation(patch: Patch): void {
|
|
|
85
106
|
patch.animationTimer = setTimeout(() => {
|
|
86
107
|
patch.animationTimer = null;
|
|
87
108
|
if (!patch.active) return;
|
|
109
|
+
let needsRender = false;
|
|
88
110
|
for (const group of patch.groups) {
|
|
89
111
|
if (
|
|
90
112
|
(group.children as any[]).some(
|
|
91
113
|
(tool) => tool?.executionStarted && status(tool) === "pending",
|
|
92
114
|
)
|
|
93
|
-
)
|
|
115
|
+
) {
|
|
94
116
|
group.invalidate();
|
|
117
|
+
// 分组卡不是 ToolExecutionComponent:它的 invalidate() 不会请求渲染,
|
|
118
|
+
// 不补这一步 spinner 只在别人渲染时才跳帧(并行工具下表现为卡顿/冻结)。
|
|
119
|
+
needsRender = true;
|
|
120
|
+
}
|
|
95
121
|
}
|
|
122
|
+
if (needsRender) requestAnimationRender(patch);
|
|
96
123
|
}, TOOL_LOADING_INTERVAL_MS);
|
|
97
124
|
patch.animationTimer.unref?.();
|
|
98
125
|
}
|
|
99
126
|
|
|
100
|
-
|
|
101
|
-
|
|
127
|
+
/** 借用子工具卡的 ui 请求一帧;子卡缺失时回退到扩展持有的 TUI 槽。 */
|
|
128
|
+
function requestAnimationRender(patch: Patch): void {
|
|
129
|
+
for (const group of patch.groups) {
|
|
130
|
+
const ui = (group.children as any[]).find(
|
|
131
|
+
(tool) => typeof tool?.ui?.requestRender === "function",
|
|
132
|
+
)?.ui;
|
|
133
|
+
if (ui) {
|
|
134
|
+
ui.requestRender();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
getToolMouseTui()?.requestRender?.();
|
|
102
139
|
}
|
|
103
140
|
|
|
104
141
|
function visibleLines(lines: string[]): string[] {
|
|
105
142
|
return lines.filter((line) => stripAnsi(line).trim());
|
|
106
143
|
}
|
|
107
144
|
|
|
108
|
-
function stripLeadingStatusIcon(line: string): string {
|
|
109
|
-
return line.replace(
|
|
110
|
-
/^((?:\x1b\[[0-9;]*m|[ \t]|[├└│─])*)(?:\x1b\[[0-9;]*m)*(?:[✓✗●○■⬤•·⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏])(?:\x1b\[[0-9;]*m)*\s+/,
|
|
111
|
-
"$1",
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export function stripBackgroundAnsi(line: string): string {
|
|
116
|
-
return line.replace(/\x1b\[(?:4[0-9]|10[0-7]|48(?:(?:;|:)[0-9]+)+|49)m/g, "");
|
|
117
|
-
}
|
|
118
|
-
|
|
119
145
|
function stripLeadingSpaces(line: string, count: number): string {
|
|
120
146
|
let offset = 0;
|
|
121
147
|
let removed = 0;
|
|
@@ -138,10 +164,6 @@ function stripLeadingSpaces(line: string, count: number): string {
|
|
|
138
164
|
}
|
|
139
165
|
|
|
140
166
|
/** 生成一行铺满 width 的 slot 背景行;bgAnsiOverride 可替换背景 ANSI(用于提亮等)。 */
|
|
141
|
-
function panelInnerWidth(width: number): number {
|
|
142
|
-
return Math.max(0, Math.floor(width) - 2);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
167
|
export function paddedBackgroundRow(
|
|
146
168
|
theme: any,
|
|
147
169
|
slot: string,
|
|
@@ -149,12 +171,9 @@ export function paddedBackgroundRow(
|
|
|
149
171
|
width: number,
|
|
150
172
|
bgAnsiOverride?: string,
|
|
151
173
|
): string {
|
|
152
|
-
const
|
|
153
|
-
const leftPadding = safeWidth > 0 ? " " : "";
|
|
154
|
-
const rightPadding = safeWidth > 1 ? " " : "";
|
|
155
|
-
const innerWidth = panelInnerWidth(width);
|
|
174
|
+
const innerWidth = Math.max(0, width - 2);
|
|
156
175
|
const clipped = truncateToWidth(stripBackgroundAnsi(content), innerWidth, "");
|
|
157
|
-
const row =
|
|
176
|
+
const row = ` ${clipped}${" ".repeat(Math.max(0, innerWidth - visibleWidth(clipped)))} `;
|
|
158
177
|
const bgAnsi =
|
|
159
178
|
bgAnsiOverride ||
|
|
160
179
|
(typeof theme?.bg === "function"
|
|
@@ -165,113 +184,12 @@ export function paddedBackgroundRow(
|
|
|
165
184
|
return `${bgAnsi}${stable}\x1b[49m`;
|
|
166
185
|
}
|
|
167
186
|
|
|
168
|
-
function oneLine(value: unknown): string {
|
|
169
|
-
return sanitizeToolResultText(String(value ?? ""), 4096)
|
|
170
|
-
.replace(/\s+/g, " ")
|
|
171
|
-
.trim();
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function humanizeToolName(name: string): string {
|
|
175
|
-
return name
|
|
176
|
-
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
177
|
-
.replace(/[_-]+/g, " ")
|
|
178
|
-
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
179
|
-
}
|
|
180
|
-
|
|
181
187
|
function toolSummary(tool: any): ToolCallSummary {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const found = keys.map((key) => args[key]).find((item) => typeof item === "string" && item);
|
|
188
|
-
return `${titled} ${oneLine(found || fallback)}`;
|
|
189
|
-
};
|
|
190
|
-
if (lowerName === "agent" || lowerName === "agents") {
|
|
191
|
-
const displayName = args.subagent_type ?? args.agent_type ?? args.agent;
|
|
192
|
-
if (typeof displayName === "string" && displayName) {
|
|
193
|
-
return { main: `${titled} ${displayName}`, detail: "" };
|
|
194
|
-
}
|
|
195
|
-
return {
|
|
196
|
-
main: value(
|
|
197
|
-
lowerName === "agent" ? "launch agent" : "launch agents",
|
|
198
|
-
"description",
|
|
199
|
-
"prompt",
|
|
200
|
-
),
|
|
201
|
-
detail: "",
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
if (lowerName === "get_subagent_result" || lowerName === "steer_subagent") {
|
|
205
|
-
return {
|
|
206
|
-
main: value(lowerName === "get_subagent_result" ? "agent result" : "steer agent", "agent_id"),
|
|
207
|
-
detail: "",
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
if (lowerName === "skill") return { main: value("run skill", "name"), detail: "" };
|
|
211
|
-
if (lowerName === "enterplanmode" || lowerName === "enter_plan_mode") {
|
|
212
|
-
return { main: `${titled} enable read-only planning`, detail: "" };
|
|
213
|
-
}
|
|
214
|
-
if (lowerName === "exitplanmode" || lowerName === "exit_plan_mode") {
|
|
215
|
-
return { main: `${titled} present plan`, detail: "" };
|
|
216
|
-
}
|
|
217
|
-
if (lowerName === "taskcreate") return { main: value("create task", "subject"), detail: "" };
|
|
218
|
-
if (lowerName === "tasklist") return { main: `${titled} task list`, detail: "" };
|
|
219
|
-
if (lowerName === "taskget" || lowerName === "taskupdate") {
|
|
220
|
-
return { main: value("task", "taskId", "task_id"), detail: "" };
|
|
221
|
-
}
|
|
222
|
-
if (lowerName === "taskoutput" || lowerName === "taskstop") {
|
|
223
|
-
return { main: value("background task", "task_id", "taskId"), detail: "" };
|
|
224
|
-
}
|
|
225
|
-
if (lowerName === "taskexecute") {
|
|
226
|
-
const ids = Array.isArray(args.task_ids)
|
|
227
|
-
? args.task_ids
|
|
228
|
-
: Array.isArray(args.taskIds)
|
|
229
|
-
? args.taskIds
|
|
230
|
-
: [];
|
|
231
|
-
return {
|
|
232
|
-
main: `${titled} ${ids.length ? `${ids[0]}${ids.length > 1 ? ` (+${ids.length - 1} tasks)` : ""}` : "start tasks"}`,
|
|
233
|
-
detail: "",
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
if (name === "read") {
|
|
237
|
-
const details = [
|
|
238
|
-
args.offset !== undefined ? `offset=${args.offset}` : "",
|
|
239
|
-
args.limit !== undefined ? `limit=${args.limit}` : "",
|
|
240
|
-
].filter(Boolean);
|
|
241
|
-
const detail = details.length ? ` (${details.join(", ")})` : "";
|
|
242
|
-
if (typeof args.path === "string" && args.path) {
|
|
243
|
-
return pathSummary("Read", args.path, tool?.cwd, detail);
|
|
244
|
-
}
|
|
245
|
-
return { main: "Read ...", detail };
|
|
246
|
-
}
|
|
247
|
-
if (name === "bash") return { main: `Bash ${oneLine(args.command || "...")}`, detail: "" };
|
|
248
|
-
if (name === "grep") {
|
|
249
|
-
const pattern = oneLine(args.pattern || "...");
|
|
250
|
-
return {
|
|
251
|
-
main: `Grep ${JSON.stringify(pattern)}${args.path ? ` in ${oneLine(args.path)}` : ""}`,
|
|
252
|
-
detail: "",
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
if (name === "find") {
|
|
256
|
-
const pattern = oneLine(args.pattern || "...");
|
|
257
|
-
return {
|
|
258
|
-
main: `Find ${JSON.stringify(pattern)}${args.path ? ` in ${oneLine(args.path)}` : ""}`,
|
|
259
|
-
detail: "",
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
const preferred =
|
|
263
|
-
args.agent_id ??
|
|
264
|
-
args.path ??
|
|
265
|
-
args.file_path ??
|
|
266
|
-
args.url ??
|
|
267
|
-
args.description ??
|
|
268
|
-
args.query ??
|
|
269
|
-
args.name ??
|
|
270
|
-
args.prompt;
|
|
271
|
-
return {
|
|
272
|
-
main: `${humanizeToolName(name)}${preferred === undefined ? "" : ` ${oneLine(preferred)}`}`,
|
|
273
|
-
detail: "",
|
|
274
|
-
};
|
|
188
|
+
return toolCallSummary(toolName(tool), tool?.args ?? {}, {
|
|
189
|
+
title: toolTitle(tool),
|
|
190
|
+
variant: "grouping",
|
|
191
|
+
cwd: tool?.cwd,
|
|
192
|
+
});
|
|
275
193
|
}
|
|
276
194
|
|
|
277
195
|
function toolNameList(tools: any[]): string {
|
|
@@ -282,10 +200,6 @@ function toolNameList(tools: any[]): string {
|
|
|
282
200
|
|
|
283
201
|
let nextGroupId = 1;
|
|
284
202
|
|
|
285
|
-
function groupChildWidth(width: number): number {
|
|
286
|
-
return Math.max(1, panelInnerWidth(width));
|
|
287
|
-
}
|
|
288
|
-
|
|
289
203
|
type SettledGroupCache = {
|
|
290
204
|
width: number;
|
|
291
205
|
hover: boolean;
|
|
@@ -297,6 +211,15 @@ type SettledGroupCache = {
|
|
|
297
211
|
lines: string[];
|
|
298
212
|
};
|
|
299
213
|
|
|
214
|
+
type ExpandedGroupCache = {
|
|
215
|
+
width: number;
|
|
216
|
+
hover: boolean;
|
|
217
|
+
theme: unknown;
|
|
218
|
+
fullscreen: boolean;
|
|
219
|
+
paints: readonly unknown[];
|
|
220
|
+
lines: string[];
|
|
221
|
+
};
|
|
222
|
+
|
|
300
223
|
export class ToolGroupComponent extends Container {
|
|
301
224
|
readonly toolCallId = `ccstyle-tool-group-${nextGroupId++}`;
|
|
302
225
|
readonly toolName = "Tool group";
|
|
@@ -307,8 +230,10 @@ export class ToolGroupComponent extends Container {
|
|
|
307
230
|
}
|
|
308
231
|
private hintHovered = false;
|
|
309
232
|
private readonly patch: Patch;
|
|
310
|
-
/** 仅缓存已完成且折叠的分组;pending
|
|
233
|
+
/** 仅缓存已完成且折叠的分组;pending 每帧现算。 */
|
|
311
234
|
private settledCache: SettledGroupCache | undefined;
|
|
235
|
+
/** 展开分组:子工具 paint 引用未变则复用整卡行。 */
|
|
236
|
+
private expandedPaintCache: ExpandedGroupCache | undefined;
|
|
312
237
|
|
|
313
238
|
constructor(patch: Patch) {
|
|
314
239
|
super();
|
|
@@ -316,14 +241,19 @@ export class ToolGroupComponent extends Container {
|
|
|
316
241
|
patch.groups.add(this);
|
|
317
242
|
}
|
|
318
243
|
|
|
319
|
-
|
|
244
|
+
private clearPaintCache(): void {
|
|
320
245
|
this.settledCache = undefined;
|
|
246
|
+
this.expandedPaintCache = undefined;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
addTool(tool: any): void {
|
|
250
|
+
this.clearPaintCache();
|
|
321
251
|
this.children.push(tool);
|
|
322
252
|
tool[PARENT_KEY] = this;
|
|
323
253
|
}
|
|
324
254
|
|
|
325
255
|
releaseTools(): any[] {
|
|
326
|
-
this.
|
|
256
|
+
this.clearPaintCache();
|
|
327
257
|
const tools = [...this.children];
|
|
328
258
|
this.children.length = 0;
|
|
329
259
|
this.patch.groups.delete(this);
|
|
@@ -331,27 +261,27 @@ export class ToolGroupComponent extends Container {
|
|
|
331
261
|
}
|
|
332
262
|
|
|
333
263
|
removeTool(tool: any): void {
|
|
334
|
-
this.
|
|
264
|
+
this.clearPaintCache();
|
|
335
265
|
const index = this.children.indexOf(tool);
|
|
336
266
|
if (index >= 0) this.children.splice(index, 1);
|
|
337
267
|
if (tool?.[PARENT_KEY] === this) delete tool[PARENT_KEY];
|
|
338
268
|
}
|
|
339
269
|
|
|
340
270
|
setExpanded(expanded: boolean): void {
|
|
341
|
-
if (this._expanded !== expanded) this.
|
|
271
|
+
if (this._expanded !== expanded) this.clearPaintCache();
|
|
342
272
|
this._expanded = expanded;
|
|
343
273
|
for (const tool of this.children)
|
|
344
274
|
(tool as Component & { setExpanded?: (expanded: boolean) => void }).setExpanded?.(expanded);
|
|
345
275
|
}
|
|
346
276
|
|
|
347
277
|
setHintHovered(hovered: boolean): void {
|
|
348
|
-
if (this.hintHovered !== hovered) this.
|
|
278
|
+
if (this.hintHovered !== hovered) this.clearPaintCache();
|
|
349
279
|
this.hintHovered = hovered;
|
|
350
280
|
}
|
|
351
281
|
|
|
352
282
|
/**
|
|
353
283
|
* 展开时按局部行定位内部组件(null = 行属于 group 自身:空行/头行/尾行)。
|
|
354
|
-
* 行数计算与 render
|
|
284
|
+
* 行数计算与 render 保持一致:宽度 width-2 + 空行过滤。
|
|
355
285
|
*/
|
|
356
286
|
childAtRow(localRow: number, width: number): { component: any; row: number } | null {
|
|
357
287
|
if (!this._expanded || localRow < 2) return null;
|
|
@@ -359,7 +289,7 @@ export class ToolGroupComponent extends Container {
|
|
|
359
289
|
for (const tool of this.children) {
|
|
360
290
|
let lines: string[] = [];
|
|
361
291
|
try {
|
|
362
|
-
const rendered = tool.render?.(
|
|
292
|
+
const rendered = tool.render?.(Math.max(1, width - 2));
|
|
363
293
|
if (Array.isArray(rendered)) lines = visibleLines(rendered.map((line) => String(line)));
|
|
364
294
|
} catch {
|
|
365
295
|
lines = [];
|
|
@@ -374,7 +304,7 @@ export class ToolGroupComponent extends Container {
|
|
|
374
304
|
}
|
|
375
305
|
|
|
376
306
|
invalidate(): void {
|
|
377
|
-
this.
|
|
307
|
+
this.clearPaintCache();
|
|
378
308
|
for (const tool of this.children) tool.invalidate?.();
|
|
379
309
|
}
|
|
380
310
|
|
|
@@ -434,8 +364,7 @@ export class ToolGroupComponent extends Container {
|
|
|
434
364
|
})
|
|
435
365
|
.join(` ${fg("dim", "•")} `);
|
|
436
366
|
const names = new Set(this.children.map(toolName));
|
|
437
|
-
const label =
|
|
438
|
-
names.size === 1 ? humanizeToolName(toolName(this.children[0])) : "Multiple Tools";
|
|
367
|
+
const label = names.size === 1 ? toolTitle(this.children[0]) : "Multiple Tools";
|
|
439
368
|
const overall: ToolStatus = counts.error ? "error" : counts.pending ? "pending" : "success";
|
|
440
369
|
if (
|
|
441
370
|
(this.children as any[]).some((tool) => tool?.executionStarted && status(tool) === "pending")
|
|
@@ -444,16 +373,42 @@ export class ToolGroupComponent extends Container {
|
|
|
444
373
|
const overallColor = overall === "pending" ? "accent" : overall;
|
|
445
374
|
const nameList = names.size > 1 ? ` ${fg("dim", `• ${toolNameList(this.children)}`)}` : "";
|
|
446
375
|
// 圆点保持 dim;hover 只高亮可点击文字。
|
|
447
|
-
const
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
376
|
+
const hintText = this._expanded ? collapseHintText() : showMoreHintText();
|
|
377
|
+
const hint = `${fg("dim", "•")} ${fg(this.hintHovered ? "text" : "dim", hintText)}`;
|
|
378
|
+
// 行尾提示承载折叠分组的可点击区(collapsedHintHitbox 依赖行尾短语)。
|
|
379
|
+
// 空间不足时先省略工具名列表,再截断计数尾部,提示最后让位;
|
|
380
|
+
// 极窄视口连“圆点+标题+计数”都放不下时才回退整行截断。
|
|
381
|
+
const viewport = toolViewportWidth(width);
|
|
382
|
+
const head = ` ${fg(overallColor, "●")} ${label}: ${countText}`;
|
|
383
|
+
const hintWidth = visibleWidth(` ${hint}`);
|
|
384
|
+
const nameBudget = viewport - visibleWidth(head) - hintWidth;
|
|
385
|
+
const elidedName =
|
|
386
|
+
nameList && nameBudget < visibleWidth(nameList)
|
|
387
|
+
? nameBudget >= 4
|
|
388
|
+
? ` ${fg("dim", "…")}`
|
|
389
|
+
: ""
|
|
390
|
+
: nameList;
|
|
391
|
+
const headBudget = viewport - hintWidth - visibleWidth(elidedName);
|
|
392
|
+
const titleLine = `${visibleWidth(head) <= headBudget ? head : truncateToWidth(head, headBudget, "…")}${elidedName} ${hint}`;
|
|
393
|
+
const lines = ["", truncateToWidth(titleLine, viewport, "…")];
|
|
456
394
|
const total = this.children.length;
|
|
395
|
+
const childPaints = this._expanded
|
|
396
|
+
? (this.children as any[]).map((tool) => tool.render?.(Math.max(1, width - 2)))
|
|
397
|
+
: undefined;
|
|
398
|
+
if (this._expanded && childPaints) {
|
|
399
|
+
const expandedHit = this.expandedPaintCache;
|
|
400
|
+
if (
|
|
401
|
+
expandedHit &&
|
|
402
|
+
expandedHit.width === width &&
|
|
403
|
+
expandedHit.hover === this.hintHovered &&
|
|
404
|
+
expandedHit.theme === this.patch.theme &&
|
|
405
|
+
expandedHit.fullscreen === isToolTuiFullscreen() &&
|
|
406
|
+
expandedHit.paints.length === childPaints.length &&
|
|
407
|
+
expandedHit.paints.every((paint, index) => paint === childPaints[index])
|
|
408
|
+
) {
|
|
409
|
+
return expandedHit.lines;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
457
412
|
const expandedLines: string[] = [];
|
|
458
413
|
for (let index = 0; index < total; index++) {
|
|
459
414
|
const tool = this.children[index];
|
|
@@ -465,17 +420,19 @@ export class ToolGroupComponent extends Container {
|
|
|
465
420
|
const summary = toolSummary(tool);
|
|
466
421
|
const prefix = ` ${fg("dim", branch)} ${fg(color, statusIcon(toolStatus))} `;
|
|
467
422
|
const detail = fg("dim", summary.detail);
|
|
468
|
-
|
|
423
|
+
// 与单工具卡标题同宽,宽屏右侧留白一致
|
|
424
|
+
const rowWidth = toolViewportWidth(width);
|
|
425
|
+
const mainWidth = Math.max(0, rowWidth - visibleWidth(prefix) - visibleWidth(detail));
|
|
469
426
|
lines.push(
|
|
470
427
|
truncateToWidth(
|
|
471
|
-
`${prefix}${
|
|
472
|
-
|
|
428
|
+
`${prefix}${renderToolSummary(summary, mainWidth, fg)}${detail}`,
|
|
429
|
+
rowWidth,
|
|
473
430
|
"",
|
|
474
431
|
),
|
|
475
432
|
);
|
|
476
433
|
continue;
|
|
477
434
|
}
|
|
478
|
-
const rendered = visibleLines(
|
|
435
|
+
const rendered = visibleLines(Array.isArray(childPaints?.[index]) ? childPaints[index] : []);
|
|
479
436
|
if (rendered.length) {
|
|
480
437
|
rendered[0] = stripLeadingStatusIcon(rendered[0])
|
|
481
438
|
.replace(/^ +/, "")
|
|
@@ -484,7 +441,8 @@ export class ToolGroupComponent extends Container {
|
|
|
484
441
|
const childLines = rendered.length ? rendered : [toolSummary(tool).main];
|
|
485
442
|
for (let lineIndex = 0; lineIndex < childLines.length; lineIndex++) {
|
|
486
443
|
const content =
|
|
487
|
-
|
|
444
|
+
// 续行只剥外层 Box 的 1 格 left pad,保留 Input/Output 相对缩进
|
|
445
|
+
lineIndex === 0 ? childLines[lineIndex] : stripLeadingSpaces(childLines[lineIndex], 1);
|
|
488
446
|
const prefix =
|
|
489
447
|
lineIndex === 0
|
|
490
448
|
? `${fg("dim", branch)} ${fg(color, statusIcon(toolStatus))} `
|
|
@@ -499,6 +457,14 @@ export class ToolGroupComponent extends Container {
|
|
|
499
457
|
lines.push(paddedBackgroundRow(theme, backgroundSlot, line, width));
|
|
500
458
|
}
|
|
501
459
|
lines.push(paddedBackgroundRow(theme, backgroundSlot, "", width));
|
|
460
|
+
this.expandedPaintCache = {
|
|
461
|
+
width,
|
|
462
|
+
hover: this.hintHovered,
|
|
463
|
+
theme: this.patch.theme,
|
|
464
|
+
fullscreen: isToolTuiFullscreen(),
|
|
465
|
+
paints: childPaints ?? [],
|
|
466
|
+
lines,
|
|
467
|
+
};
|
|
502
468
|
} else if (counts.pending === 0) {
|
|
503
469
|
this.storeSettledCache(width, lines);
|
|
504
470
|
}
|
|
@@ -525,7 +491,7 @@ function ungroup(patch: Patch): void {
|
|
|
525
491
|
}
|
|
526
492
|
}
|
|
527
493
|
|
|
528
|
-
function normalizeGroup(group: ToolGroupComponent): void {
|
|
494
|
+
function normalizeGroup(patch: Patch, group: ToolGroupComponent): void {
|
|
529
495
|
if (group.children.length > 1) return;
|
|
530
496
|
const parent = (group as any)[PARENT_KEY];
|
|
531
497
|
const index = parent?.children?.indexOf(group) ?? -1;
|
|
@@ -574,32 +540,17 @@ function maybeGroup(patch: Patch, parent: any, component: any): void {
|
|
|
574
540
|
/** /reload 不会重新 addChild;扫描当前 mounted roots,把已有工具重新送入同一分组规则。 */
|
|
575
541
|
function regroup(patch: Patch, root: any): void {
|
|
576
542
|
if (!patch.active || !patch.enabled() || !root) return;
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
if (
|
|
580
|
-
seen.add(value);
|
|
581
|
-
if (Array.isArray(value)) {
|
|
582
|
-
for (const child of value) visit(child);
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
543
|
+
walkComponentTree(root, (value: any) => {
|
|
544
|
+
// 分组卡与可分组工具是分组边界:不继续下钻(与原有遍历过滤一致)。
|
|
545
|
+
if (value instanceof ToolGroupComponent || isGroupable(value)) return false;
|
|
585
546
|
const children = value.children;
|
|
586
547
|
if (Array.isArray(children)) {
|
|
587
548
|
for (const child of [...children]) {
|
|
588
549
|
if (child && typeof child === "object") child[PARENT_KEY] = value;
|
|
589
550
|
maybeGroup(patch, value, child);
|
|
590
551
|
}
|
|
591
|
-
for (const child of [...children]) {
|
|
592
|
-
if (!(child instanceof ToolGroupComponent) && !isGroupable(child)) visit(child);
|
|
593
|
-
}
|
|
594
552
|
}
|
|
595
|
-
|
|
596
|
-
const mounted = value.getMountedRoots?.();
|
|
597
|
-
if (Array.isArray(mounted)) visit(mounted);
|
|
598
|
-
} catch {
|
|
599
|
-
// renderer 切换中的惰性 Proxy 可能暂时没有 mounted roots。
|
|
600
|
-
}
|
|
601
|
-
};
|
|
602
|
-
visit(root);
|
|
553
|
+
});
|
|
603
554
|
}
|
|
604
555
|
|
|
605
556
|
export type ToolGroupingHooks = {
|
|
@@ -610,8 +561,7 @@ export type ToolGroupingHooks = {
|
|
|
610
561
|
|
|
611
562
|
export function installToolGrouping(getEnabled: () => boolean): ToolGroupingHooks {
|
|
612
563
|
const prototype = Container.prototype as any;
|
|
613
|
-
const
|
|
614
|
-
const previous = host[PATCH_KEY] as Patch | undefined;
|
|
564
|
+
const previous = patchRegistry.get<Patch>(PATCH_KEY);
|
|
615
565
|
if (previous) {
|
|
616
566
|
previous.active = false;
|
|
617
567
|
previous.enabled = () => false;
|
|
@@ -656,12 +606,12 @@ export function installToolGrouping(getEnabled: () => boolean): ToolGroupingHook
|
|
|
656
606
|
const group = component?.[PARENT_KEY];
|
|
657
607
|
if (group instanceof ToolGroupComponent && (group as any)[PARENT_KEY] === this) {
|
|
658
608
|
group.removeTool(component);
|
|
659
|
-
normalizeGroup(group);
|
|
609
|
+
normalizeGroup(patch, group);
|
|
660
610
|
return;
|
|
661
611
|
}
|
|
662
612
|
const result = patch.original.removeChild.call(this, component);
|
|
663
613
|
if (component?.[PARENT_KEY] === this) delete component[PARENT_KEY];
|
|
664
|
-
if (this instanceof ToolGroupComponent) normalizeGroup(this);
|
|
614
|
+
if (this instanceof ToolGroupComponent) normalizeGroup(patch, this);
|
|
665
615
|
if (component instanceof ToolGroupComponent) {
|
|
666
616
|
for (const tool of component.releaseTools()) delete tool[PARENT_KEY];
|
|
667
617
|
}
|
|
@@ -681,7 +631,7 @@ export function installToolGrouping(getEnabled: () => boolean): ToolGroupingHook
|
|
|
681
631
|
prototype.addChild = patch.installed.addChild;
|
|
682
632
|
prototype.removeChild = patch.installed.removeChild;
|
|
683
633
|
prototype.clear = patch.installed.clear;
|
|
684
|
-
|
|
634
|
+
patchRegistry.install(PATCH_KEY, patch);
|
|
685
635
|
return {
|
|
686
636
|
setTheme(theme: any) {
|
|
687
637
|
patch.theme = theme;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { humanizeToolLabel } from "./names.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MCP 工具的识别与标题,单卡(default-mode)与分组行/分组头(grouping)共用。
|
|
5
|
+
*
|
|
6
|
+
* - 判定:工具名带 mcp 片段(`mcp` 网关、`mcpScript`、`mcp__<server>`),或 adapter 注册时
|
|
7
|
+
* 给的 label 以 `MCP:` 开头。默认/none/short 前缀模式下直连工具的名字里没有 mcp 片段
|
|
8
|
+
* (`github_search_code`、`search_code`),只能靠 label 判定。
|
|
9
|
+
* - 标题:具体工具用 adapter 暴露的真实工具名,不做二次格式化;只有 `mcp` / `mcpScript`
|
|
10
|
+
* 这两个入口没有具体工具名,跟普通工具一样人性化(MCP / MCP Script)。
|
|
11
|
+
* - 参数摘要与其它工具共用 toolCallSummary 的字段链,网关额外把 `args.tool` 提到标题后。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** 单次 MCP 调用的识别信息。 */
|
|
15
|
+
export type McpToolCall = {
|
|
16
|
+
toolName: string;
|
|
17
|
+
/** 工具定义;默认/none/short 前缀模式要靠 label 判定,会话重放时可能缺失。 */
|
|
18
|
+
definition?: any;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const GATEWAY_TOOL_NAME = "mcp";
|
|
22
|
+
const SCRIPT_TOOL_NAME = "mcpScript";
|
|
23
|
+
|
|
24
|
+
export function isMcpToolDefinition(definition: any, toolName: string): boolean {
|
|
25
|
+
const label = typeof definition?.label === "string" ? definition.label.trim() : "";
|
|
26
|
+
if (/^MCP(?::|$)/i.test(label)) return true;
|
|
27
|
+
if (toolName === GATEWAY_TOOL_NAME || toolName === SCRIPT_TOOL_NAME) return true;
|
|
28
|
+
return /^mcp[_:-]|[_:-]mcp[_:-]/i.test(toolName);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** 标题:MCP 工具返回标题(入口走人性化,其余用真实工具名);非 MCP 返回 undefined。 */
|
|
32
|
+
export function mcpToolTitle(call: McpToolCall): string | undefined {
|
|
33
|
+
const { toolName } = call;
|
|
34
|
+
if (!isMcpToolDefinition(call.definition, toolName)) return undefined;
|
|
35
|
+
if (toolName === GATEWAY_TOOL_NAME || toolName === SCRIPT_TOOL_NAME) {
|
|
36
|
+
return humanizeToolLabel(toolName);
|
|
37
|
+
}
|
|
38
|
+
return toolName;
|
|
39
|
+
}
|