feat(coding-agent): merge origin/main into model runtime facade
This commit is contained in:
@@ -47,6 +47,7 @@ import {
|
||||
getAgentDir,
|
||||
getAuthPath,
|
||||
getDebugLogPath,
|
||||
getDocsPath,
|
||||
getShareViewerUrl,
|
||||
VERSION,
|
||||
} from "../../config.ts";
|
||||
@@ -86,7 +87,7 @@ import { isInstallTelemetryEnabled } from "../../core/telemetry.ts";
|
||||
import type { TruncationResult } from "../../core/tools/truncate.ts";
|
||||
import { hasTrustRequiringProjectResources, ProjectTrustStore } from "../../core/trust-manager.ts";
|
||||
import { getChangelogPath, getNewEntries, normalizeChangelogLinks, parseChangelog } from "../../utils/changelog.ts";
|
||||
import { copyToClipboard } from "../../utils/clipboard.ts";
|
||||
import { copyToClipboard, readClipboardText } from "../../utils/clipboard.ts";
|
||||
import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.ts";
|
||||
import { parseGitUrl } from "../../utils/git.ts";
|
||||
import { getCwdRelativePath } from "../../utils/paths.ts";
|
||||
@@ -745,7 +746,7 @@ export class InteractiveMode {
|
||||
rawKeyHint("!!", "to run bash (no context)"),
|
||||
hint("app.message.followUp", "to queue follow-up"),
|
||||
hint("app.message.dequeue", "to edit all queued messages"),
|
||||
hint("app.clipboard.pasteImage", "to paste image"),
|
||||
hint("app.clipboard.pasteImage", "to paste image (with text fallback)"),
|
||||
rawKeyHint("drop files", "to attach"),
|
||||
].join("\n");
|
||||
const compactInstructions = [
|
||||
@@ -2558,6 +2559,7 @@ export class InteractiveMode {
|
||||
this.defaultEditor.onAction("app.tools.expand", () => this.toggleToolOutputExpansion());
|
||||
this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility());
|
||||
this.defaultEditor.onAction("app.editor.external", () => this.openExternalEditor());
|
||||
this.defaultEditor.onAction("app.message.copy", () => void this.handleCopyCommand());
|
||||
this.defaultEditor.onAction("app.message.followUp", () => this.handleFollowUp());
|
||||
this.defaultEditor.onAction("app.message.dequeue", () => this.handleDequeue());
|
||||
this.defaultEditor.onAction("app.session.new", () => this.handleClearCommand());
|
||||
@@ -2573,29 +2575,33 @@ export class InteractiveMode {
|
||||
}
|
||||
};
|
||||
|
||||
// Handle clipboard image paste (triggered on Ctrl+V)
|
||||
// Handle clipboard paste (triggered on Ctrl+V). Images are attached by path;
|
||||
// otherwise, paste plain text from the system clipboard.
|
||||
this.defaultEditor.onPasteImage = () => {
|
||||
this.handleClipboardImagePaste();
|
||||
void this.handleClipboardPaste();
|
||||
};
|
||||
}
|
||||
|
||||
private async handleClipboardImagePaste(): Promise<void> {
|
||||
private async handleClipboardPaste(): Promise<void> {
|
||||
try {
|
||||
const image = await readClipboardImage();
|
||||
if (!image) {
|
||||
if (image) {
|
||||
const tmpDir = os.tmpdir();
|
||||
const ext = extensionForImageMimeType(image.mimeType) ?? "png";
|
||||
const fileName = `pi-clipboard-${crypto.randomUUID()}.${ext}`;
|
||||
const filePath = path.join(tmpDir, fileName);
|
||||
fs.writeFileSync(filePath, Buffer.from(image.bytes));
|
||||
|
||||
this.editor.insertTextAtCursor?.(filePath);
|
||||
this.ui.requestRender();
|
||||
return;
|
||||
}
|
||||
|
||||
// Write to temp file
|
||||
const tmpDir = os.tmpdir();
|
||||
const ext = extensionForImageMimeType(image.mimeType) ?? "png";
|
||||
const fileName = `pi-clipboard-${crypto.randomUUID()}.${ext}`;
|
||||
const filePath = path.join(tmpDir, fileName);
|
||||
fs.writeFileSync(filePath, Buffer.from(image.bytes));
|
||||
|
||||
// Insert file path directly
|
||||
this.editor.insertTextAtCursor?.(filePath);
|
||||
this.ui.requestRender();
|
||||
const text = await readClipboardText();
|
||||
if (text) {
|
||||
this.editor.insertTextAtCursor?.(text);
|
||||
this.ui.requestRender();
|
||||
}
|
||||
} catch {
|
||||
// Silently ignore clipboard errors (may not have permission, etc.)
|
||||
}
|
||||
@@ -4681,6 +4687,18 @@ export class InteractiveMode {
|
||||
initialSelectedId,
|
||||
initialFilterMode,
|
||||
);
|
||||
selector.onCopy = async (text) => {
|
||||
if (!text) {
|
||||
this.showError("Selected entry has no text to copy");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await copyToClipboard(text);
|
||||
this.showStatus("Copied selected message to clipboard");
|
||||
} catch (error) {
|
||||
this.showError(error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
};
|
||||
return { component: selector, focus: selector };
|
||||
});
|
||||
}
|
||||
@@ -4851,8 +4869,8 @@ export class InteractiveMode {
|
||||
}
|
||||
|
||||
private showLoginAuthTypeSelector(providerOptions?: AuthSelectorProvider[]): void {
|
||||
const subscriptionLabel = "Use a subscription";
|
||||
const apiKeyLabel = "Use an API key";
|
||||
const subscriptionLabel = "Sign in with an account";
|
||||
const apiKeyLabel = "Sign in with an API key";
|
||||
const availableAuthTypes = providerOptions
|
||||
? new Set(providerOptions.map((provider) => provider.authType))
|
||||
: new Set<AuthSelectorProvider["authType"]>(["oauth", "api_key"]);
|
||||
@@ -5083,6 +5101,14 @@ export class InteractiveMode {
|
||||
providerName,
|
||||
);
|
||||
|
||||
if (providerId === "amazon-bedrock") {
|
||||
dialog.showDetails([
|
||||
theme.fg("text", "You can also use an AWS profile, IAM keys, or role-based credentials."),
|
||||
theme.fg("muted", "See:"),
|
||||
theme.fg("accent", ` ${path.join(getDocsPath(), "providers.md")}`),
|
||||
]);
|
||||
}
|
||||
|
||||
this.editorContainer.clear();
|
||||
this.editorContainer.addChild(dialog);
|
||||
this.ui.setFocus(dialog);
|
||||
@@ -5698,6 +5724,7 @@ export class InteractiveMode {
|
||||
const toggleThinking = this.getAppKeyDisplay("app.thinking.toggle");
|
||||
const externalEditor = this.getAppKeyDisplay("app.editor.external");
|
||||
const cycleModelBackward = this.getAppKeyDisplay("app.model.cycleBackward");
|
||||
const copyMessage = this.getAppKeyDisplay("app.message.copy");
|
||||
const followUp = this.getAppKeyDisplay("app.message.followUp");
|
||||
const dequeue = this.getAppKeyDisplay("app.message.dequeue");
|
||||
const pasteImage = this.getAppKeyDisplay("app.clipboard.pasteImage");
|
||||
@@ -5741,9 +5768,10 @@ export class InteractiveMode {
|
||||
| \`${expandTools}\` | Toggle tool output expansion |
|
||||
| \`${toggleThinking}\` | Toggle thinking block visibility |
|
||||
| \`${externalEditor}\` | Edit message in external editor |
|
||||
| \`${copyMessage}\` | Copy last assistant message |
|
||||
| \`${followUp}\` | Queue follow-up message |
|
||||
| \`${dequeue}\` | Restore queued messages |
|
||||
| \`${pasteImage}\` | Paste image from clipboard |
|
||||
| \`${pasteImage}\` | Paste image or text from clipboard |
|
||||
| \`/\` | Slash commands |
|
||||
| \`!\` | Run bash command |
|
||||
| \`!!\` | Run bash command (excluded from context) |
|
||||
|
||||
Reference in New Issue
Block a user