@timurproko/a1 0.1.8-dev.368 → 0.1.8-dev.402
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/dist/composition/owned-ui.d.ts +2 -0
- package/dist/composition/owned-ui.js +28 -11
- package/dist/contracts/owned-ui/image-attachments.d.ts +7 -0
- package/dist/contracts/owned-ui/image-attachments.js +7 -0
- package/dist/foundation/release/dependency-certification.js +65 -7
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +8 -6
- package/dist/integrations/pi/components/owned-editor-ux.js +115 -26
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +4 -0
- package/dist/integrations/pi/components/shell-presenters-transcript.js +15 -5
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +14 -5
- package/dist/integrations/pi/components/tool-image-presentation.d.ts +21 -0
- package/dist/integrations/pi/components/tool-image-presentation.js +86 -0
- package/dist/integrations/pi/components/tool-result-adapter.d.ts +2 -2
- package/dist/integrations/pi/components/transcript-image-resolver.d.ts +7 -0
- package/dist/integrations/pi/components/transcript-image-resolver.js +19 -0
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +3 -1
- package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +61 -0
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +344 -0
- package/dist/integrations/pi/engine/adapter.d.ts +4 -1
- package/dist/integrations/pi/engine/adapter.js +66 -28
- package/dist/integrations/pi/engine/pending-delivery.d.ts +4 -0
- package/dist/integrations/pi/engine/pending-delivery.js +15 -6
- package/dist/integrations/pi/engine/tool-rendering.js +26 -3
- package/dist/integrations/pi/engine/transcript-image-assets.d.ts +11 -0
- package/dist/integrations/pi/engine/transcript-image-assets.js +86 -0
- package/dist/integrations/pi/session-ui/clipboard-diagnostics.d.ts +16 -0
- package/dist/integrations/pi/session-ui/clipboard-diagnostics.js +65 -0
- package/dist/integrations/pi/session-ui/editor-hyperlink-budget.d.ts +9 -0
- package/dist/integrations/pi/session-ui/editor-hyperlink-budget.js +23 -0
- package/dist/integrations/pi/session-ui/index.d.ts +1 -0
- package/dist/integrations/pi/session-ui/index.js +1 -0
- package/dist/integrations/pi/session-ui/paste-executor.d.ts +10 -0
- package/dist/integrations/pi/session-ui/paste-executor.js +232 -0
- package/dist/integrations/pi/session-ui/paste-helper.d.ts +1 -0
- package/dist/integrations/pi/session-ui/paste-helper.js +173 -0
- package/dist/integrations/pi/session-ui/paste-preparation-client.d.ts +16 -0
- package/dist/integrations/pi/session-ui/paste-preparation-client.js +159 -0
- package/dist/integrations/pi/session-ui/paste-protocol.d.ts +78 -0
- package/dist/integrations/pi/session-ui/paste-protocol.js +18 -0
- package/dist/integrations/pi/session-ui/paste-text-preparation.d.ts +20 -0
- package/dist/integrations/pi/session-ui/paste-text-preparation.js +134 -0
- package/dist/integrations/pi/session-ui/paste-text-worker.d.ts +1 -0
- package/dist/integrations/pi/session-ui/paste-text-worker.js +12 -0
- package/dist/integrations/pi/session-ui/path-chip-presentation.d.ts +7 -0
- package/dist/integrations/pi/session-ui/path-chip-presentation.js +24 -0
- package/dist/integrations/pi/session-ui/prompt-chips.d.ts +8 -1
- package/dist/integrations/pi/session-ui/prompt-chips.js +129 -149
- package/dist/integrations/pi/session-ui/response-copy-coordinator.d.ts +27 -0
- package/dist/integrations/pi/session-ui/response-copy-coordinator.js +189 -0
- package/dist/integrations/pi/session-ui/response-copy-helper.d.ts +1 -0
- package/dist/integrations/pi/session-ui/response-copy-helper.js +122 -0
- package/dist/integrations/pi/session-ui/response-copy-protocol.d.ts +56 -0
- package/dist/integrations/pi/session-ui/response-copy-protocol.js +5 -0
- package/dist/integrations/pi/session-ui/response-copy-transport.d.ts +31 -0
- package/dist/integrations/pi/session-ui/response-copy-transport.js +187 -0
- package/dist/integrations/pi/session-ui/session-shell-root.d.ts +15 -6
- package/dist/integrations/pi/session-ui/session-shell-root.js +25 -6
- package/dist/integrations/pi/session-ui/session-shell.js +100 -28
- package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +2 -2
- package/dist/integrations/pi/session-ui/session-viewport-controller.js +4 -3
- package/dist/integrations/pi/session-ui/system-clipboard.d.ts +2 -2
- package/dist/integrations/pi/session-ui/system-clipboard.js +26 -11
- package/dist/integrations/pi/tui-runtime/adapter.js +3 -1
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/product-identity.d.ts +1 -1
- package/dist/product-identity.js +1 -1
- package/dist/product-identity.json +1 -0
- package/dist/ui/components/index.d.ts +1 -0
- package/dist/ui/components/index.js +1 -0
- package/dist/ui/components/selection-copy.d.ts +24 -0
- package/dist/ui/components/selection-copy.js +39 -0
- package/dist/ui/components/spans.d.ts +5 -2
- package/dist/ui/components/spans.js +12 -3
- package/dist/ui/components/text-selection.js +2 -0
- package/dist/ui/components/text.js +1 -1
- package/dist/ui/components/transcript-viewport.d.ts +5 -0
- package/dist/ui/components/transcript-viewport.js +100 -23
- package/dist/ui/components/visible-hyperlinks.js +40 -8
- package/docs/architecture/response-copy-delivery.md +83 -0
- package/docs/architecture/tool-image-presentation.md +62 -0
- package/docs/ci-release-runbook.md +19 -7
- package/docs/openspec-archive-automation.md +124 -0
- package/package.json +2 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { convertToPng } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { getCapabilities, Image, Text } from "#pi-tui";
|
|
3
|
+
import { piTheme } from "./theme.js";
|
|
4
|
+
const MAX_BYTES = 20 * 1024 * 1024;
|
|
5
|
+
const MAX_IMAGES = 16;
|
|
6
|
+
const bytes = (data) => Math.floor(data.length * 3 / 4) - (data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0);
|
|
7
|
+
/**
|
|
8
|
+
* Own only current attachment presentation; tool renderers still receive original result content.
|
|
9
|
+
* Unlike pinned 0.84.2's index cache, entries follow immutable source bytes and stale completions
|
|
10
|
+
* cannot republish. At most one conversion runs per mount; queued work is only the current set.
|
|
11
|
+
*/
|
|
12
|
+
export class ToolImagePresentation {
|
|
13
|
+
changed;
|
|
14
|
+
convert;
|
|
15
|
+
#entries = [];
|
|
16
|
+
#visible = false;
|
|
17
|
+
#disposed = false;
|
|
18
|
+
#active = false;
|
|
19
|
+
constructor(changed, convert = convertToPng) {
|
|
20
|
+
this.changed = changed;
|
|
21
|
+
this.convert = convert;
|
|
22
|
+
}
|
|
23
|
+
update(content, show) {
|
|
24
|
+
if (this.#disposed)
|
|
25
|
+
return;
|
|
26
|
+
const previous = this.#entries;
|
|
27
|
+
const next = [];
|
|
28
|
+
for (const part of content) {
|
|
29
|
+
if (part.type !== "image" || !part.data || !part.mimeType)
|
|
30
|
+
continue;
|
|
31
|
+
const source = { data: part.data, mimeType: part.mimeType };
|
|
32
|
+
const same = (entry) => entry.source.data === source.data && entry.source.mimeType === source.mimeType;
|
|
33
|
+
next.push(next.find(same) ?? previous.find(same) ?? { source, state: "waiting" });
|
|
34
|
+
if (next.length === MAX_IMAGES)
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
this.#entries = next;
|
|
38
|
+
this.#visible = show && getCapabilities().images !== null;
|
|
39
|
+
this.#pump();
|
|
40
|
+
}
|
|
41
|
+
/** Public Image remains responsible for Kitty/iTerm protocol and dimension-aware fallback. */
|
|
42
|
+
components(width) {
|
|
43
|
+
if (this.#disposed || !this.#visible)
|
|
44
|
+
return [];
|
|
45
|
+
return this.#entries.map(entry => {
|
|
46
|
+
const source = entry.source;
|
|
47
|
+
if (getCapabilities().images === "kitty" && source.mimeType !== "image/png") {
|
|
48
|
+
if (entry.converted === undefined)
|
|
49
|
+
return new Text(piTheme().fg("muted", entry.state === "unavailable" ? `[Image unavailable: ${source.mimeType}]` : `[Image converting: ${source.mimeType}]`), 1, 0);
|
|
50
|
+
}
|
|
51
|
+
const image = getCapabilities().images === "kitty" ? entry.converted ?? source : source;
|
|
52
|
+
return new Image(image.data, image.mimeType, { fallbackColor: text => piTheme().fg("toolOutput", text) }, { maxWidthCells: width });
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
dispose() {
|
|
56
|
+
this.#disposed = true;
|
|
57
|
+
this.#entries = [];
|
|
58
|
+
}
|
|
59
|
+
#pump() {
|
|
60
|
+
if (this.#disposed || this.#active || !this.#visible || getCapabilities().images !== "kitty")
|
|
61
|
+
return;
|
|
62
|
+
const entry = this.#entries.find(value => value.source.mimeType !== "image/png" && value.state === "waiting");
|
|
63
|
+
if (entry === undefined)
|
|
64
|
+
return;
|
|
65
|
+
entry.state = "converting";
|
|
66
|
+
this.#active = true;
|
|
67
|
+
// Concurrency: defer codec entry until the caller's synchronous component mutation finishes.
|
|
68
|
+
void Promise.resolve().then(() => this.#disposed || !this.#entries.includes(entry)
|
|
69
|
+
? null : this.convert(entry.source.data, entry.source.mimeType)).catch(() => null).then(converted => {
|
|
70
|
+
this.#active = false;
|
|
71
|
+
if (this.#disposed)
|
|
72
|
+
return;
|
|
73
|
+
if (this.#entries.includes(entry)) {
|
|
74
|
+
// Invariant: limits match the existing 16 references / 20 MiB per image, not a new aggregate cap.
|
|
75
|
+
if (converted !== null && converted.mimeType === "image/png" && bytes(converted.data) <= MAX_BYTES) {
|
|
76
|
+
entry.converted = converted;
|
|
77
|
+
entry.state = "ready";
|
|
78
|
+
}
|
|
79
|
+
else
|
|
80
|
+
entry.state = "unavailable";
|
|
81
|
+
this.changed();
|
|
82
|
+
}
|
|
83
|
+
this.#pump();
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ToolExecutionComponent } from "
|
|
1
|
+
import type { ToolExecutionComponent } from "./upstream/components/tool-execution.js";
|
|
2
2
|
import type { OwnedUiTranscriptBlock } from "../../../contracts/owned-ui/index.js";
|
|
3
3
|
import { type PiShellImageAssetResolver } from "./shell-shared-facade.js";
|
|
4
4
|
/** Reconstruct public Pi inputs once at the component boundary, never from diagnostic summaries. */
|
|
5
|
-
export declare function updatePiToolResult(component: ToolExecutionComponent, block: OwnedUiTranscriptBlock, assets?: PiShellImageAssetResolver): void;
|
|
5
|
+
export declare function updatePiToolResult(component: Pick<ToolExecutionComponent, "updateResult">, block: OwnedUiTranscriptBlock, assets?: PiShellImageAssetResolver): void;
|
|
6
6
|
export declare function piToolArguments(block: OwnedUiTranscriptBlock): unknown;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { OwnedUiImageAttachment, OwnedUiTranscriptBlock } from "../../../contracts/owned-ui/index.js";
|
|
2
|
+
import type { PiShellImageAssetResolver } from "./shell-shared-facade.js";
|
|
3
|
+
export declare function createTranscriptImageResolver(source: PiShellImageAssetResolver | undefined, initial: OwnedUiTranscriptBlock): {
|
|
4
|
+
resolve: (id: string) => OwnedUiImageAttachment | null;
|
|
5
|
+
update: (block: OwnedUiTranscriptBlock) => void;
|
|
6
|
+
dispose: () => void;
|
|
7
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Concurrency: a mounted block can outlive its engine revision while delivery is pending; retain its immutable assets through rebuilds.
|
|
2
|
+
export function createTranscriptImageResolver(source, initial) {
|
|
3
|
+
let images = new Map();
|
|
4
|
+
const update = (block) => {
|
|
5
|
+
const next = new Map();
|
|
6
|
+
for (const reference of block.imageReferences ?? []) {
|
|
7
|
+
const asset = source?.resolve(reference.assetId) ?? images.get(reference.assetId);
|
|
8
|
+
if (asset != null)
|
|
9
|
+
next.set(reference.assetId, asset);
|
|
10
|
+
}
|
|
11
|
+
images = next;
|
|
12
|
+
};
|
|
13
|
+
update(initial);
|
|
14
|
+
return {
|
|
15
|
+
resolve: (id) => images.get(id) ?? null,
|
|
16
|
+
update,
|
|
17
|
+
dispose: () => images.clear(),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Source-synchronized from Pi 0.84.2
|
|
3
3
|
* packages/coding-agent/src/modes/interactive/components/scoped-models-selector.ts
|
|
4
|
-
* Mechanical changes: public DynamicBorder
|
|
4
|
+
* Mechanical changes: public DynamicBorder import, local platform-aware key labels, and owned theme adapter.
|
|
5
5
|
*/
|
|
6
6
|
interface ScopedModel {
|
|
7
7
|
readonly provider: string;
|
|
@@ -3,7 +3,9 @@ import { DynamicBorder } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
import { getModelSearchText } from "../model-search.js";
|
|
4
4
|
import { piTheme } from "../theme/theme.js";
|
|
5
5
|
function keyText(keybinding) {
|
|
6
|
-
return getKeybindings().getKeys(keybinding)
|
|
6
|
+
return getKeybindings().getKeys(keybinding)
|
|
7
|
+
.map((key) => key.split("+").map((part) => process.platform === "darwin" && part.toLowerCase() === "alt" ? "option" : part).join("+"))
|
|
8
|
+
.join("/");
|
|
7
9
|
}
|
|
8
10
|
const theme = new Proxy({}, {
|
|
9
11
|
get(_target, property) {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Container, type TUI } from "#pi-tui";
|
|
2
|
+
import { type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
type ToolPresentationResult = Parameters<NonNullable<ToolDefinition["renderResult"]>>[0] & {
|
|
4
|
+
isError: boolean;
|
|
5
|
+
};
|
|
6
|
+
export interface ToolExecutionOptions {
|
|
7
|
+
showImages?: boolean;
|
|
8
|
+
imageWidthCells?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class ToolExecutionComponent extends Container {
|
|
11
|
+
private contentBox;
|
|
12
|
+
private contentText;
|
|
13
|
+
private selfRenderContainer;
|
|
14
|
+
private callRendererComponent;
|
|
15
|
+
private resultRendererComponent;
|
|
16
|
+
private rendererState;
|
|
17
|
+
private imageComponents;
|
|
18
|
+
private readonly images;
|
|
19
|
+
private disposed;
|
|
20
|
+
private updatingDisplay;
|
|
21
|
+
private imageSpacers;
|
|
22
|
+
private toolName;
|
|
23
|
+
private toolCallId;
|
|
24
|
+
private args;
|
|
25
|
+
private expanded;
|
|
26
|
+
private showImages;
|
|
27
|
+
private imageWidthCells;
|
|
28
|
+
private isPartial;
|
|
29
|
+
private toolDefinition;
|
|
30
|
+
private builtInToolDefinition;
|
|
31
|
+
private ui;
|
|
32
|
+
private cwd;
|
|
33
|
+
private executionStarted;
|
|
34
|
+
private argsComplete;
|
|
35
|
+
private result;
|
|
36
|
+
private hideComponent;
|
|
37
|
+
constructor(toolName: string, toolCallId: string, args: any, options: ToolExecutionOptions | undefined, toolDefinition: ToolDefinition<any, any> | undefined, ui: TUI, cwd: string);
|
|
38
|
+
private getCallRenderer;
|
|
39
|
+
private getResultRenderer;
|
|
40
|
+
private hasRendererDefinition;
|
|
41
|
+
private getRenderShell;
|
|
42
|
+
private getRenderContext;
|
|
43
|
+
private createCallFallback;
|
|
44
|
+
private createResultFallback;
|
|
45
|
+
updateArgs(args: any): void;
|
|
46
|
+
markExecutionStarted(): void;
|
|
47
|
+
setArgsComplete(): void;
|
|
48
|
+
updateResult(result: ToolPresentationResult, isPartial?: boolean): void;
|
|
49
|
+
/** End mount ownership without letting an obsolete conversion or extension callback repaint. */
|
|
50
|
+
dispose(): void;
|
|
51
|
+
setExpanded(expanded: boolean): void;
|
|
52
|
+
setShowImages(show: boolean): void;
|
|
53
|
+
setImageWidthCells(width: number): void;
|
|
54
|
+
invalidate(): void;
|
|
55
|
+
render(width: number): string[];
|
|
56
|
+
private updateDisplay;
|
|
57
|
+
private composeDisplay;
|
|
58
|
+
private getTextOutput;
|
|
59
|
+
private formatToolExecution;
|
|
60
|
+
}
|
|
61
|
+
export {};
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source-derived from Pi 0.84.2, MIT, repository https://github.com/earendil-works/pi,
|
|
3
|
+
* commit 914cf1472e715297caa30db4b9535d534a9eb718:
|
|
4
|
+
* packages/coding-agent/src/modes/interactive/components/tool-execution.ts.
|
|
5
|
+
* The private image cache cannot be corrected through the public component API without
|
|
6
|
+
* rebuilding renderer state. Retain its shell/lifecycle and actual public tool definitions;
|
|
7
|
+
* replace only image ownership/conversion, guard invalidation, and remap private imports.
|
|
8
|
+
* Generic text fallback below follows core/tools/render-utils.ts and utils/shell.ts.
|
|
9
|
+
*/
|
|
10
|
+
import { stripVTControlCharacters } from "node:util";
|
|
11
|
+
import { Box, Container, getCapabilities, getImageDimensions, imageFallback, Spacer, Text } from "#pi-tui";
|
|
12
|
+
import { createReadToolDefinition, createBashToolDefinition, createEditToolDefinition, createWriteToolDefinition, createGrepToolDefinition, createFindToolDefinition, createLsToolDefinition, keyHint, } from "@earendil-works/pi-coding-agent";
|
|
13
|
+
import { piTheme } from "../theme/theme.js";
|
|
14
|
+
import { ToolImagePresentation } from "../../tool-image-presentation.js";
|
|
15
|
+
const definitions = { read: createReadToolDefinition, bash: createBashToolDefinition, edit: createEditToolDefinition,
|
|
16
|
+
write: createWriteToolDefinition, grep: createGrepToolDefinition, find: createFindToolDefinition, ls: createLsToolDefinition };
|
|
17
|
+
const FALLBACK_PREVIEW_LINES = 10;
|
|
18
|
+
export class ToolExecutionComponent extends Container {
|
|
19
|
+
contentBox;
|
|
20
|
+
contentText;
|
|
21
|
+
selfRenderContainer;
|
|
22
|
+
callRendererComponent;
|
|
23
|
+
resultRendererComponent;
|
|
24
|
+
rendererState = {};
|
|
25
|
+
imageComponents = [];
|
|
26
|
+
images;
|
|
27
|
+
disposed = false;
|
|
28
|
+
updatingDisplay = false;
|
|
29
|
+
imageSpacers = [];
|
|
30
|
+
toolName;
|
|
31
|
+
toolCallId;
|
|
32
|
+
args;
|
|
33
|
+
expanded = false;
|
|
34
|
+
showImages;
|
|
35
|
+
imageWidthCells;
|
|
36
|
+
isPartial = true;
|
|
37
|
+
toolDefinition;
|
|
38
|
+
builtInToolDefinition;
|
|
39
|
+
ui;
|
|
40
|
+
cwd;
|
|
41
|
+
executionStarted = false;
|
|
42
|
+
argsComplete = false;
|
|
43
|
+
result;
|
|
44
|
+
hideComponent = false;
|
|
45
|
+
constructor(toolName, toolCallId, args, options = {}, toolDefinition, ui, cwd) {
|
|
46
|
+
super();
|
|
47
|
+
this.toolName = toolName;
|
|
48
|
+
this.toolCallId = toolCallId;
|
|
49
|
+
this.args = args;
|
|
50
|
+
this.toolDefinition = toolDefinition;
|
|
51
|
+
this.builtInToolDefinition = Object.hasOwn(definitions, toolName)
|
|
52
|
+
? definitions[toolName](cwd) : undefined;
|
|
53
|
+
this.showImages = options.showImages ?? true;
|
|
54
|
+
this.imageWidthCells = options.imageWidthCells ?? 60;
|
|
55
|
+
this.ui = ui;
|
|
56
|
+
this.cwd = cwd;
|
|
57
|
+
this.images = new ToolImagePresentation(() => {
|
|
58
|
+
if (this.disposed)
|
|
59
|
+
return;
|
|
60
|
+
this.updateDisplay();
|
|
61
|
+
this.ui.requestRender();
|
|
62
|
+
});
|
|
63
|
+
this.addChild(new Spacer(1));
|
|
64
|
+
// Always create all shell variants. contentBox is used for default renderer-based composition.
|
|
65
|
+
// selfRenderContainer is used when the tool renders its own framing.
|
|
66
|
+
// contentText is reserved for generic fallback rendering when no tool definition exists.
|
|
67
|
+
this.contentBox = new Box(1, 1, (text) => piTheme().bg("toolPendingBg", text));
|
|
68
|
+
this.contentText = new Text("", 1, 1, (text) => piTheme().bg("toolPendingBg", text));
|
|
69
|
+
this.selfRenderContainer = new Container();
|
|
70
|
+
if (this.hasRendererDefinition()) {
|
|
71
|
+
this.addChild(this.getRenderShell() === "self" ? this.selfRenderContainer : this.contentBox);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.addChild(this.contentText);
|
|
75
|
+
}
|
|
76
|
+
this.updateDisplay();
|
|
77
|
+
}
|
|
78
|
+
getCallRenderer() {
|
|
79
|
+
if (!this.builtInToolDefinition) {
|
|
80
|
+
return this.toolDefinition?.renderCall;
|
|
81
|
+
}
|
|
82
|
+
if (!this.toolDefinition) {
|
|
83
|
+
return this.builtInToolDefinition.renderCall;
|
|
84
|
+
}
|
|
85
|
+
return this.toolDefinition.renderCall ?? this.builtInToolDefinition.renderCall;
|
|
86
|
+
}
|
|
87
|
+
getResultRenderer() {
|
|
88
|
+
if (!this.builtInToolDefinition) {
|
|
89
|
+
return this.toolDefinition?.renderResult;
|
|
90
|
+
}
|
|
91
|
+
if (!this.toolDefinition) {
|
|
92
|
+
return this.builtInToolDefinition.renderResult;
|
|
93
|
+
}
|
|
94
|
+
return this.toolDefinition.renderResult ?? this.builtInToolDefinition.renderResult;
|
|
95
|
+
}
|
|
96
|
+
hasRendererDefinition() {
|
|
97
|
+
return this.builtInToolDefinition !== undefined || this.toolDefinition !== undefined;
|
|
98
|
+
}
|
|
99
|
+
getRenderShell() {
|
|
100
|
+
if (!this.builtInToolDefinition) {
|
|
101
|
+
return this.toolDefinition?.renderShell ?? "default";
|
|
102
|
+
}
|
|
103
|
+
if (!this.toolDefinition) {
|
|
104
|
+
return this.builtInToolDefinition.renderShell ?? "default";
|
|
105
|
+
}
|
|
106
|
+
return this.toolDefinition.renderShell ?? this.builtInToolDefinition.renderShell ?? "default";
|
|
107
|
+
}
|
|
108
|
+
getRenderContext(lastComponent) {
|
|
109
|
+
return {
|
|
110
|
+
args: this.args,
|
|
111
|
+
toolCallId: this.toolCallId,
|
|
112
|
+
invalidate: () => {
|
|
113
|
+
if (this.disposed || this.updatingDisplay)
|
|
114
|
+
return;
|
|
115
|
+
this.invalidate();
|
|
116
|
+
this.ui.requestRender();
|
|
117
|
+
},
|
|
118
|
+
lastComponent,
|
|
119
|
+
state: this.rendererState,
|
|
120
|
+
cwd: this.cwd,
|
|
121
|
+
executionStarted: this.executionStarted,
|
|
122
|
+
argsComplete: this.argsComplete,
|
|
123
|
+
isPartial: this.isPartial,
|
|
124
|
+
expanded: this.expanded,
|
|
125
|
+
showImages: this.showImages,
|
|
126
|
+
isError: this.result?.isError ?? false,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
createCallFallback() {
|
|
130
|
+
const theme = piTheme();
|
|
131
|
+
return new Text(theme.fg("toolTitle", theme.bold(this.toolName)), 0, 0);
|
|
132
|
+
}
|
|
133
|
+
createResultFallback() {
|
|
134
|
+
const theme = piTheme();
|
|
135
|
+
const output = this.getTextOutput();
|
|
136
|
+
if (!output) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
const lines = output.split("\n");
|
|
140
|
+
const displayLines = this.expanded ? lines : lines.slice(0, FALLBACK_PREVIEW_LINES);
|
|
141
|
+
const remaining = lines.length - displayLines.length;
|
|
142
|
+
let text = displayLines.map((line) => theme.fg("toolOutput", line)).join("\n");
|
|
143
|
+
if (remaining > 0) {
|
|
144
|
+
text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("app.tools.expand", "to expand")}${theme.fg("muted", ")")}`;
|
|
145
|
+
}
|
|
146
|
+
return new Text(text, 0, 0);
|
|
147
|
+
}
|
|
148
|
+
updateArgs(args) {
|
|
149
|
+
this.args = args;
|
|
150
|
+
this.updateDisplay();
|
|
151
|
+
}
|
|
152
|
+
markExecutionStarted() {
|
|
153
|
+
this.executionStarted = true;
|
|
154
|
+
this.updateDisplay();
|
|
155
|
+
this.ui.requestRender();
|
|
156
|
+
}
|
|
157
|
+
setArgsComplete() {
|
|
158
|
+
this.argsComplete = true;
|
|
159
|
+
this.updateDisplay();
|
|
160
|
+
this.ui.requestRender();
|
|
161
|
+
}
|
|
162
|
+
updateResult(result, isPartial = false) {
|
|
163
|
+
this.result = result;
|
|
164
|
+
this.isPartial = isPartial;
|
|
165
|
+
this.updateDisplay();
|
|
166
|
+
}
|
|
167
|
+
/** End mount ownership without letting an obsolete conversion or extension callback repaint. */
|
|
168
|
+
dispose() {
|
|
169
|
+
this.disposed = true;
|
|
170
|
+
this.images.dispose();
|
|
171
|
+
this.result = undefined;
|
|
172
|
+
this.callRendererComponent = undefined;
|
|
173
|
+
this.resultRendererComponent = undefined;
|
|
174
|
+
this.rendererState = {};
|
|
175
|
+
this.imageComponents = [];
|
|
176
|
+
this.imageSpacers = [];
|
|
177
|
+
this.clear();
|
|
178
|
+
}
|
|
179
|
+
setExpanded(expanded) {
|
|
180
|
+
this.expanded = expanded;
|
|
181
|
+
this.updateDisplay();
|
|
182
|
+
}
|
|
183
|
+
setShowImages(show) {
|
|
184
|
+
this.showImages = show;
|
|
185
|
+
this.updateDisplay();
|
|
186
|
+
}
|
|
187
|
+
setImageWidthCells(width) {
|
|
188
|
+
this.imageWidthCells = Math.max(1, Math.floor(width));
|
|
189
|
+
this.updateDisplay();
|
|
190
|
+
}
|
|
191
|
+
invalidate() {
|
|
192
|
+
super.invalidate();
|
|
193
|
+
this.updateDisplay();
|
|
194
|
+
}
|
|
195
|
+
render(width) {
|
|
196
|
+
if (this.hideComponent) {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
if (this.hasRendererDefinition() && this.getRenderShell() === "self") {
|
|
200
|
+
const contentLines = this.selfRenderContainer.render(width);
|
|
201
|
+
if (contentLines.length === 0 && this.imageComponents.length === 0) {
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
204
|
+
const lines = [];
|
|
205
|
+
if (contentLines.length > 0) {
|
|
206
|
+
lines.push("");
|
|
207
|
+
lines.push(...contentLines);
|
|
208
|
+
}
|
|
209
|
+
for (let i = 0; i < this.imageComponents.length; i++) {
|
|
210
|
+
const spacer = this.imageSpacers[i];
|
|
211
|
+
if (spacer) {
|
|
212
|
+
lines.push(...spacer.render(width));
|
|
213
|
+
}
|
|
214
|
+
const imageComponent = this.imageComponents[i];
|
|
215
|
+
if (imageComponent) {
|
|
216
|
+
lines.push(...imageComponent.render(width));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return lines;
|
|
220
|
+
}
|
|
221
|
+
return super.render(width);
|
|
222
|
+
}
|
|
223
|
+
updateDisplay() {
|
|
224
|
+
if (this.disposed || this.updatingDisplay)
|
|
225
|
+
return;
|
|
226
|
+
this.updatingDisplay = true;
|
|
227
|
+
try {
|
|
228
|
+
this.composeDisplay();
|
|
229
|
+
}
|
|
230
|
+
finally {
|
|
231
|
+
this.updatingDisplay = false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
composeDisplay() {
|
|
235
|
+
const theme = piTheme();
|
|
236
|
+
const bgFn = this.isPartial
|
|
237
|
+
? (text) => theme.bg("toolPendingBg", text)
|
|
238
|
+
: this.result?.isError
|
|
239
|
+
? (text) => theme.bg("toolErrorBg", text)
|
|
240
|
+
: (text) => theme.bg("toolSuccessBg", text);
|
|
241
|
+
let hasContent = false;
|
|
242
|
+
this.hideComponent = false;
|
|
243
|
+
if (this.hasRendererDefinition()) {
|
|
244
|
+
const renderContainer = this.getRenderShell() === "self" ? this.selfRenderContainer : this.contentBox;
|
|
245
|
+
if (renderContainer instanceof Box) {
|
|
246
|
+
renderContainer.setBgFn(bgFn);
|
|
247
|
+
}
|
|
248
|
+
renderContainer.clear();
|
|
249
|
+
const callRenderer = this.getCallRenderer();
|
|
250
|
+
if (!callRenderer) {
|
|
251
|
+
renderContainer.addChild(this.createCallFallback());
|
|
252
|
+
hasContent = true;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
try {
|
|
256
|
+
const component = callRenderer(this.args, theme, this.getRenderContext(this.callRendererComponent));
|
|
257
|
+
this.callRendererComponent = component;
|
|
258
|
+
renderContainer.addChild(component);
|
|
259
|
+
hasContent = true;
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
this.callRendererComponent = undefined;
|
|
263
|
+
renderContainer.addChild(this.createCallFallback());
|
|
264
|
+
hasContent = true;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (this.result) {
|
|
268
|
+
const resultRenderer = this.getResultRenderer();
|
|
269
|
+
if (!resultRenderer) {
|
|
270
|
+
const component = this.createResultFallback();
|
|
271
|
+
if (component) {
|
|
272
|
+
renderContainer.addChild(component);
|
|
273
|
+
hasContent = true;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
try {
|
|
278
|
+
const component = resultRenderer({ content: this.result.content, details: this.result.details }, { expanded: this.expanded, isPartial: this.isPartial }, theme, this.getRenderContext(this.resultRendererComponent));
|
|
279
|
+
this.resultRendererComponent = component;
|
|
280
|
+
renderContainer.addChild(component);
|
|
281
|
+
hasContent = true;
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
this.resultRendererComponent = undefined;
|
|
285
|
+
const component = this.createResultFallback();
|
|
286
|
+
if (component) {
|
|
287
|
+
renderContainer.addChild(component);
|
|
288
|
+
hasContent = true;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
this.contentText.setCustomBgFn(bgFn);
|
|
296
|
+
this.contentText.setText(this.formatToolExecution());
|
|
297
|
+
hasContent = true;
|
|
298
|
+
}
|
|
299
|
+
for (const img of this.imageComponents) {
|
|
300
|
+
this.removeChild(img);
|
|
301
|
+
}
|
|
302
|
+
this.imageComponents = [];
|
|
303
|
+
for (const spacer of this.imageSpacers) {
|
|
304
|
+
this.removeChild(spacer);
|
|
305
|
+
}
|
|
306
|
+
this.imageSpacers = [];
|
|
307
|
+
this.images.update(this.result?.content ?? [], this.showImages);
|
|
308
|
+
for (const imageComponent of this.images.components(this.imageWidthCells)) {
|
|
309
|
+
const spacer = new Spacer(1);
|
|
310
|
+
this.addChild(spacer);
|
|
311
|
+
this.imageSpacers.push(spacer);
|
|
312
|
+
this.imageComponents.push(imageComponent);
|
|
313
|
+
this.addChild(imageComponent);
|
|
314
|
+
}
|
|
315
|
+
if (this.hasRendererDefinition() && !hasContent && this.imageComponents.length === 0) {
|
|
316
|
+
this.hideComponent = true;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
getTextOutput() {
|
|
320
|
+
if (!this.result)
|
|
321
|
+
return "";
|
|
322
|
+
const text = this.result.content.filter(part => part.type === "text").map(part => Array.from(stripVTControlCharacters(part.text ?? "")).filter(char => {
|
|
323
|
+
const code = char.codePointAt(0);
|
|
324
|
+
return code === 9 || code === 10 || code > 31 && (code < 0xfff9 || code > 0xfffb);
|
|
325
|
+
}).join("")).join("\n");
|
|
326
|
+
if (getCapabilities().images && this.showImages)
|
|
327
|
+
return text;
|
|
328
|
+
const indicators = this.result.content.filter(part => part.type === "image").map(part => imageFallback(part.mimeType ?? "image/unknown", part.data && part.mimeType ? getImageDimensions(part.data, part.mimeType) ?? undefined : undefined)).join("\n");
|
|
329
|
+
return text && indicators ? `${text}\n${indicators}` : text || indicators;
|
|
330
|
+
}
|
|
331
|
+
formatToolExecution() {
|
|
332
|
+
const theme = piTheme();
|
|
333
|
+
let text = theme.fg("toolTitle", theme.bold(this.toolName));
|
|
334
|
+
const content = JSON.stringify(this.args, null, 2);
|
|
335
|
+
if (content) {
|
|
336
|
+
text += `\n\n${content}`;
|
|
337
|
+
}
|
|
338
|
+
const output = this.getTextOutput();
|
|
339
|
+
if (output) {
|
|
340
|
+
text += `\n${output}`;
|
|
341
|
+
}
|
|
342
|
+
return text;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
@@ -213,7 +213,10 @@ export declare class PiEngineAdapter implements OwnedUiPromptSuggestionGenerator
|
|
|
213
213
|
readonly description: string;
|
|
214
214
|
}[];
|
|
215
215
|
pinnedToolDefinition(toolName: string): unknown;
|
|
216
|
-
|
|
216
|
+
/** Bind the owned UI's clipboard lifecycle without changing the comparison host. True means acknowledged delivery. */
|
|
217
|
+
bindClipboardWriter(writer: (text: string) => Promise<boolean>): () => void;
|
|
218
|
+
/** Workflow and tree copying share the active owner's write fence and delivery acknowledgment. */
|
|
219
|
+
copyWorkflowText(text: string): Promise<boolean>;
|
|
217
220
|
clearQueuedWorkflows(): readonly string[];
|
|
218
221
|
abortBashWorkflow(): void;
|
|
219
222
|
executeBashWorkflow(command: string, excludeFromContext: boolean): Promise<PiBashWorkflowResult>;
|