@di-code/coding-agent 0.1.4 → 0.1.6
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 +74 -23
- package/dist/cli.d.ts +15 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +96 -17
- package/dist/cli.js.map +1 -1
- package/dist/core/clipboard-image.d.ts +3 -3
- package/dist/core/clipboard-image.d.ts.map +1 -1
- package/dist/core/clipboard-image.js +26 -13
- package/dist/core/clipboard-image.js.map +1 -1
- package/dist/core/resources/loader.d.ts.map +1 -1
- package/dist/core/resources/loader.js +15 -42
- package/dist/core/resources/loader.js.map +1 -1
- package/dist/core/resources/types.d.ts +6 -9
- package/dist/core/resources/types.d.ts.map +1 -1
- package/dist/core/resources/types.js.map +1 -1
- package/dist/core/session/session-manager.d.ts +12 -1
- package/dist/core/session/session-manager.d.ts.map +1 -1
- package/dist/core/session/session-manager.js +66 -3
- package/dist/core/session/session-manager.js.map +1 -1
- package/dist/core/session/session-storage.d.ts.map +1 -1
- package/dist/core/session/session-storage.js +29 -15
- package/dist/core/session/session-storage.js.map +1 -1
- package/dist/core/session/types.d.ts +6 -1
- package/dist/core/session/types.d.ts.map +1 -1
- package/dist/core/session/types.js +1 -1
- package/dist/core/session/types.js.map +1 -1
- package/dist/core/session.d.ts +21 -1
- package/dist/core/session.d.ts.map +1 -1
- package/dist/core/session.js +81 -12
- package/dist/core/session.js.map +1 -1
- package/dist/core/system-prompt.js +2 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/file-search.d.ts +14 -0
- package/dist/core/tools/file-search.d.ts.map +1 -0
- package/dist/core/tools/file-search.js +99 -0
- package/dist/core/tools/file-search.js.map +1 -0
- package/dist/core/tools/glob.d.ts +11 -0
- package/dist/core/tools/glob.d.ts.map +1 -0
- package/dist/core/tools/glob.js +44 -0
- package/dist/core/tools/glob.js.map +1 -0
- package/dist/core/tools/grep.d.ts +13 -0
- package/dist/core/tools/grep.d.ts.map +1 -0
- package/dist/core/tools/grep.js +67 -0
- package/dist/core/tools/grep.js.map +1 -0
- package/dist/core/tools/load-skill.d.ts +10 -0
- package/dist/core/tools/load-skill.d.ts.map +1 -0
- package/dist/core/tools/load-skill.js +22 -0
- package/dist/core/tools/load-skill.js.map +1 -0
- package/dist/core/tools/path-boundary.d.ts +2 -0
- package/dist/core/tools/path-boundary.d.ts.map +1 -1
- package/dist/core/tools/path-boundary.js +4 -0
- package/dist/core/tools/path-boundary.js.map +1 -1
- package/dist/core/user-data.d.ts +3 -0
- package/dist/core/user-data.d.ts.map +1 -0
- package/dist/core/user-data.js +8 -0
- package/dist/core/user-data.js.map +1 -0
- package/dist/entry.js +13 -0
- package/dist/entry.js.map +1 -1
- package/dist/extensions/runtime.js +2 -2
- package/dist/extensions/runtime.js.map +1 -1
- package/dist/i18n.d.ts +5 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +223 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +17 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +238 -11
- package/dist/main.js.map +1 -1
- package/dist/mcp/config.d.ts +33 -0
- package/dist/mcp/config.d.ts.map +1 -0
- package/dist/mcp/config.js +211 -0
- package/dist/mcp/config.js.map +1 -0
- package/dist/mcp/loader.d.ts +21 -0
- package/dist/mcp/loader.d.ts.map +1 -0
- package/dist/mcp/loader.js +60 -0
- package/dist/mcp/loader.js.map +1 -0
- package/dist/mcp/tool-adapter.d.ts +5 -0
- package/dist/mcp/tool-adapter.d.ts.map +1 -0
- package/dist/mcp/tool-adapter.js +149 -0
- package/dist/mcp/tool-adapter.js.map +1 -0
- package/dist/modes/interactive-components.d.ts +2 -2
- package/dist/modes/interactive-components.d.ts.map +1 -1
- package/dist/modes/interactive-components.js +30 -33
- package/dist/modes/interactive-components.js.map +1 -1
- package/dist/modes/interactive-state.d.ts.map +1 -1
- package/dist/modes/interactive-state.js +1 -0
- package/dist/modes/interactive-state.js.map +1 -1
- package/dist/modes/interactive.d.ts +9 -0
- package/dist/modes/interactive.d.ts.map +1 -1
- package/dist/modes/interactive.js +191 -62
- package/dist/modes/interactive.js.map +1 -1
- package/dist/modes/tree-selector.d.ts +30 -0
- package/dist/modes/tree-selector.d.ts.map +1 -0
- package/dist/modes/tree-selector.js +205 -0
- package/dist/modes/tree-selector.js.map +1 -0
- package/dist/plugins/loader.d.ts +19 -1
- package/dist/plugins/loader.d.ts.map +1 -1
- package/dist/plugins/loader.js +21 -2
- package/dist/plugins/loader.js.map +1 -1
- package/dist/provider-onboarding.d.ts.map +1 -1
- package/dist/provider-onboarding.js +119 -22
- package/dist/provider-onboarding.js.map +1 -1
- package/dist/startup.d.ts +24 -1
- package/dist/startup.d.ts.map +1 -1
- package/dist/startup.js +200 -9
- package/dist/startup.js.map +1 -1
- package/package.json +6 -4
|
@@ -1,31 +1,38 @@
|
|
|
1
1
|
import { statSync } from "node:fs";
|
|
2
|
-
import {
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { extname, join, resolve } from "node:path";
|
|
3
4
|
import { CombinedAutocompleteProvider, Editor, Key, matchesKey, SelectList, SettingsList, } from "@di-code/tui";
|
|
4
5
|
import { cleanupStaleClipboardImages, clipboardImageDirectory, isClipboardImagePath, readClipboardImagePath, removeClipboardImage, } from "../core/clipboard-image.js";
|
|
5
6
|
import { extractImageAttachments } from "../core/image-input.js";
|
|
7
|
+
import { DEFAULT_LOCALE, translate } from "../i18n.js";
|
|
6
8
|
import { showInteractiveProviderOnboarding, } from "../provider-onboarding.js";
|
|
7
|
-
import {
|
|
9
|
+
import { removeGlobalProviderApiKey, saveGlobalLocale } from "../startup.js";
|
|
8
10
|
import { AutocompleteMenu, InteractiveChat, InteractiveComposer, InteractiveFooter, InteractiveHeader, } from "./interactive-components.js";
|
|
9
11
|
import { InteractiveLayout } from "./interactive-layout.js";
|
|
10
12
|
import { InteractiveProjection } from "./interactive-state.js";
|
|
13
|
+
import { TreeSelector } from "./tree-selector.js";
|
|
11
14
|
export { InteractiveProjection } from "./interactive-state.js";
|
|
12
15
|
const IMAGE_EXTENSIONS = new Set([".gif", ".jpeg", ".jpg", ".png", ".webp"]);
|
|
13
16
|
const PASTE_IMAGE_KEY = process.platform === "win32" ? Key.alt("v") : Key.ctrl("v");
|
|
14
17
|
const PASTE_IMAGE_SHORTCUT = process.platform === "win32" ? "Alt+V" : "Ctrl+V";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
function builtinSlashCommands(locale) {
|
|
19
|
+
const t = (key) => translate(locale, key);
|
|
20
|
+
return [
|
|
21
|
+
{ name: "help", description: t("showInteractiveCommands") },
|
|
22
|
+
{ name: "clear", description: t("clearVisibleMessages") },
|
|
23
|
+
{ name: "model", description: t("openModelSelector") },
|
|
24
|
+
{ name: "session", description: t("openSessionSelector") },
|
|
25
|
+
{ name: "tree", description: t("openTreeSelector") },
|
|
26
|
+
{ name: "theme", description: t("openThemeSelector") },
|
|
27
|
+
{ name: "settings", description: t("openSettingsSelector") },
|
|
28
|
+
{ name: "login", description: t("chooseProvider") },
|
|
29
|
+
{ name: "logout", description: t("removeProviderKey") },
|
|
30
|
+
{ name: "compact", description: t("compactContext") },
|
|
31
|
+
{ name: "usage", description: t("showUsage") },
|
|
32
|
+
{ name: "retry", description: t("retryPrompt") },
|
|
33
|
+
{ name: "steer", description: t("steerAgent") },
|
|
34
|
+
];
|
|
35
|
+
}
|
|
29
36
|
function asImageAttachmentReference(pasted, cwd) {
|
|
30
37
|
if (pasted.includes("\n"))
|
|
31
38
|
return pasted;
|
|
@@ -66,6 +73,7 @@ export class InteractiveMode {
|
|
|
66
73
|
sessionChoices;
|
|
67
74
|
extensionHost;
|
|
68
75
|
providerOnboarding;
|
|
76
|
+
agentDir;
|
|
69
77
|
readClipboardImagePath;
|
|
70
78
|
clipboardDirectory;
|
|
71
79
|
clipboardFiles = new Set();
|
|
@@ -85,6 +93,7 @@ export class InteractiveMode {
|
|
|
85
93
|
spinnerTimer;
|
|
86
94
|
shutdownEmitted = false;
|
|
87
95
|
preserveClipboardFiles = false;
|
|
96
|
+
locale;
|
|
88
97
|
constructor(options) {
|
|
89
98
|
this.session = options.session;
|
|
90
99
|
this.projection.configureFilePreview(this.session.allowedRoot, () => this.refresh());
|
|
@@ -93,8 +102,10 @@ export class InteractiveMode {
|
|
|
93
102
|
this.sessionChoices = [...(options.sessions ?? [])];
|
|
94
103
|
this.extensionHost = options.extensionHost;
|
|
95
104
|
this.providerOnboarding = options.providerOnboarding;
|
|
105
|
+
this.agentDir = resolve(options.agentDir ?? join(homedir(), ".di-code"));
|
|
106
|
+
this.locale = options.locale ?? DEFAULT_LOCALE;
|
|
96
107
|
this.readClipboardImagePath = options.readClipboardImagePath ?? readClipboardImagePath;
|
|
97
|
-
this.clipboardDirectory = clipboardImageDirectory(this.session.allowedRoot);
|
|
108
|
+
this.clipboardDirectory = clipboardImageDirectory(this.agentDir, this.session.allowedRoot);
|
|
98
109
|
const autocomplete = {
|
|
99
110
|
getSuggestions: (context, autocompleteOptions) => new CombinedAutocompleteProvider(this.listSlashCommands(), this.session.allowedRoot).getSuggestions(context, autocompleteOptions),
|
|
100
111
|
applyCompletion: (context, item, prefix) => new CombinedAutocompleteProvider(this.listSlashCommands(), this.session.allowedRoot).applyCompletion(context, item, prefix),
|
|
@@ -120,6 +131,7 @@ export class InteractiveMode {
|
|
|
120
131
|
...this.projection.state,
|
|
121
132
|
model: `${this.session.modelId}${this.session.thinkingLevel ? `(${this.session.thinkingLevel})` : ""}`,
|
|
122
133
|
theme: this.theme,
|
|
134
|
+
locale: this.locale,
|
|
123
135
|
pasteImageShortcut: PASTE_IMAGE_SHORTCUT,
|
|
124
136
|
});
|
|
125
137
|
this.root = new InteractiveLayout({
|
|
@@ -141,7 +153,7 @@ export class InteractiveMode {
|
|
|
141
153
|
this.projection.replaceTranscript(this.session.transcript);
|
|
142
154
|
this.projection.setUsage(this.session.usage);
|
|
143
155
|
this.subscribeToSession();
|
|
144
|
-
void cleanupStaleClipboardImages(this.session.allowedRoot).catch((cause) => {
|
|
156
|
+
void cleanupStaleClipboardImages(this.agentDir, this.session.allowedRoot).catch((cause) => {
|
|
145
157
|
this.projection.setError(cause instanceof Error ? cause.message : String(cause));
|
|
146
158
|
this.refresh();
|
|
147
159
|
});
|
|
@@ -227,7 +239,7 @@ export class InteractiveMode {
|
|
|
227
239
|
try {
|
|
228
240
|
const path = await this.readClipboardImagePath(this.clipboardDirectory);
|
|
229
241
|
if (!path) {
|
|
230
|
-
this.projection.setStatus("Clipboard has no supported image.");
|
|
242
|
+
this.projection.setStatus(this.locale === "zh-CN" ? "剪贴板中没有受支持的图片。" : "Clipboard has no supported image.");
|
|
231
243
|
}
|
|
232
244
|
else {
|
|
233
245
|
this.editor.insertTextAtCursor(path);
|
|
@@ -241,9 +253,10 @@ export class InteractiveMode {
|
|
|
241
253
|
this.refresh();
|
|
242
254
|
}
|
|
243
255
|
openThemeSelector() {
|
|
256
|
+
const t = (key) => translate(this.locale, key);
|
|
244
257
|
const list = new SelectList([
|
|
245
|
-
{ value: "dark", label: "
|
|
246
|
-
{ value: "light", label: "
|
|
258
|
+
{ value: "dark", label: t("dark"), description: t("darkTheme") },
|
|
259
|
+
{ value: "light", label: t("light"), description: t("lightTheme") },
|
|
247
260
|
]);
|
|
248
261
|
list.onSelect = (item) => {
|
|
249
262
|
this.theme = item.value === "light" ? "light" : "dark";
|
|
@@ -275,11 +288,12 @@ export class InteractiveMode {
|
|
|
275
288
|
this.showOverlay(list);
|
|
276
289
|
}
|
|
277
290
|
openSessionSelector() {
|
|
291
|
+
const t = (key) => translate(this.locale, key);
|
|
278
292
|
const list = new SelectList([
|
|
279
293
|
{
|
|
280
294
|
value: "__current__",
|
|
281
|
-
label: "
|
|
282
|
-
description: this.session.sessionFile ?? "
|
|
295
|
+
label: t("currentSession"),
|
|
296
|
+
description: this.session.sessionFile ?? t("inMemorySession"),
|
|
283
297
|
},
|
|
284
298
|
...this.sessionChoices.map((choice) => ({
|
|
285
299
|
value: choice.id,
|
|
@@ -303,28 +317,124 @@ export class InteractiveMode {
|
|
|
303
317
|
list.onCancel = () => this.closeOverlay();
|
|
304
318
|
this.showOverlay(list);
|
|
305
319
|
}
|
|
320
|
+
openTreeSelector() {
|
|
321
|
+
if (this.promptInFlight || this.compactionInFlight || this.sessionSwitching) {
|
|
322
|
+
this.projection.setError(this.locale === "zh-CN"
|
|
323
|
+
? "提示词运行时不能浏览会话树。"
|
|
324
|
+
: "Cannot browse the session tree while a prompt is running.");
|
|
325
|
+
this.refresh();
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (this.session.sessionTree.length === 0) {
|
|
329
|
+
this.projection.setStatus(this.locale === "zh-CN" ? "当前会话还没有历史节点。" : "The current session has no history nodes.");
|
|
330
|
+
this.refresh();
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const selector = new TreeSelector({
|
|
334
|
+
nodes: this.session.sessionTree,
|
|
335
|
+
leafId: this.session.sessionLeafId,
|
|
336
|
+
locale: this.locale,
|
|
337
|
+
onContinue: (entry) => {
|
|
338
|
+
this.closeOverlay();
|
|
339
|
+
void this.navigateTreeEntry(entry.id);
|
|
340
|
+
},
|
|
341
|
+
onEdit: (entry) => {
|
|
342
|
+
if (entry.type !== "message" || entry.message.role !== "user") {
|
|
343
|
+
this.projection.setError(this.locale === "zh-CN" ? "只能编辑历史用户消息。" : "Only historical user messages can be edited.");
|
|
344
|
+
this.refresh();
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
this.closeOverlay();
|
|
348
|
+
void this.navigateTreeEntry(entry.id);
|
|
349
|
+
},
|
|
350
|
+
onSummarize: (entry) => {
|
|
351
|
+
this.closeOverlay();
|
|
352
|
+
void this.summarizeTreeBranch(entry.id);
|
|
353
|
+
},
|
|
354
|
+
onCancel: () => this.closeOverlay(),
|
|
355
|
+
});
|
|
356
|
+
this.showOverlay(selector, true, "80%");
|
|
357
|
+
}
|
|
358
|
+
async navigateTreeEntry(entryId) {
|
|
359
|
+
try {
|
|
360
|
+
const result = await this.session.navigateTree(entryId);
|
|
361
|
+
if (!this.started)
|
|
362
|
+
return false;
|
|
363
|
+
this.projection.replaceTranscript(this.session.transcript);
|
|
364
|
+
this.editor.setValue(result.editorText ?? "");
|
|
365
|
+
this.projection.setStatus(result.imagesOmitted
|
|
366
|
+
? this.locale === "zh-CN"
|
|
367
|
+
? "已恢复文本;请重新附加图片。"
|
|
368
|
+
: "Text restored; reattach images before sending."
|
|
369
|
+
: `tree=${result.selectedEntryId}`);
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
catch (cause) {
|
|
373
|
+
this.projection.setError(cause instanceof Error ? cause.message : String(cause));
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
finally {
|
|
377
|
+
this.refresh();
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
async summarizeTreeBranch(entryId) {
|
|
381
|
+
if (this.compactionInFlight || this.promptInFlight)
|
|
382
|
+
return;
|
|
383
|
+
this.compactionInFlight = true;
|
|
384
|
+
this.projection.setStatus(this.locale === "zh-CN" ? "正在为分支生成摘要。" : "Summarizing selected branch.");
|
|
385
|
+
this.refresh();
|
|
386
|
+
try {
|
|
387
|
+
if (!(await this.navigateTreeEntry(entryId)))
|
|
388
|
+
return;
|
|
389
|
+
await this.session.compact();
|
|
390
|
+
this.editor.setValue("");
|
|
391
|
+
this.projection.replaceTranscript(this.session.transcript);
|
|
392
|
+
this.projection.setStatus(this.locale === "zh-CN" ? "摘要分支已创建。" : "Summary branch created.");
|
|
393
|
+
}
|
|
394
|
+
catch (cause) {
|
|
395
|
+
this.projection.setError(cause instanceof Error ? cause.message : String(cause));
|
|
396
|
+
}
|
|
397
|
+
finally {
|
|
398
|
+
this.compactionInFlight = false;
|
|
399
|
+
this.refresh();
|
|
400
|
+
}
|
|
401
|
+
}
|
|
306
402
|
openSettingsSelector() {
|
|
403
|
+
const t = (key) => translate(this.locale, key);
|
|
307
404
|
const list = new SettingsList([
|
|
308
405
|
{
|
|
309
406
|
id: "compaction",
|
|
310
|
-
label: "
|
|
311
|
-
currentValue: this.session.compactionEnabled ? "on" : "off",
|
|
312
|
-
values: ["on", "off"],
|
|
313
|
-
description: "Summarize older persisted context before the model limit is reached.",
|
|
407
|
+
label: t("contextCompaction"),
|
|
408
|
+
currentValue: this.session.compactionEnabled ? t("on") : t("off"),
|
|
409
|
+
values: [t("on"), t("off")],
|
|
314
410
|
},
|
|
315
|
-
|
|
411
|
+
{
|
|
412
|
+
id: "locale",
|
|
413
|
+
label: t("language"),
|
|
414
|
+
currentValue: this.locale,
|
|
415
|
+
values: ["en", "zh-CN"],
|
|
416
|
+
},
|
|
417
|
+
], { title: t("settings") });
|
|
316
418
|
list.onChange = (id, value) => {
|
|
419
|
+
if (id === "locale") {
|
|
420
|
+
this.locale = value === "zh-CN" ? "zh-CN" : "en";
|
|
421
|
+
void saveGlobalLocale(this.agentDir, this.locale).catch((cause) => {
|
|
422
|
+
this.projection.setError(cause instanceof Error ? cause.message : String(cause));
|
|
423
|
+
this.refresh();
|
|
424
|
+
});
|
|
425
|
+
this.closeOverlay();
|
|
426
|
+
this.openSettingsSelector();
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
317
429
|
if (id !== "compaction")
|
|
318
430
|
return;
|
|
319
431
|
try {
|
|
320
|
-
const enabled = this.session.setCompactionEnabled(value === "on");
|
|
321
|
-
list.updateValue(id, enabled ? "on" : "off");
|
|
322
|
-
this.projection.setStatus(enabled === (value === "on")
|
|
323
|
-
? `compaction=${enabled ? "on" : "off"}`
|
|
324
|
-
: "compaction unavailable without a persisted session");
|
|
432
|
+
const enabled = this.session.setCompactionEnabled(value === t("on"));
|
|
433
|
+
list.updateValue(id, enabled ? t("on") : t("off"));
|
|
434
|
+
this.projection.setStatus(enabled === (value === t("on")) ? `compaction=${enabled ? t("on") : t("off")}` : t("compactionUnavailable"));
|
|
325
435
|
}
|
|
326
436
|
catch (cause) {
|
|
327
|
-
list.updateValue(id, this.session.compactionEnabled ? "on" : "off");
|
|
437
|
+
list.updateValue(id, this.session.compactionEnabled ? t("on") : t("off"));
|
|
328
438
|
this.projection.setError(cause instanceof Error ? cause.message : String(cause));
|
|
329
439
|
this.closeOverlay();
|
|
330
440
|
}
|
|
@@ -333,15 +443,16 @@ export class InteractiveMode {
|
|
|
333
443
|
list.onCancel = () => this.closeOverlay();
|
|
334
444
|
this.showOverlay(list);
|
|
335
445
|
}
|
|
336
|
-
showOverlay(component) {
|
|
446
|
+
showOverlay(component, preserveLastLine = false, maxHeight = "60%") {
|
|
337
447
|
this.editor.cancelAutocomplete();
|
|
338
448
|
this.closeAutocompleteOverlay();
|
|
339
449
|
this.closeOverlay();
|
|
340
450
|
this.overlay = this.tui.showOverlay(component, {
|
|
341
451
|
width: "70%",
|
|
342
|
-
maxHeight
|
|
452
|
+
maxHeight,
|
|
343
453
|
anchor: "center",
|
|
344
454
|
margin: 1,
|
|
455
|
+
preserveLastLine,
|
|
345
456
|
});
|
|
346
457
|
}
|
|
347
458
|
closeOverlay() {
|
|
@@ -361,7 +472,7 @@ export class InteractiveMode {
|
|
|
361
472
|
const editorBounds = this.root.getEditorBounds(this.tui.columns);
|
|
362
473
|
this.autocompleteOverlay = this.tui.showOverlay(menu, {
|
|
363
474
|
width: "55%",
|
|
364
|
-
maxHeight:
|
|
475
|
+
maxHeight: 9,
|
|
365
476
|
anchor: "bottom-left",
|
|
366
477
|
placement: {
|
|
367
478
|
anchorRow: editorBounds.end - 1,
|
|
@@ -369,6 +480,7 @@ export class InteractiveMode {
|
|
|
369
480
|
preferred: "below",
|
|
370
481
|
},
|
|
371
482
|
margin: 1,
|
|
483
|
+
preserveLastLine: true,
|
|
372
484
|
nonCapturing: true,
|
|
373
485
|
});
|
|
374
486
|
this.refresh();
|
|
@@ -399,6 +511,11 @@ export class InteractiveMode {
|
|
|
399
511
|
this.unsubscribeSession = this.session.subscribeSession((event) => {
|
|
400
512
|
if (!this.started)
|
|
401
513
|
return;
|
|
514
|
+
if (event.type === "tree_navigated") {
|
|
515
|
+
this.projection.replaceTranscript(this.session.transcript);
|
|
516
|
+
this.refresh();
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
402
519
|
if (event.type === "queue_update") {
|
|
403
520
|
this.steeringPrompts = [...event.steering];
|
|
404
521
|
this.refreshQueue();
|
|
@@ -411,7 +528,7 @@ export class InteractiveMode {
|
|
|
411
528
|
}
|
|
412
529
|
async switchSession(choice) {
|
|
413
530
|
if (this.promptInFlight || this.sessionSwitching) {
|
|
414
|
-
this.projection.setError("Cannot switch sessions while a prompt is running.");
|
|
531
|
+
this.projection.setError(this.locale === "zh-CN" ? "提示词运行时不能切换会话。" : "Cannot switch sessions while a prompt is running.");
|
|
415
532
|
this.refresh();
|
|
416
533
|
return;
|
|
417
534
|
}
|
|
@@ -425,8 +542,8 @@ export class InteractiveMode {
|
|
|
425
542
|
this.unsubscribeSession?.();
|
|
426
543
|
this.session = next;
|
|
427
544
|
this.projection.configureFilePreview(next.allowedRoot, () => this.refresh());
|
|
428
|
-
this.clipboardDirectory = clipboardImageDirectory(next.allowedRoot);
|
|
429
|
-
void cleanupStaleClipboardImages(next.allowedRoot);
|
|
545
|
+
this.clipboardDirectory = clipboardImageDirectory(this.agentDir, next.allowedRoot);
|
|
546
|
+
void cleanupStaleClipboardImages(this.agentDir, next.allowedRoot);
|
|
430
547
|
this.queuedPrompts = [];
|
|
431
548
|
this.steeringPrompts = [];
|
|
432
549
|
this.refreshQueue();
|
|
@@ -458,12 +575,16 @@ export class InteractiveMode {
|
|
|
458
575
|
return;
|
|
459
576
|
}
|
|
460
577
|
if (this.sessionSwitching) {
|
|
461
|
-
this.projection.setError(
|
|
578
|
+
this.projection.setError(this.locale === "zh-CN"
|
|
579
|
+
? "会话正在打开,请等待后再提交提示词。"
|
|
580
|
+
: "A session is opening; wait before submitting a prompt.");
|
|
462
581
|
this.refresh();
|
|
463
582
|
return;
|
|
464
583
|
}
|
|
465
584
|
if (this.compactionInFlight) {
|
|
466
|
-
this.projection.setError(
|
|
585
|
+
this.projection.setError(this.locale === "zh-CN"
|
|
586
|
+
? "正在压缩上下文,请等待后再提交提示词。"
|
|
587
|
+
: "A compaction is running; wait before submitting a prompt.");
|
|
467
588
|
this.refresh();
|
|
468
589
|
return;
|
|
469
590
|
}
|
|
@@ -509,22 +630,28 @@ export class InteractiveMode {
|
|
|
509
630
|
async submitSteering(text) {
|
|
510
631
|
const prompt = text.trim();
|
|
511
632
|
if (prompt.length === 0) {
|
|
512
|
-
this.projection.setError("Steering content must not be empty.");
|
|
633
|
+
this.projection.setError(this.locale === "zh-CN" ? "引导内容不能为空。" : "Steering content must not be empty.");
|
|
513
634
|
this.refresh();
|
|
514
635
|
return;
|
|
515
636
|
}
|
|
516
637
|
if (this.sessionSwitching) {
|
|
517
|
-
this.projection.setError(
|
|
638
|
+
this.projection.setError(this.locale === "zh-CN"
|
|
639
|
+
? "会话正在打开,请等待后再引导 Agent。"
|
|
640
|
+
: "A session is opening; wait before steering the agent.");
|
|
518
641
|
this.refresh();
|
|
519
642
|
return;
|
|
520
643
|
}
|
|
521
644
|
if (this.compactionInFlight) {
|
|
522
|
-
this.projection.setError(
|
|
645
|
+
this.projection.setError(this.locale === "zh-CN"
|
|
646
|
+
? "正在压缩上下文,请等待后再引导 Agent。"
|
|
647
|
+
: "A compaction is running; wait before steering the agent.");
|
|
523
648
|
this.refresh();
|
|
524
649
|
return;
|
|
525
650
|
}
|
|
526
651
|
if (!this.promptInFlight) {
|
|
527
|
-
this.projection.setError(
|
|
652
|
+
this.projection.setError(this.locale === "zh-CN"
|
|
653
|
+
? "只能在提示词运行时引导 Agent。"
|
|
654
|
+
: "Steering is only available while a prompt is running.");
|
|
528
655
|
this.refresh();
|
|
529
656
|
return;
|
|
530
657
|
}
|
|
@@ -570,7 +697,7 @@ export class InteractiveMode {
|
|
|
570
697
|
break;
|
|
571
698
|
case "clear":
|
|
572
699
|
this.projection.clearVisibleMessages();
|
|
573
|
-
this.projection.setStatus(
|
|
700
|
+
this.projection.setStatus(translate(this.locale, "visibleMessagesCleared"));
|
|
574
701
|
break;
|
|
575
702
|
case "model":
|
|
576
703
|
this.openModelSelector();
|
|
@@ -578,6 +705,9 @@ export class InteractiveMode {
|
|
|
578
705
|
case "session":
|
|
579
706
|
this.openSessionSelector();
|
|
580
707
|
return;
|
|
708
|
+
case "tree":
|
|
709
|
+
this.openTreeSelector();
|
|
710
|
+
return;
|
|
581
711
|
case "theme":
|
|
582
712
|
this.openThemeSelector();
|
|
583
713
|
return;
|
|
@@ -600,7 +730,7 @@ export class InteractiveMode {
|
|
|
600
730
|
if (this.lastFailedPrompt && !this.promptInFlight)
|
|
601
731
|
void this.submit(this.lastFailedPrompt, true);
|
|
602
732
|
else
|
|
603
|
-
this.projection.setStatus(
|
|
733
|
+
this.projection.setStatus(translate(this.locale, "nothingToRetry"));
|
|
604
734
|
break;
|
|
605
735
|
default: {
|
|
606
736
|
if (this.extensionHost?.listCommands().some((entry) => entry.name === command)) {
|
|
@@ -628,7 +758,11 @@ export class InteractiveMode {
|
|
|
628
758
|
}
|
|
629
759
|
this.editor.cancelAutocomplete();
|
|
630
760
|
this.closeAutocompleteOverlay();
|
|
631
|
-
void showInteractiveProviderOnboarding({
|
|
761
|
+
void showInteractiveProviderOnboarding({
|
|
762
|
+
...this.providerOnboarding,
|
|
763
|
+
configuration: { ...this.providerOnboarding.configuration, locale: this.locale },
|
|
764
|
+
tui: this.tui,
|
|
765
|
+
})
|
|
632
766
|
.then((runtime) => {
|
|
633
767
|
if (!this.started)
|
|
634
768
|
return;
|
|
@@ -660,7 +794,6 @@ export class InteractiveMode {
|
|
|
660
794
|
return;
|
|
661
795
|
}
|
|
662
796
|
const providerId = this.session.providerId;
|
|
663
|
-
const modelId = this.session.modelId;
|
|
664
797
|
try {
|
|
665
798
|
const removed = await removeGlobalProviderApiKey(this.providerOnboarding.agentDir, providerId);
|
|
666
799
|
if (!removed) {
|
|
@@ -668,14 +801,7 @@ export class InteractiveMode {
|
|
|
668
801
|
this.refresh();
|
|
669
802
|
return;
|
|
670
803
|
}
|
|
671
|
-
|
|
672
|
-
const runtime = resolveStartupRuntime({
|
|
673
|
-
...configuration.environment,
|
|
674
|
-
DI_CODE_PROVIDER: providerId,
|
|
675
|
-
DI_CODE_MODEL: modelId,
|
|
676
|
-
}, configuration.providers, configuration.defaults);
|
|
677
|
-
this.session.setRuntime(runtime.provider, runtime.model);
|
|
678
|
-
this.projection.setStatus(`global API key removed for provider=${providerId}; environment variables may still apply`);
|
|
804
|
+
this.projection.setStatus(`global API key removed for provider=${providerId}; it remains active until this session ends`);
|
|
679
805
|
}
|
|
680
806
|
catch (cause) {
|
|
681
807
|
this.projection.setError(cause instanceof Error ? cause.message : String(cause));
|
|
@@ -687,7 +813,7 @@ export class InteractiveMode {
|
|
|
687
813
|
name: `skill:${skill.name}`,
|
|
688
814
|
description: skill.description,
|
|
689
815
|
}));
|
|
690
|
-
return [...
|
|
816
|
+
return [...builtinSlashCommands(this.locale), ...(this.extensionHost?.listCommands() ?? []), ...skills];
|
|
691
817
|
}
|
|
692
818
|
isLoadedSkillCommand(input) {
|
|
693
819
|
const name = /^\/skill:([a-z0-9]+(?:-[a-z0-9]+)*)(?:\s|$)/.exec(input)?.[1];
|
|
@@ -695,20 +821,23 @@ export class InteractiveMode {
|
|
|
695
821
|
}
|
|
696
822
|
formatUsage() {
|
|
697
823
|
const usage = this.session.usage;
|
|
824
|
+
if (this.locale === "zh-CN") {
|
|
825
|
+
return `用量:请求=${usage.requestCount} 输入=${usage.inputTokens} 输出=${usage.outputTokens} 总计=${usage.totalTokens} 费用=$${usage.cost.total.toFixed(4)} 上下文=${usage.estimatedContextTokens}/${usage.contextWindow}`;
|
|
826
|
+
}
|
|
698
827
|
return `usage: requests=${usage.requestCount} input=${usage.inputTokens} output=${usage.outputTokens} total=${usage.totalTokens} cost=$${usage.cost.total.toFixed(4)} context=${usage.estimatedContextTokens}/${usage.contextWindow}`;
|
|
699
828
|
}
|
|
700
829
|
async runManualCompaction() {
|
|
701
830
|
if (this.promptInFlight || this.compactionInFlight) {
|
|
702
|
-
this.projection.setError("Cannot compact while a prompt is running.");
|
|
831
|
+
this.projection.setError(this.locale === "zh-CN" ? "提示词运行时不能压缩上下文。" : "Cannot compact while a prompt is running.");
|
|
703
832
|
this.refresh();
|
|
704
833
|
return;
|
|
705
834
|
}
|
|
706
835
|
this.compactionInFlight = true;
|
|
707
|
-
this.projection.setStatus(
|
|
836
|
+
this.projection.setStatus(translate(this.locale, "manualCompactionStarting"));
|
|
708
837
|
this.refresh();
|
|
709
838
|
try {
|
|
710
839
|
await this.session.compact();
|
|
711
|
-
this.projection.setStatus(
|
|
840
|
+
this.projection.setStatus(translate(this.locale, "manualCompactionComplete"));
|
|
712
841
|
}
|
|
713
842
|
catch (cause) {
|
|
714
843
|
this.projection.setError(cause instanceof Error ? cause.message : String(cause));
|