@selesai/code 0.1.14 → 0.1.15
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.
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Interactive mode for the coding agent.
|
|
3
3
|
* Handles TUI rendering and user interaction, delegating business logic to AgentSession.
|
|
4
4
|
*/
|
|
5
|
-
import * as crypto from "node:crypto";
|
|
6
5
|
import * as fs from "node:fs";
|
|
7
6
|
import * as os from "node:os";
|
|
8
7
|
import * as path from "node:path";
|
|
@@ -27,8 +26,9 @@ import { isInstallTelemetryEnabled } from "../../core/telemetry.js";
|
|
|
27
26
|
import { hasTrustRequiringProjectResources, ProjectTrustStore } from "../../core/trust-manager.js";
|
|
28
27
|
import { getChangelogPath, getNewEntries, normalizeChangelogLinks, parseChangelog } from "../../utils/changelog.js";
|
|
29
28
|
import { copyToClipboard } from "../../utils/clipboard.js";
|
|
30
|
-
import {
|
|
29
|
+
import { readClipboardImage } from "../../utils/clipboard-image.js";
|
|
31
30
|
import { parseGitUrl } from "../../utils/git.js";
|
|
31
|
+
import { formatDimensionNote, resizeImage } from "../../utils/image-resize.js";
|
|
32
32
|
import { getCwdRelativePath } from "../../utils/paths.js";
|
|
33
33
|
import { getPiUserAgent } from "../../utils/pi-user-agent.js";
|
|
34
34
|
import { killTrackedDetachedChildren } from "../../utils/shell.js";
|
|
@@ -152,6 +152,7 @@ export class InteractiveMode {
|
|
|
152
152
|
isInitialized = false;
|
|
153
153
|
onInputCallback;
|
|
154
154
|
pendingUserInputs = [];
|
|
155
|
+
pendingInputImages = [];
|
|
155
156
|
loadingAnimation = undefined;
|
|
156
157
|
workingMessage = undefined;
|
|
157
158
|
workingVisible = true;
|
|
@@ -595,7 +596,7 @@ export class InteractiveMode {
|
|
|
595
596
|
while (true) {
|
|
596
597
|
const userInput = await this.getUserInput();
|
|
597
598
|
try {
|
|
598
|
-
await this.session.prompt(userInput);
|
|
599
|
+
await this.session.prompt(userInput.text, { images: userInput.images });
|
|
599
600
|
}
|
|
600
601
|
catch (error) {
|
|
601
602
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
@@ -1996,25 +1997,54 @@ export class InteractiveMode {
|
|
|
1996
1997
|
if (!image) {
|
|
1997
1998
|
return;
|
|
1998
1999
|
}
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
const
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2000
|
+
let attachment;
|
|
2001
|
+
let dimensionNote;
|
|
2002
|
+
const autoResizeImages = this.settingsManager.getImageAutoResize();
|
|
2003
|
+
if (autoResizeImages) {
|
|
2004
|
+
const resized = await resizeImage(image.bytes, image.mimeType);
|
|
2005
|
+
if (!resized) {
|
|
2006
|
+
this.showWarning("Clipboard image could not be resized below the inline image size limit.");
|
|
2007
|
+
return;
|
|
2008
|
+
}
|
|
2009
|
+
dimensionNote = formatDimensionNote(resized);
|
|
2010
|
+
attachment = {
|
|
2011
|
+
type: "image",
|
|
2012
|
+
mimeType: resized.mimeType,
|
|
2013
|
+
data: resized.data,
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
else {
|
|
2017
|
+
attachment = {
|
|
2018
|
+
type: "image",
|
|
2019
|
+
mimeType: image.mimeType,
|
|
2020
|
+
data: Buffer.from(image.bytes).toString("base64"),
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
this.pendingInputImages.push(attachment);
|
|
2024
|
+
const label = dimensionNote
|
|
2025
|
+
? `[clipboard image ${this.pendingInputImages.length}: ${dimensionNote}]`
|
|
2026
|
+
: `[clipboard image ${this.pendingInputImages.length}]`;
|
|
2027
|
+
this.editor.insertTextAtCursor?.(label);
|
|
2007
2028
|
this.ui.requestRender();
|
|
2008
2029
|
}
|
|
2009
2030
|
catch {
|
|
2010
2031
|
// Silently ignore clipboard errors (may not have permission, etc.)
|
|
2011
2032
|
}
|
|
2012
2033
|
}
|
|
2034
|
+
consumePendingInputImages() {
|
|
2035
|
+
if (this.pendingInputImages.length === 0) {
|
|
2036
|
+
return undefined;
|
|
2037
|
+
}
|
|
2038
|
+
const images = this.pendingInputImages;
|
|
2039
|
+
this.pendingInputImages = [];
|
|
2040
|
+
return images;
|
|
2041
|
+
}
|
|
2013
2042
|
setupEditorSubmitHandler() {
|
|
2014
2043
|
this.defaultEditor.onSubmit = async (text) => {
|
|
2015
2044
|
text = text.trim();
|
|
2016
2045
|
if (!text)
|
|
2017
2046
|
return;
|
|
2047
|
+
const images = this.consumePendingInputImages();
|
|
2018
2048
|
// Handle commands
|
|
2019
2049
|
if (text === "/settings") {
|
|
2020
2050
|
this.showSettingsSelector();
|
|
@@ -2165,10 +2195,10 @@ export class InteractiveMode {
|
|
|
2165
2195
|
if (this.isExtensionCommand(text)) {
|
|
2166
2196
|
this.editor.addToHistory?.(text);
|
|
2167
2197
|
this.editor.setText("");
|
|
2168
|
-
await this.session.prompt(text);
|
|
2198
|
+
await this.session.prompt(text, { images });
|
|
2169
2199
|
}
|
|
2170
2200
|
else {
|
|
2171
|
-
this.queueCompactionMessage(text, "steer");
|
|
2201
|
+
this.queueCompactionMessage(text, "steer", images);
|
|
2172
2202
|
}
|
|
2173
2203
|
return;
|
|
2174
2204
|
}
|
|
@@ -2177,7 +2207,7 @@ export class InteractiveMode {
|
|
|
2177
2207
|
if (this.session.isStreaming) {
|
|
2178
2208
|
this.editor.addToHistory?.(text);
|
|
2179
2209
|
this.editor.setText("");
|
|
2180
|
-
await this.session.prompt(text, { streamingBehavior: "steer" });
|
|
2210
|
+
await this.session.prompt(text, { streamingBehavior: "steer", images });
|
|
2181
2211
|
this.updatePendingMessagesDisplay();
|
|
2182
2212
|
this.ui.requestRender();
|
|
2183
2213
|
return;
|
|
@@ -2185,11 +2215,12 @@ export class InteractiveMode {
|
|
|
2185
2215
|
// Normal message submission
|
|
2186
2216
|
// First, move any pending bash components to chat
|
|
2187
2217
|
this.flushPendingBashComponents();
|
|
2218
|
+
const input = { text, images };
|
|
2188
2219
|
if (this.onInputCallback) {
|
|
2189
|
-
this.onInputCallback(
|
|
2220
|
+
this.onInputCallback(input);
|
|
2190
2221
|
}
|
|
2191
2222
|
else {
|
|
2192
|
-
this.pendingUserInputs.push(
|
|
2223
|
+
this.pendingUserInputs.push(input);
|
|
2193
2224
|
}
|
|
2194
2225
|
this.editor.addToHistory?.(text);
|
|
2195
2226
|
};
|
|
@@ -2685,9 +2716,9 @@ export class InteractiveMode {
|
|
|
2685
2716
|
return queuedInput;
|
|
2686
2717
|
}
|
|
2687
2718
|
return new Promise((resolve) => {
|
|
2688
|
-
this.onInputCallback = (
|
|
2719
|
+
this.onInputCallback = (input) => {
|
|
2689
2720
|
this.onInputCallback = undefined;
|
|
2690
|
-
resolve(
|
|
2721
|
+
resolve(input);
|
|
2691
2722
|
};
|
|
2692
2723
|
});
|
|
2693
2724
|
}
|
|
@@ -2882,22 +2913,24 @@ export class InteractiveMode {
|
|
|
2882
2913
|
return;
|
|
2883
2914
|
// Queue input during compaction (extension commands execute immediately)
|
|
2884
2915
|
if (this.session.isCompacting) {
|
|
2916
|
+
const images = this.consumePendingInputImages();
|
|
2885
2917
|
if (this.isExtensionCommand(text)) {
|
|
2886
2918
|
this.editor.addToHistory?.(text);
|
|
2887
2919
|
this.editor.setText("");
|
|
2888
|
-
await this.session.prompt(text);
|
|
2920
|
+
await this.session.prompt(text, { images });
|
|
2889
2921
|
}
|
|
2890
2922
|
else {
|
|
2891
|
-
this.queueCompactionMessage(text, "followUp");
|
|
2923
|
+
this.queueCompactionMessage(text, "followUp", images);
|
|
2892
2924
|
}
|
|
2893
2925
|
return;
|
|
2894
2926
|
}
|
|
2895
2927
|
// Alt+Enter queues a follow-up message (waits until agent finishes)
|
|
2896
2928
|
// This handles extension commands (execute immediately), prompt template expansion, and queueing
|
|
2897
2929
|
if (this.session.isStreaming) {
|
|
2930
|
+
const images = this.consumePendingInputImages();
|
|
2898
2931
|
this.editor.addToHistory?.(text);
|
|
2899
2932
|
this.editor.setText("");
|
|
2900
|
-
await this.session.prompt(text, { streamingBehavior: "followUp" });
|
|
2933
|
+
await this.session.prompt(text, { streamingBehavior: "followUp", images });
|
|
2901
2934
|
this.updatePendingMessagesDisplay();
|
|
2902
2935
|
this.ui.requestRender();
|
|
2903
2936
|
}
|
|
@@ -3040,6 +3073,7 @@ export class InteractiveMode {
|
|
|
3040
3073
|
// =========================================================================
|
|
3041
3074
|
clearEditor() {
|
|
3042
3075
|
this.editor.setText("");
|
|
3076
|
+
this.pendingInputImages = [];
|
|
3043
3077
|
this.ui.requestRender();
|
|
3044
3078
|
}
|
|
3045
3079
|
showError(errorMessage) {
|
|
@@ -3157,8 +3191,8 @@ export class InteractiveMode {
|
|
|
3157
3191
|
}
|
|
3158
3192
|
return allQueued.length;
|
|
3159
3193
|
}
|
|
3160
|
-
queueCompactionMessage(text, mode) {
|
|
3161
|
-
this.compactionQueuedMessages.push({ text, mode });
|
|
3194
|
+
queueCompactionMessage(text, mode, images) {
|
|
3195
|
+
this.compactionQueuedMessages.push({ text, images, mode });
|
|
3162
3196
|
this.editor.addToHistory?.(text);
|
|
3163
3197
|
this.editor.setText("");
|
|
3164
3198
|
this.updatePendingMessagesDisplay();
|
|
@@ -3190,13 +3224,13 @@ export class InteractiveMode {
|
|
|
3190
3224
|
// When retry is pending, queue messages for the retry turn
|
|
3191
3225
|
for (const message of queuedMessages) {
|
|
3192
3226
|
if (this.isExtensionCommand(message.text)) {
|
|
3193
|
-
await this.session.prompt(message.text);
|
|
3227
|
+
await this.session.prompt(message.text, { images: message.images });
|
|
3194
3228
|
}
|
|
3195
3229
|
else if (message.mode === "followUp") {
|
|
3196
|
-
await this.session.followUp(message.text);
|
|
3230
|
+
await this.session.followUp(message.text, message.images);
|
|
3197
3231
|
}
|
|
3198
3232
|
else {
|
|
3199
|
-
await this.session.steer(message.text);
|
|
3233
|
+
await this.session.steer(message.text, message.images);
|
|
3200
3234
|
}
|
|
3201
3235
|
}
|
|
3202
3236
|
this.updatePendingMessagesDisplay();
|
|
@@ -3207,7 +3241,7 @@ export class InteractiveMode {
|
|
|
3207
3241
|
if (firstPromptIndex === -1) {
|
|
3208
3242
|
// All extension commands - execute them all
|
|
3209
3243
|
for (const message of queuedMessages) {
|
|
3210
|
-
await this.session.prompt(message.text);
|
|
3244
|
+
await this.session.prompt(message.text, { images: message.images });
|
|
3211
3245
|
}
|
|
3212
3246
|
return;
|
|
3213
3247
|
}
|
|
@@ -3216,22 +3250,22 @@ export class InteractiveMode {
|
|
|
3216
3250
|
const firstPrompt = queuedMessages[firstPromptIndex];
|
|
3217
3251
|
const rest = queuedMessages.slice(firstPromptIndex + 1);
|
|
3218
3252
|
for (const message of preCommands) {
|
|
3219
|
-
await this.session.prompt(message.text);
|
|
3253
|
+
await this.session.prompt(message.text, { images: message.images });
|
|
3220
3254
|
}
|
|
3221
3255
|
// Send first prompt (starts streaming)
|
|
3222
|
-
const promptPromise = this.session.prompt(firstPrompt.text).catch((error) => {
|
|
3256
|
+
const promptPromise = this.session.prompt(firstPrompt.text, { images: firstPrompt.images }).catch((error) => {
|
|
3223
3257
|
restoreQueue(error);
|
|
3224
3258
|
});
|
|
3225
3259
|
// Queue remaining messages
|
|
3226
3260
|
for (const message of rest) {
|
|
3227
3261
|
if (this.isExtensionCommand(message.text)) {
|
|
3228
|
-
await this.session.prompt(message.text);
|
|
3262
|
+
await this.session.prompt(message.text, { images: message.images });
|
|
3229
3263
|
}
|
|
3230
3264
|
else if (message.mode === "followUp") {
|
|
3231
|
-
await this.session.followUp(message.text);
|
|
3265
|
+
await this.session.followUp(message.text, message.images);
|
|
3232
3266
|
}
|
|
3233
3267
|
else {
|
|
3234
|
-
await this.session.steer(message.text);
|
|
3268
|
+
await this.session.steer(message.text, message.images);
|
|
3235
3269
|
}
|
|
3236
3270
|
}
|
|
3237
3271
|
this.updatePendingMessagesDisplay();
|