@di-code/coding-agent 0.1.2 → 0.1.3
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 +489 -143
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/clipboard-image.d.ts +13 -0
- package/dist/core/clipboard-image.d.ts.map +1 -0
- package/dist/core/clipboard-image.js +99 -0
- package/dist/core/clipboard-image.js.map +1 -0
- package/dist/core/image-input.d.ts +13 -0
- package/dist/core/image-input.d.ts.map +1 -0
- package/dist/core/image-input.js +102 -0
- package/dist/core/image-input.js.map +1 -0
- package/dist/core/session.d.ts +3 -1
- package/dist/core/session.d.ts.map +1 -1
- package/dist/core/session.js +12 -4
- package/dist/core/session.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +8 -2
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive-components.d.ts +1 -0
- package/dist/modes/interactive-components.d.ts.map +1 -1
- package/dist/modes/interactive-components.js +44 -20
- package/dist/modes/interactive-components.js.map +1 -1
- package/dist/modes/interactive-layout.d.ts +2 -1
- package/dist/modes/interactive-layout.d.ts.map +1 -1
- package/dist/modes/interactive-layout.js +17 -1
- package/dist/modes/interactive-layout.js.map +1 -1
- package/dist/modes/interactive-state.d.ts +12 -2
- package/dist/modes/interactive-state.d.ts.map +1 -1
- package/dist/modes/interactive-state.js +49 -2
- package/dist/modes/interactive-state.js.map +1 -1
- package/dist/modes/interactive.d.ts +8 -0
- package/dist/modes/interactive.d.ts.map +1 -1
- package/dist/modes/interactive.js +99 -4
- package/dist/modes/interactive.js.map +1 -1
- package/dist/provider-onboarding.d.ts.map +1 -1
- package/dist/provider-onboarding.js +7 -0
- package/dist/provider-onboarding.js.map +1 -1
- package/dist/startup.d.ts.map +1 -1
- package/dist/startup.js +50 -23
- package/dist/startup.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts +2 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -0
- package/dist/utils/syntax-highlight.js +55 -0
- package/dist/utils/syntax-highlight.js.map +1 -0
- package/package.json +8 -4
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import type { Message } from "@di-code/ai";
|
|
2
2
|
import type { AgentSessionEvent, SessionUsage } from "../core/session.ts";
|
|
3
|
-
export
|
|
3
|
+
export type InteractiveMessage = {
|
|
4
4
|
readonly role: "user" | "assistant";
|
|
5
5
|
readonly text: string;
|
|
6
|
-
}
|
|
6
|
+
} | {
|
|
7
|
+
readonly role: "file_change";
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly path: string;
|
|
10
|
+
readonly kind: "edit" | "write";
|
|
11
|
+
readonly removed: readonly string[];
|
|
12
|
+
readonly added: readonly string[];
|
|
13
|
+
};
|
|
7
14
|
export type InteractiveProcessItem = {
|
|
8
15
|
readonly type: "thinking";
|
|
9
16
|
readonly id: "thinking";
|
|
@@ -31,6 +38,7 @@ export interface InteractiveState {
|
|
|
31
38
|
export declare class InteractiveProjection {
|
|
32
39
|
private readonly messages;
|
|
33
40
|
private readonly messageItems;
|
|
41
|
+
private readonly pendingFileChanges;
|
|
34
42
|
private readonly processItems;
|
|
35
43
|
private readonly toolStatus;
|
|
36
44
|
private spinnerFrame;
|
|
@@ -57,5 +65,7 @@ export declare class InteractiveProjection {
|
|
|
57
65
|
private removeThinking;
|
|
58
66
|
private updateToolStatus;
|
|
59
67
|
private appendCompletedMessage;
|
|
68
|
+
private storeFileChange;
|
|
69
|
+
private commitFileChange;
|
|
60
70
|
}
|
|
61
71
|
//# sourceMappingURL=interactive-state.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive-state.d.ts","sourceRoot":"","sources":["../../src/modes/interactive-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE1E,MAAM,
|
|
1
|
+
{"version":3,"file":"interactive-state.d.ts","sourceRoot":"","sources":["../../src/modes/interactive-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE1E,MAAM,MAAM,kBAAkB,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9D;IACA,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC,CAAC;AAIL,MAAM,MAAM,sBAAsB,GAC/B;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAA;CAAE,GACtD;IACA,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;CAC7C,CAAC;AAEL,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,YAAY,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrD,QAAQ,CAAC,YAAY,EAAE,SAAS,sBAAsB,EAAE,CAAC;IACzD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;CAC7B;AAUD,qBAAa,qBAAqB;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA4B;IACzD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA0C;IAC7E,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAgC;IAC7D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IACxD,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,aAAa,CAAM;IAC3B,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,KAAK,CAYX;IAEF,IAAI,KAAK,IAAI,gBAAgB,CAoB5B;IAED,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAI3C,oBAAoB,IAAI,IAAI;IAU5B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI;IAIxC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAI3C,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAInC,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAIjC,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAInC,cAAc,IAAI,OAAO;IAMzB,qBAAqB,IAAI,IAAI;IAM7B,iBAAiB,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI;IAarD,KAAK,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAmErC,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,sBAAsB;IAwB9B,OAAO,CAAC,eAAe;IA6BvB,OAAO,CAAC,gBAAgB;CAMxB"}
|
|
@@ -7,6 +7,7 @@ function textOf(message) {
|
|
|
7
7
|
export class InteractiveProjection {
|
|
8
8
|
messages = [];
|
|
9
9
|
messageItems = [];
|
|
10
|
+
pendingFileChanges = new Map();
|
|
10
11
|
processItems = [];
|
|
11
12
|
toolStatus = new Map();
|
|
12
13
|
spinnerFrame = 0;
|
|
@@ -33,7 +34,9 @@ export class InteractiveProjection {
|
|
|
33
34
|
get state() {
|
|
34
35
|
return {
|
|
35
36
|
messages: [...this.messages],
|
|
36
|
-
messageItems: this.messageItems.map((message) =>
|
|
37
|
+
messageItems: this.messageItems.map((message) => message.role === "file_change"
|
|
38
|
+
? { ...message, removed: [...message.removed], added: [...message.added] }
|
|
39
|
+
: { ...message }),
|
|
37
40
|
processItems: this.processItems.map((item) => ({ ...item })),
|
|
38
41
|
spinnerFrame: this.spinnerFrame,
|
|
39
42
|
streamingText: this.streamingText,
|
|
@@ -53,6 +56,7 @@ export class InteractiveProjection {
|
|
|
53
56
|
clearVisibleMessages() {
|
|
54
57
|
this.messages.length = 0;
|
|
55
58
|
this.messageItems.length = 0;
|
|
59
|
+
this.pendingFileChanges.clear();
|
|
56
60
|
this.streamingText = "";
|
|
57
61
|
this.processItems.length = 0;
|
|
58
62
|
this.toolStatus.clear();
|
|
@@ -87,6 +91,7 @@ export class InteractiveProjection {
|
|
|
87
91
|
replaceTranscript(messages) {
|
|
88
92
|
this.messages.length = 0;
|
|
89
93
|
this.messageItems.length = 0;
|
|
94
|
+
this.pendingFileChanges.clear();
|
|
90
95
|
this.processItems.length = 0;
|
|
91
96
|
this.toolStatus.clear();
|
|
92
97
|
this.spinnerFrame = 0;
|
|
@@ -137,10 +142,12 @@ export class InteractiveProjection {
|
|
|
137
142
|
status: "running",
|
|
138
143
|
});
|
|
139
144
|
this.toolStatus.set(event.toolCallId, `${event.toolName}: running`);
|
|
145
|
+
this.storeFileChange(event.toolCallId, event.toolName, event.arguments);
|
|
140
146
|
return;
|
|
141
147
|
case "tool_execution_end":
|
|
142
148
|
this.updateToolStatus(event.toolCallId, event.result.isError ? "error" : "done");
|
|
143
149
|
this.toolStatus.set(event.toolCallId, `${event.toolName}: ${event.result.isError ? "error" : "done"}`);
|
|
150
|
+
this.commitFileChange(event.toolCallId, event.result.isError);
|
|
144
151
|
return;
|
|
145
152
|
case "message_end":
|
|
146
153
|
this.appendCompletedMessage(event.message);
|
|
@@ -175,8 +182,10 @@ export class InteractiveProjection {
|
|
|
175
182
|
this.processItems[index] = { ...item, status };
|
|
176
183
|
}
|
|
177
184
|
appendCompletedMessage(message) {
|
|
178
|
-
if (message.role === "tool_result")
|
|
185
|
+
if (message.role === "tool_result") {
|
|
186
|
+
this.commitFileChange(message.toolCallId, message.isError);
|
|
179
187
|
return;
|
|
188
|
+
}
|
|
180
189
|
const text = textOf(message);
|
|
181
190
|
if (message.role === "user") {
|
|
182
191
|
if (text.length > 0) {
|
|
@@ -189,10 +198,48 @@ export class InteractiveProjection {
|
|
|
189
198
|
this.messages.push(text);
|
|
190
199
|
this.messageItems.push({ role: "assistant", text });
|
|
191
200
|
}
|
|
201
|
+
for (const content of message.content) {
|
|
202
|
+
if (content.type === "tool_call")
|
|
203
|
+
this.storeFileChange(content.id, content.name, content.arguments);
|
|
204
|
+
}
|
|
192
205
|
if (message.stopReason === "error" || message.stopReason === "aborted")
|
|
193
206
|
this.error = message.errorMessage;
|
|
194
207
|
this.streamingText = "";
|
|
195
208
|
}
|
|
209
|
+
storeFileChange(id, toolName, argumentsValue) {
|
|
210
|
+
const path = argumentsValue.path;
|
|
211
|
+
if (typeof path !== "string")
|
|
212
|
+
return;
|
|
213
|
+
if (toolName === "edit" &&
|
|
214
|
+
typeof argumentsValue.oldText === "string" &&
|
|
215
|
+
typeof argumentsValue.newText === "string") {
|
|
216
|
+
this.pendingFileChanges.set(id, {
|
|
217
|
+
role: "file_change",
|
|
218
|
+
id,
|
|
219
|
+
path,
|
|
220
|
+
kind: "edit",
|
|
221
|
+
removed: argumentsValue.oldText.replaceAll("\r\n", "\n").split("\n"),
|
|
222
|
+
added: argumentsValue.newText.replaceAll("\r\n", "\n").split("\n"),
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
if (toolName === "write" && typeof argumentsValue.content === "string") {
|
|
226
|
+
this.pendingFileChanges.set(id, {
|
|
227
|
+
role: "file_change",
|
|
228
|
+
id,
|
|
229
|
+
path,
|
|
230
|
+
kind: "write",
|
|
231
|
+
removed: [],
|
|
232
|
+
added: argumentsValue.content.replaceAll("\r\n", "\n").split("\n"),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
commitFileChange(id, isError) {
|
|
237
|
+
const change = this.pendingFileChanges.get(id);
|
|
238
|
+
this.pendingFileChanges.delete(id);
|
|
239
|
+
if (!change || isError)
|
|
240
|
+
return;
|
|
241
|
+
this.messageItems.push(change);
|
|
242
|
+
}
|
|
196
243
|
}
|
|
197
244
|
function formatToolCommand(toolName, argumentsValue) {
|
|
198
245
|
const serialized = JSON.stringify(argumentsValue);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive-state.js","sourceRoot":"","sources":["../../src/modes/interactive-state.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Message } from \"@di-code/ai\";\nimport type { AgentSessionEvent, SessionUsage } from \"../core/session.ts\";\n\nexport interface InteractiveMessage {\n\treadonly role: \"user\" | \"assistant\";\n\treadonly text: string;\n}\n\nexport type InteractiveProcessItem =\n\t| { readonly type: \"thinking\"; readonly id: \"thinking\" }\n\t| {\n\t\t\treadonly type: \"tool\";\n\t\t\treadonly id: string;\n\t\t\treadonly command: string;\n\t\t\treadonly status: \"running\" | \"done\" | \"error\";\n\t };\n\nexport interface InteractiveState {\n\treadonly messages: readonly string[];\n\treadonly messageItems: readonly InteractiveMessage[];\n\treadonly processItems: readonly InteractiveProcessItem[];\n\treadonly spinnerFrame: number;\n\treadonly streamingText: string;\n\treadonly toolStatus: readonly string[];\n\treadonly busy: boolean;\n\treadonly error?: string;\n\treadonly queue: readonly string[];\n\treadonly status?: string;\n\treadonly compacting: boolean;\n\treadonly retrying: boolean;\n\treadonly usage: SessionUsage;\n}\n\nfunction textOf(message: Message): string {\n\ttype TextBlock = Extract<Message[\"content\"][number], { type: \"text\" }>;\n\treturn message.content\n\t\t.filter((content): content is TextBlock => content.type === \"text\")\n\t\t.map((content) => content.text)\n\t\t.join(\"\");\n}\n\nexport class InteractiveProjection {\n\tprivate readonly messages: string[] = [];\n\tprivate readonly messageItems: InteractiveMessage[] = [];\n\tprivate readonly processItems: InteractiveProcessItem[] = [];\n\tprivate readonly toolStatus = new Map<string, string>();\n\tprivate spinnerFrame = 0;\n\tprivate streamingText = \"\";\n\tprivate busy = false;\n\tprivate error: string | undefined;\n\tprivate queue: string[] = [];\n\tprivate status: string | undefined;\n\tprivate compacting = false;\n\tprivate retrying = false;\n\tprivate usage: SessionUsage = {\n\t\trequestCount: 0,\n\t\tinputTokens: 0,\n\t\toutputTokens: 0,\n\t\tcacheReadTokens: 0,\n\t\tcacheWriteTokens: 0,\n\t\ttotalTokens: 0,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\testimatedContextTokens: 0,\n\t\tcontextWindow: 0,\n\t\treserveTokens: 0,\n\t\ttriggerTokens: 0,\n\t};\n\n\tget state(): InteractiveState {\n\t\treturn {\n\t\t\tmessages: [...this.messages],\n\t\t\tmessageItems: this.messageItems.map((message) => ({ ...message })),\n\t\t\tprocessItems: this.processItems.map((item) => ({ ...item })),\n\t\t\tspinnerFrame: this.spinnerFrame,\n\t\t\tstreamingText: this.streamingText,\n\t\t\ttoolStatus: [...this.toolStatus.values()],\n\t\t\tbusy: this.busy,\n\t\t\terror: this.error,\n\t\t\tqueue: [...this.queue],\n\t\t\tstatus: this.status,\n\t\t\tcompacting: this.compacting,\n\t\t\tretrying: this.retrying,\n\t\t\tusage: { ...this.usage, cost: { ...this.usage.cost } },\n\t\t};\n\t}\n\n\tsetError(message: string | undefined): void {\n\t\tthis.error = message;\n\t}\n\n\tclearVisibleMessages(): void {\n\t\tthis.messages.length = 0;\n\t\tthis.messageItems.length = 0;\n\t\tthis.streamingText = \"\";\n\t\tthis.processItems.length = 0;\n\t\tthis.toolStatus.clear();\n\t\tthis.error = undefined;\n\t}\n\n\tsetQueue(queue: readonly string[]): void {\n\t\tthis.queue = [...queue];\n\t}\n\n\tsetStatus(status: string | undefined): void {\n\t\tthis.status = status;\n\t}\n\n\tsetCompacting(value: boolean): void {\n\t\tthis.compacting = value;\n\t}\n\n\tsetRetrying(value: boolean): void {\n\t\tthis.retrying = value;\n\t}\n\n\tsetUsage(usage: SessionUsage): void {\n\t\tthis.usage = structuredClone(usage);\n\t}\n\n\tadvanceSpinner(): boolean {\n\t\tif (!this.busy) return false;\n\t\tthis.spinnerFrame = (this.spinnerFrame + 1) % 4;\n\t\treturn true;\n\t}\n\n\tclearTransientProcess(): void {\n\t\tthis.processItems.length = 0;\n\t\tthis.toolStatus.clear();\n\t\tthis.spinnerFrame = 0;\n\t}\n\n\treplaceTranscript(messages: readonly Message[]): void {\n\t\tthis.messages.length = 0;\n\t\tthis.messageItems.length = 0;\n\t\tthis.processItems.length = 0;\n\t\tthis.toolStatus.clear();\n\t\tthis.spinnerFrame = 0;\n\t\tthis.streamingText = \"\";\n\t\tthis.busy = false;\n\t\tthis.error = undefined;\n\t\tfor (const message of messages) this.appendCompletedMessage(message);\n\t}\n\n\tapply(event: AgentSessionEvent): void {\n\t\tswitch (event.type) {\n\t\t\tcase \"agent_start\":\n\t\t\t\tthis.busy = true;\n\t\t\t\tthis.error = undefined;\n\t\t\t\tthis.retrying = false;\n\t\t\t\tthis.processItems.length = 0;\n\t\t\t\tthis.processItems.push({ type: \"thinking\", id: \"thinking\" });\n\t\t\t\tthis.toolStatus.clear();\n\t\t\t\tthis.spinnerFrame = 0;\n\t\t\t\treturn;\n\t\t\tcase \"compaction_start\":\n\t\t\t\tthis.compacting = true;\n\t\t\t\treturn;\n\t\t\tcase \"compaction_end\":\n\t\t\t\tthis.compacting = false;\n\t\t\t\tif (!event.success) this.error = event.errorMessage;\n\t\t\t\treturn;\n\t\t\tcase \"usage_update\":\n\t\t\t\tthis.setUsage(event.usage);\n\t\t\t\treturn;\n\t\t\tcase \"message_start\":\n\t\t\t\tif (event.message.role === \"assistant\") this.streamingText = \"\";\n\t\t\t\treturn;\n\t\t\tcase \"message_update\":\n\t\t\t\tif (event.event.type === \"text_delta\") {\n\t\t\t\t\tthis.removeThinking();\n\t\t\t\t\tthis.streamingText += event.event.delta;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase \"tool_execution_start\":\n\t\t\t\tthis.removeThinking();\n\t\t\t\tthis.processItems.push({\n\t\t\t\t\ttype: \"tool\",\n\t\t\t\t\tid: event.toolCallId,\n\t\t\t\t\tcommand: formatToolCommand(event.toolName, event.arguments),\n\t\t\t\t\tstatus: \"running\",\n\t\t\t\t});\n\t\t\t\tthis.toolStatus.set(event.toolCallId, `${event.toolName}: running`);\n\t\t\t\treturn;\n\t\t\tcase \"tool_execution_end\":\n\t\t\t\tthis.updateToolStatus(event.toolCallId, event.result.isError ? \"error\" : \"done\");\n\t\t\t\tthis.toolStatus.set(event.toolCallId, `${event.toolName}: ${event.result.isError ? \"error\" : \"done\"}`);\n\t\t\t\treturn;\n\t\t\tcase \"message_end\":\n\t\t\t\tthis.appendCompletedMessage(event.message);\n\t\t\t\treturn;\n\t\t\tcase \"agent_end\":\n\t\t\t\tthis.busy = false;\n\t\t\t\tthis.retrying = false;\n\t\t\t\tthis.processItems.length = 0;\n\t\t\t\tthis.toolStatus.clear();\n\t\t\t\tthis.spinnerFrame = 0;\n\t\t\t\treturn;\n\t\t\tcase \"turn_start\":\n\t\t\t\tif (this.busy && !this.processItems.some((item) => item.type === \"thinking\")) {\n\t\t\t\t\tthis.processItems.push({ type: \"thinking\", id: \"thinking\" });\n\t\t\t\t\tthis.spinnerFrame = 0;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase \"turn_end\":\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate removeThinking(): void {\n\t\tconst index = this.processItems.findIndex((item) => item.type === \"thinking\");\n\t\tif (index >= 0) this.processItems.splice(index, 1);\n\t}\n\n\tprivate updateToolStatus(id: string, status: \"done\" | \"error\"): void {\n\t\tconst index = this.processItems.findIndex((item) => item.type === \"tool\" && item.id === id);\n\t\tconst item = this.processItems[index];\n\t\tif (index < 0 || item?.type !== \"tool\") return;\n\t\tthis.processItems[index] = { ...item, status };\n\t}\n\n\tprivate appendCompletedMessage(message: Message): void {\n\t\tif (message.role === \"tool_result\") return;\n\t\tconst text = textOf(message);\n\t\tif (message.role === \"user\") {\n\t\t\tif (text.length > 0) {\n\t\t\t\tthis.messages.push(text);\n\t\t\t\tthis.messageItems.push({ role: \"user\", text });\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (text.length > 0 && message.stopReason !== \"tool_use\") {\n\t\t\tthis.messages.push(text);\n\t\t\tthis.messageItems.push({ role: \"assistant\", text });\n\t\t}\n\t\tif (message.stopReason === \"error\" || message.stopReason === \"aborted\") this.error = message.errorMessage;\n\t\tthis.streamingText = \"\";\n\t}\n}\n\nfunction formatToolCommand(toolName: string, argumentsValue: Record<string, unknown>): string {\n\tconst serialized = JSON.stringify(argumentsValue);\n\treturn serialized === \"{}\" ? toolName : `${toolName} ${serialized}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"interactive-state.js","sourceRoot":"","sources":["../../src/modes/interactive-state.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Message } from \"@di-code/ai\";\nimport type { AgentSessionEvent, SessionUsage } from \"../core/session.ts\";\n\nexport type InteractiveMessage =\n\t| { readonly role: \"user\" | \"assistant\"; readonly text: string }\n\t| {\n\t\t\treadonly role: \"file_change\";\n\t\t\treadonly id: string;\n\t\t\treadonly path: string;\n\t\t\treadonly kind: \"edit\" | \"write\";\n\t\t\treadonly removed: readonly string[];\n\t\t\treadonly added: readonly string[];\n\t };\n\ntype FileChangeCandidate = Extract<InteractiveMessage, { role: \"file_change\" }>;\n\nexport type InteractiveProcessItem =\n\t| { readonly type: \"thinking\"; readonly id: \"thinking\" }\n\t| {\n\t\t\treadonly type: \"tool\";\n\t\t\treadonly id: string;\n\t\t\treadonly command: string;\n\t\t\treadonly status: \"running\" | \"done\" | \"error\";\n\t };\n\nexport interface InteractiveState {\n\treadonly messages: readonly string[];\n\treadonly messageItems: readonly InteractiveMessage[];\n\treadonly processItems: readonly InteractiveProcessItem[];\n\treadonly spinnerFrame: number;\n\treadonly streamingText: string;\n\treadonly toolStatus: readonly string[];\n\treadonly busy: boolean;\n\treadonly error?: string;\n\treadonly queue: readonly string[];\n\treadonly status?: string;\n\treadonly compacting: boolean;\n\treadonly retrying: boolean;\n\treadonly usage: SessionUsage;\n}\n\nfunction textOf(message: Message): string {\n\ttype TextBlock = Extract<Message[\"content\"][number], { type: \"text\" }>;\n\treturn message.content\n\t\t.filter((content): content is TextBlock => content.type === \"text\")\n\t\t.map((content) => content.text)\n\t\t.join(\"\");\n}\n\nexport class InteractiveProjection {\n\tprivate readonly messages: string[] = [];\n\tprivate readonly messageItems: InteractiveMessage[] = [];\n\tprivate readonly pendingFileChanges = new Map<string, FileChangeCandidate>();\n\tprivate readonly processItems: InteractiveProcessItem[] = [];\n\tprivate readonly toolStatus = new Map<string, string>();\n\tprivate spinnerFrame = 0;\n\tprivate streamingText = \"\";\n\tprivate busy = false;\n\tprivate error: string | undefined;\n\tprivate queue: string[] = [];\n\tprivate status: string | undefined;\n\tprivate compacting = false;\n\tprivate retrying = false;\n\tprivate usage: SessionUsage = {\n\t\trequestCount: 0,\n\t\tinputTokens: 0,\n\t\toutputTokens: 0,\n\t\tcacheReadTokens: 0,\n\t\tcacheWriteTokens: 0,\n\t\ttotalTokens: 0,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\testimatedContextTokens: 0,\n\t\tcontextWindow: 0,\n\t\treserveTokens: 0,\n\t\ttriggerTokens: 0,\n\t};\n\n\tget state(): InteractiveState {\n\t\treturn {\n\t\t\tmessages: [...this.messages],\n\t\t\tmessageItems: this.messageItems.map((message) =>\n\t\t\t\tmessage.role === \"file_change\"\n\t\t\t\t\t? { ...message, removed: [...message.removed], added: [...message.added] }\n\t\t\t\t\t: { ...message },\n\t\t\t),\n\t\t\tprocessItems: this.processItems.map((item) => ({ ...item })),\n\t\t\tspinnerFrame: this.spinnerFrame,\n\t\t\tstreamingText: this.streamingText,\n\t\t\ttoolStatus: [...this.toolStatus.values()],\n\t\t\tbusy: this.busy,\n\t\t\terror: this.error,\n\t\t\tqueue: [...this.queue],\n\t\t\tstatus: this.status,\n\t\t\tcompacting: this.compacting,\n\t\t\tretrying: this.retrying,\n\t\t\tusage: { ...this.usage, cost: { ...this.usage.cost } },\n\t\t};\n\t}\n\n\tsetError(message: string | undefined): void {\n\t\tthis.error = message;\n\t}\n\n\tclearVisibleMessages(): void {\n\t\tthis.messages.length = 0;\n\t\tthis.messageItems.length = 0;\n\t\tthis.pendingFileChanges.clear();\n\t\tthis.streamingText = \"\";\n\t\tthis.processItems.length = 0;\n\t\tthis.toolStatus.clear();\n\t\tthis.error = undefined;\n\t}\n\n\tsetQueue(queue: readonly string[]): void {\n\t\tthis.queue = [...queue];\n\t}\n\n\tsetStatus(status: string | undefined): void {\n\t\tthis.status = status;\n\t}\n\n\tsetCompacting(value: boolean): void {\n\t\tthis.compacting = value;\n\t}\n\n\tsetRetrying(value: boolean): void {\n\t\tthis.retrying = value;\n\t}\n\n\tsetUsage(usage: SessionUsage): void {\n\t\tthis.usage = structuredClone(usage);\n\t}\n\n\tadvanceSpinner(): boolean {\n\t\tif (!this.busy) return false;\n\t\tthis.spinnerFrame = (this.spinnerFrame + 1) % 4;\n\t\treturn true;\n\t}\n\n\tclearTransientProcess(): void {\n\t\tthis.processItems.length = 0;\n\t\tthis.toolStatus.clear();\n\t\tthis.spinnerFrame = 0;\n\t}\n\n\treplaceTranscript(messages: readonly Message[]): void {\n\t\tthis.messages.length = 0;\n\t\tthis.messageItems.length = 0;\n\t\tthis.pendingFileChanges.clear();\n\t\tthis.processItems.length = 0;\n\t\tthis.toolStatus.clear();\n\t\tthis.spinnerFrame = 0;\n\t\tthis.streamingText = \"\";\n\t\tthis.busy = false;\n\t\tthis.error = undefined;\n\t\tfor (const message of messages) this.appendCompletedMessage(message);\n\t}\n\n\tapply(event: AgentSessionEvent): void {\n\t\tswitch (event.type) {\n\t\t\tcase \"agent_start\":\n\t\t\t\tthis.busy = true;\n\t\t\t\tthis.error = undefined;\n\t\t\t\tthis.retrying = false;\n\t\t\t\tthis.processItems.length = 0;\n\t\t\t\tthis.processItems.push({ type: \"thinking\", id: \"thinking\" });\n\t\t\t\tthis.toolStatus.clear();\n\t\t\t\tthis.spinnerFrame = 0;\n\t\t\t\treturn;\n\t\t\tcase \"compaction_start\":\n\t\t\t\tthis.compacting = true;\n\t\t\t\treturn;\n\t\t\tcase \"compaction_end\":\n\t\t\t\tthis.compacting = false;\n\t\t\t\tif (!event.success) this.error = event.errorMessage;\n\t\t\t\treturn;\n\t\t\tcase \"usage_update\":\n\t\t\t\tthis.setUsage(event.usage);\n\t\t\t\treturn;\n\t\t\tcase \"message_start\":\n\t\t\t\tif (event.message.role === \"assistant\") this.streamingText = \"\";\n\t\t\t\treturn;\n\t\t\tcase \"message_update\":\n\t\t\t\tif (event.event.type === \"text_delta\") {\n\t\t\t\t\tthis.removeThinking();\n\t\t\t\t\tthis.streamingText += event.event.delta;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase \"tool_execution_start\":\n\t\t\t\tthis.removeThinking();\n\t\t\t\tthis.processItems.push({\n\t\t\t\t\ttype: \"tool\",\n\t\t\t\t\tid: event.toolCallId,\n\t\t\t\t\tcommand: formatToolCommand(event.toolName, event.arguments),\n\t\t\t\t\tstatus: \"running\",\n\t\t\t\t});\n\t\t\t\tthis.toolStatus.set(event.toolCallId, `${event.toolName}: running`);\n\t\t\t\tthis.storeFileChange(event.toolCallId, event.toolName, event.arguments);\n\t\t\t\treturn;\n\t\t\tcase \"tool_execution_end\":\n\t\t\t\tthis.updateToolStatus(event.toolCallId, event.result.isError ? \"error\" : \"done\");\n\t\t\t\tthis.toolStatus.set(event.toolCallId, `${event.toolName}: ${event.result.isError ? \"error\" : \"done\"}`);\n\t\t\t\tthis.commitFileChange(event.toolCallId, event.result.isError);\n\t\t\t\treturn;\n\t\t\tcase \"message_end\":\n\t\t\t\tthis.appendCompletedMessage(event.message);\n\t\t\t\treturn;\n\t\t\tcase \"agent_end\":\n\t\t\t\tthis.busy = false;\n\t\t\t\tthis.retrying = false;\n\t\t\t\tthis.processItems.length = 0;\n\t\t\t\tthis.toolStatus.clear();\n\t\t\t\tthis.spinnerFrame = 0;\n\t\t\t\treturn;\n\t\t\tcase \"turn_start\":\n\t\t\t\tif (this.busy && !this.processItems.some((item) => item.type === \"thinking\")) {\n\t\t\t\t\tthis.processItems.push({ type: \"thinking\", id: \"thinking\" });\n\t\t\t\t\tthis.spinnerFrame = 0;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase \"turn_end\":\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate removeThinking(): void {\n\t\tconst index = this.processItems.findIndex((item) => item.type === \"thinking\");\n\t\tif (index >= 0) this.processItems.splice(index, 1);\n\t}\n\n\tprivate updateToolStatus(id: string, status: \"done\" | \"error\"): void {\n\t\tconst index = this.processItems.findIndex((item) => item.type === \"tool\" && item.id === id);\n\t\tconst item = this.processItems[index];\n\t\tif (index < 0 || item?.type !== \"tool\") return;\n\t\tthis.processItems[index] = { ...item, status };\n\t}\n\n\tprivate appendCompletedMessage(message: Message): void {\n\t\tif (message.role === \"tool_result\") {\n\t\t\tthis.commitFileChange(message.toolCallId, message.isError);\n\t\t\treturn;\n\t\t}\n\t\tconst text = textOf(message);\n\t\tif (message.role === \"user\") {\n\t\t\tif (text.length > 0) {\n\t\t\t\tthis.messages.push(text);\n\t\t\t\tthis.messageItems.push({ role: \"user\", text });\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (text.length > 0 && message.stopReason !== \"tool_use\") {\n\t\t\tthis.messages.push(text);\n\t\t\tthis.messageItems.push({ role: \"assistant\", text });\n\t\t}\n\t\tfor (const content of message.content) {\n\t\t\tif (content.type === \"tool_call\") this.storeFileChange(content.id, content.name, content.arguments);\n\t\t}\n\t\tif (message.stopReason === \"error\" || message.stopReason === \"aborted\") this.error = message.errorMessage;\n\t\tthis.streamingText = \"\";\n\t}\n\n\tprivate storeFileChange(id: string, toolName: string, argumentsValue: Record<string, unknown>): void {\n\t\tconst path = argumentsValue.path;\n\t\tif (typeof path !== \"string\") return;\n\t\tif (\n\t\t\ttoolName === \"edit\" &&\n\t\t\ttypeof argumentsValue.oldText === \"string\" &&\n\t\t\ttypeof argumentsValue.newText === \"string\"\n\t\t) {\n\t\t\tthis.pendingFileChanges.set(id, {\n\t\t\t\trole: \"file_change\",\n\t\t\t\tid,\n\t\t\t\tpath,\n\t\t\t\tkind: \"edit\",\n\t\t\t\tremoved: argumentsValue.oldText.replaceAll(\"\\r\\n\", \"\\n\").split(\"\\n\"),\n\t\t\t\tadded: argumentsValue.newText.replaceAll(\"\\r\\n\", \"\\n\").split(\"\\n\"),\n\t\t\t});\n\t\t}\n\t\tif (toolName === \"write\" && typeof argumentsValue.content === \"string\") {\n\t\t\tthis.pendingFileChanges.set(id, {\n\t\t\t\trole: \"file_change\",\n\t\t\t\tid,\n\t\t\t\tpath,\n\t\t\t\tkind: \"write\",\n\t\t\t\tremoved: [],\n\t\t\t\tadded: argumentsValue.content.replaceAll(\"\\r\\n\", \"\\n\").split(\"\\n\"),\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate commitFileChange(id: string, isError: boolean): void {\n\t\tconst change = this.pendingFileChanges.get(id);\n\t\tthis.pendingFileChanges.delete(id);\n\t\tif (!change || isError) return;\n\t\tthis.messageItems.push(change);\n\t}\n}\n\nfunction formatToolCommand(toolName: string, argumentsValue: Record<string, unknown>): string {\n\tconst serialized = JSON.stringify(argumentsValue);\n\treturn serialized === \"{}\" ? toolName : `${toolName} ${serialized}`;\n}\n"]}
|
|
@@ -10,6 +10,7 @@ export interface InteractiveModeOptions {
|
|
|
10
10
|
readonly onExit?: () => void;
|
|
11
11
|
readonly sessions?: readonly InteractiveSessionChoice[];
|
|
12
12
|
readonly extensionHost?: ExtensionHost;
|
|
13
|
+
readonly readClipboardImagePath?: (directory?: string) => Promise<string | null>;
|
|
13
14
|
}
|
|
14
15
|
export interface InteractiveSessionChoice {
|
|
15
16
|
readonly id: string;
|
|
@@ -25,6 +26,9 @@ export declare class InteractiveMode {
|
|
|
25
26
|
private readonly tui;
|
|
26
27
|
private readonly sessionChoices;
|
|
27
28
|
private readonly extensionHost?;
|
|
29
|
+
private readonly readClipboardImagePath;
|
|
30
|
+
private clipboardDirectory;
|
|
31
|
+
private readonly clipboardFiles;
|
|
28
32
|
private unsubscribeSession?;
|
|
29
33
|
private sessionSwitching;
|
|
30
34
|
private promptInFlight;
|
|
@@ -39,11 +43,13 @@ export declare class InteractiveMode {
|
|
|
39
43
|
private autocompleteOverlay?;
|
|
40
44
|
private spinnerTimer?;
|
|
41
45
|
private shutdownEmitted;
|
|
46
|
+
private preserveClipboardFiles;
|
|
42
47
|
constructor(options: InteractiveModeOptions);
|
|
43
48
|
start(initialPrompt?: string): void;
|
|
44
49
|
stop(): void;
|
|
45
50
|
private exit;
|
|
46
51
|
private handleCommand;
|
|
52
|
+
private attachClipboardImage;
|
|
47
53
|
private openThemeSelector;
|
|
48
54
|
private openModelSelector;
|
|
49
55
|
private openSessionSelector;
|
|
@@ -58,6 +64,8 @@ export declare class InteractiveMode {
|
|
|
58
64
|
private subscribeToSession;
|
|
59
65
|
private switchSession;
|
|
60
66
|
private submit;
|
|
67
|
+
private cleanupUnreferencedClipboardFiles;
|
|
68
|
+
private cleanupClipboardFiles;
|
|
61
69
|
private handleSlashCommand;
|
|
62
70
|
private formatUsage;
|
|
63
71
|
private runManualCompaction;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/modes/interactive.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/modes/interactive.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
import { extname, resolve } from "node:path";
|
|
3
|
+
import { CombinedAutocompleteProvider, Editor, Key, matchesKey, SelectList, SettingsList, } from "@di-code/tui";
|
|
4
|
+
import { cleanupStaleClipboardImages, clipboardImageDirectory, isClipboardImagePath, readClipboardImagePath, removeClipboardImage, } from "../core/clipboard-image.js";
|
|
5
|
+
import { extractImageAttachments } from "../core/image-input.js";
|
|
2
6
|
import { AutocompleteMenu, InteractiveChat, InteractiveComposer, InteractiveFooter, InteractiveHeader, } from "./interactive-components.js";
|
|
3
7
|
import { InteractiveLayout } from "./interactive-layout.js";
|
|
4
8
|
import { InteractiveProjection } from "./interactive-state.js";
|
|
5
9
|
export { InteractiveProjection } from "./interactive-state.js";
|
|
10
|
+
const IMAGE_EXTENSIONS = new Set([".gif", ".jpeg", ".jpg", ".png", ".webp"]);
|
|
11
|
+
const PASTE_IMAGE_KEY = process.platform === "win32" ? Key.alt("v") : Key.ctrl("v");
|
|
12
|
+
const PASTE_IMAGE_SHORTCUT = process.platform === "win32" ? "Alt+V" : "Ctrl+V";
|
|
13
|
+
function asImageAttachmentReference(pasted, cwd) {
|
|
14
|
+
if (pasted.includes("\n"))
|
|
15
|
+
return pasted;
|
|
16
|
+
const candidate = pasted.trim().replace(/^(?:"([\s\S]*)"|'([\s\S]*)')$/, "$1$2");
|
|
17
|
+
if (!candidate || !IMAGE_EXTENSIONS.has(extname(candidate).toLowerCase()))
|
|
18
|
+
return pasted;
|
|
19
|
+
try {
|
|
20
|
+
if (!statSync(resolve(cwd, candidate)).isFile())
|
|
21
|
+
return pasted;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return pasted;
|
|
25
|
+
}
|
|
26
|
+
return `@"${candidate.replaceAll('"', '\\"')}" `;
|
|
27
|
+
}
|
|
28
|
+
function normalizeDroppedImagePrompt(prompt, cwd) {
|
|
29
|
+
const normalized = asImageAttachmentReference(prompt, cwd);
|
|
30
|
+
if (normalized !== prompt)
|
|
31
|
+
return normalized;
|
|
32
|
+
const prefix = /^((?:(?:[A-Za-z]:[\\/])|(?:\.\.?[\\/])|\/)[\s\S]*?\.(?:gif|jpe?g|png|webp))([\s\S]*)$/i.exec(prompt);
|
|
33
|
+
if (!prefix)
|
|
34
|
+
return prompt;
|
|
35
|
+
const imageReference = asImageAttachmentReference(prefix[1] ?? "", cwd);
|
|
36
|
+
return imageReference === prefix[1]
|
|
37
|
+
? prompt
|
|
38
|
+
: `${imageReference}${normalizeDroppedImagePrompt(prefix[2] ?? "", cwd)}`;
|
|
39
|
+
}
|
|
6
40
|
export class InteractiveMode {
|
|
7
41
|
projection = new InteractiveProjection();
|
|
8
42
|
root;
|
|
@@ -11,6 +45,9 @@ export class InteractiveMode {
|
|
|
11
45
|
tui;
|
|
12
46
|
sessionChoices;
|
|
13
47
|
extensionHost;
|
|
48
|
+
readClipboardImagePath;
|
|
49
|
+
clipboardDirectory;
|
|
50
|
+
clipboardFiles = new Set();
|
|
14
51
|
unsubscribeSession;
|
|
15
52
|
sessionSwitching = false;
|
|
16
53
|
promptInFlight = false;
|
|
@@ -25,12 +62,15 @@ export class InteractiveMode {
|
|
|
25
62
|
autocompleteOverlay;
|
|
26
63
|
spinnerTimer;
|
|
27
64
|
shutdownEmitted = false;
|
|
65
|
+
preserveClipboardFiles = false;
|
|
28
66
|
constructor(options) {
|
|
29
67
|
this.session = options.session;
|
|
30
68
|
this.tui = options.tui;
|
|
31
69
|
this.onExit = options.onExit;
|
|
32
70
|
this.sessionChoices = [...(options.sessions ?? [])];
|
|
33
71
|
this.extensionHost = options.extensionHost;
|
|
72
|
+
this.readClipboardImagePath = options.readClipboardImagePath ?? readClipboardImagePath;
|
|
73
|
+
this.clipboardDirectory = clipboardImageDirectory(this.session.allowedRoot);
|
|
34
74
|
const commands = [
|
|
35
75
|
{ name: "help", description: "Show interactive commands" },
|
|
36
76
|
{ name: "clear", description: "Clear visible messages" },
|
|
@@ -55,11 +95,20 @@ export class InteractiveMode {
|
|
|
55
95
|
this.editor.onEscape = () => this.activeAbort?.abort();
|
|
56
96
|
this.editor.onCommand = (data) => this.handleCommand(data);
|
|
57
97
|
this.editor.onInterrupt = () => this.exit();
|
|
98
|
+
this.editor.onChange = () => {
|
|
99
|
+
if (!this.preserveClipboardFiles) {
|
|
100
|
+
void this.cleanupUnreferencedClipboardFiles().catch((cause) => {
|
|
101
|
+
this.projection.setError(cause instanceof Error ? cause.message : String(cause));
|
|
102
|
+
this.refresh();
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
};
|
|
58
106
|
this.editor.onAutocompleteChange = () => this.updateAutocompleteOverlay();
|
|
59
107
|
const readViewState = () => ({
|
|
60
108
|
...this.projection.state,
|
|
61
109
|
model: this.session.modelId,
|
|
62
110
|
theme: this.theme,
|
|
111
|
+
pasteImageShortcut: PASTE_IMAGE_SHORTCUT,
|
|
63
112
|
});
|
|
64
113
|
this.root = new InteractiveLayout({
|
|
65
114
|
header: new InteractiveHeader(readViewState),
|
|
@@ -80,6 +129,10 @@ export class InteractiveMode {
|
|
|
80
129
|
this.projection.replaceTranscript(this.session.transcript);
|
|
81
130
|
this.projection.setUsage(this.session.usage);
|
|
82
131
|
this.subscribeToSession();
|
|
132
|
+
void cleanupStaleClipboardImages(this.session.allowedRoot).catch((cause) => {
|
|
133
|
+
this.projection.setError(cause instanceof Error ? cause.message : String(cause));
|
|
134
|
+
this.refresh();
|
|
135
|
+
});
|
|
83
136
|
try {
|
|
84
137
|
this.tui.start();
|
|
85
138
|
}
|
|
@@ -105,6 +158,7 @@ export class InteractiveMode {
|
|
|
105
158
|
this.closeAutocompleteOverlay();
|
|
106
159
|
this.unsubscribeSession?.();
|
|
107
160
|
this.unsubscribeSession = undefined;
|
|
161
|
+
void this.cleanupClipboardFiles();
|
|
108
162
|
this.tui.stop({ finalLines: this.root.renderTranscript(this.tui.columns) });
|
|
109
163
|
if (!this.shutdownEmitted) {
|
|
110
164
|
this.shutdownEmitted = true;
|
|
@@ -136,8 +190,29 @@ export class InteractiveMode {
|
|
|
136
190
|
void this.submit(this.lastFailedPrompt, true);
|
|
137
191
|
return true;
|
|
138
192
|
}
|
|
193
|
+
if (matchesKey(data, PASTE_IMAGE_KEY)) {
|
|
194
|
+
void this.attachClipboardImage();
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
139
197
|
return false;
|
|
140
198
|
}
|
|
199
|
+
async attachClipboardImage() {
|
|
200
|
+
try {
|
|
201
|
+
const path = await this.readClipboardImagePath(this.clipboardDirectory);
|
|
202
|
+
if (!path) {
|
|
203
|
+
this.projection.setStatus("Clipboard has no supported image.");
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
this.editor.insertTextAtCursor(path);
|
|
207
|
+
if (isClipboardImagePath(path, this.clipboardDirectory))
|
|
208
|
+
this.clipboardFiles.add(path);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (cause) {
|
|
212
|
+
this.projection.setError(cause instanceof Error ? cause.message : String(cause));
|
|
213
|
+
}
|
|
214
|
+
this.refresh();
|
|
215
|
+
}
|
|
141
216
|
openThemeSelector() {
|
|
142
217
|
const list = new SelectList([
|
|
143
218
|
{ value: "dark", label: "Dark", description: "Dark terminal theme" },
|
|
@@ -310,6 +385,8 @@ export class InteractiveMode {
|
|
|
310
385
|
return;
|
|
311
386
|
this.unsubscribeSession?.();
|
|
312
387
|
this.session = next;
|
|
388
|
+
this.clipboardDirectory = clipboardImageDirectory(next.allowedRoot);
|
|
389
|
+
void cleanupStaleClipboardImages(next.allowedRoot);
|
|
313
390
|
this.queuedPrompts = [];
|
|
314
391
|
this.projection.setQueue([]);
|
|
315
392
|
this.projection.replaceTranscript(next.transcript);
|
|
@@ -354,19 +431,24 @@ export class InteractiveMode {
|
|
|
354
431
|
this.promptInFlight = true;
|
|
355
432
|
this.activeAbort = new AbortController();
|
|
356
433
|
this.projection.setRetrying(retry);
|
|
357
|
-
this.editor.setValue("");
|
|
358
434
|
try {
|
|
359
|
-
const
|
|
435
|
+
const input = await extractImageAttachments(normalizeDroppedImagePrompt(prompt, this.session.allowedRoot), this.session.allowedRoot);
|
|
436
|
+
this.preserveClipboardFiles = true;
|
|
437
|
+
this.editor.setValue("");
|
|
438
|
+
const result = await this.session.promptWithImages(input.text, input.images, this.activeAbort.signal);
|
|
360
439
|
if (result.stopReason === "error" || result.stopReason === "aborted")
|
|
361
440
|
this.lastFailedPrompt = prompt;
|
|
362
|
-
else
|
|
441
|
+
else {
|
|
363
442
|
this.lastFailedPrompt = undefined;
|
|
443
|
+
await this.cleanupClipboardFiles();
|
|
444
|
+
}
|
|
364
445
|
}
|
|
365
446
|
catch (cause) {
|
|
366
447
|
this.lastFailedPrompt = prompt;
|
|
367
448
|
this.projection.setError(cause instanceof Error ? cause.message : String(cause));
|
|
368
449
|
}
|
|
369
450
|
finally {
|
|
451
|
+
this.preserveClipboardFiles = false;
|
|
370
452
|
this.promptInFlight = false;
|
|
371
453
|
this.activeAbort = undefined;
|
|
372
454
|
this.projection.setRetrying(false);
|
|
@@ -378,6 +460,19 @@ export class InteractiveMode {
|
|
|
378
460
|
void this.submit(next);
|
|
379
461
|
}
|
|
380
462
|
}
|
|
463
|
+
async cleanupUnreferencedClipboardFiles() {
|
|
464
|
+
const text = this.editor.getValue();
|
|
465
|
+
const files = [...this.clipboardFiles].filter((path) => !text.includes(path));
|
|
466
|
+
await Promise.all(files.map((path) => removeClipboardImage(path, this.clipboardDirectory)));
|
|
467
|
+
for (const path of files)
|
|
468
|
+
this.clipboardFiles.delete(path);
|
|
469
|
+
}
|
|
470
|
+
async cleanupClipboardFiles() {
|
|
471
|
+
const files = [...this.clipboardFiles];
|
|
472
|
+
await Promise.all(files.map((path) => removeClipboardImage(path, this.clipboardDirectory)));
|
|
473
|
+
for (const path of files)
|
|
474
|
+
this.clipboardFiles.delete(path);
|
|
475
|
+
}
|
|
381
476
|
handleSlashCommand(input) {
|
|
382
477
|
const trimmed = input.slice(1).trim();
|
|
383
478
|
const [rawCommand = "", ...argParts] = trimmed.split(/\s+/);
|