dsh-ssh-tui 0.6.2 → 0.6.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.en.md +51 -0
- package/README.md +43 -2
- package/lib/i18n/en.js +15 -3
- package/lib/i18n/en.js.map +1 -1
- package/lib/i18n/zh.js +15 -3
- package/lib/i18n/zh.js.map +1 -1
- package/lib/index.js +10 -4
- package/lib/index.js.map +1 -1
- package/lib/preset-label.js +73 -0
- package/lib/preset-label.js.map +1 -0
- package/lib/preset-rows.js +96 -0
- package/lib/preset-rows.js.map +1 -0
- package/lib/reasoning.js +9 -4
- package/lib/reasoning.js.map +1 -1
- package/lib/tui.js +81 -15
- package/lib/tui.js.map +1 -1
- package/lib/types/preset-label.d.ts +38 -0
- package/lib/types/preset-rows.d.ts +47 -0
- package/lib/types/reasoning.d.ts +9 -4
- package/lib/types/tui.d.ts +14 -0
- package/package.json +1 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The profile patch that mounts the agent-preset roster `/mode` needs.
|
|
3
|
+
*
|
|
4
|
+
* A terminal profile built on `dsh-base` composes no preset roster (only the
|
|
5
|
+
* Web bundle does), and this plugin's own bundle patch may not mount one: DSH
|
|
6
|
+
* STORE accepts additive rows with plugin-owned ids and no `@deepseek-ai/*`
|
|
7
|
+
* module names. The profile's user layer is the supported home for the row, so
|
|
8
|
+
* both the install script and the running TUI write the same block here — the
|
|
9
|
+
* TUI needs it because `dsh plugin add dsh-ssh-tui@latest` (the in-app update
|
|
10
|
+
* path) never runs `scripts/`, which npm installs do not ship.
|
|
11
|
+
*
|
|
12
|
+
* The roster is not cosmetic: without it `/mode` cannot switch, and the tools
|
|
13
|
+
* the shipped presets own (`ask_user_question`, `present`, PTC's presentation)
|
|
14
|
+
* are absent from the agent's catalog.
|
|
15
|
+
* @module dsh-ssh-tui/preset-rows
|
|
16
|
+
*/
|
|
17
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
18
|
+
import { dirname, join } from 'node:path';
|
|
19
|
+
/**
|
|
20
|
+
* The exact profile patch block that mounts the roster and the two host
|
|
21
|
+
* services the shipped presets need. `scripts/ensure-profile-rows.sh` carries
|
|
22
|
+
* the same text; a test compares the two so they cannot drift.
|
|
23
|
+
*/
|
|
24
|
+
export const ROSTER_PATCH_BLOCK = `# dsh-ssh-tui /mode: the agent-preset roster (standard / minimal / PTC /
|
|
25
|
+
# cordis, plus every preset under $DSH_HOME/.agent-presets) and the two host
|
|
26
|
+
# services the shipped presets need. dsh-base composes no roster in a terminal
|
|
27
|
+
# profile, and a third-party bundle patch may not mount an @deepseek-ai row, so
|
|
28
|
+
# the profile's user layer owns them.
|
|
29
|
+
- insert:
|
|
30
|
+
- id: agent-presets
|
|
31
|
+
name: '@deepseek-ai/dsh-agent-presets'
|
|
32
|
+
config:
|
|
33
|
+
default: standard
|
|
34
|
+
|
|
35
|
+
- id: code-runtime
|
|
36
|
+
name: '@deepseek-ai/dsh-code-runtime-worker-thread'
|
|
37
|
+
|
|
38
|
+
- id: subagent-model-selection-settings
|
|
39
|
+
name: '@deepseek-ai/dsh-tool-subagent/model-selection-settings'
|
|
40
|
+
`;
|
|
41
|
+
/** The profile patch file the roster block belongs in. */
|
|
42
|
+
export function rosterPatchPath(home, profile) {
|
|
43
|
+
return join(home, 'profiles', profile, 'cordis.patch.yml');
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The patch text with the roster block appended, or `undefined` when the file
|
|
47
|
+
* already names the roster row.
|
|
48
|
+
*
|
|
49
|
+
* A file that is exactly `[]` (the profile template) is replaced, so the result
|
|
50
|
+
* stays a valid top-level patch list; anything else keeps its content and the
|
|
51
|
+
* block is appended after a blank line.
|
|
52
|
+
* @param existing - the patch file's current text.
|
|
53
|
+
* @returns the new text, or `undefined` when nothing has to change.
|
|
54
|
+
*/
|
|
55
|
+
export function rosterPatchText(existing) {
|
|
56
|
+
if (/name:\s*'@deepseek-ai\/dsh-agent-presets'/.test(existing))
|
|
57
|
+
return undefined;
|
|
58
|
+
if (/^\s*\[\s*\]\s*$/m.test(existing))
|
|
59
|
+
return existing.replace(/^\s*\[\s*\]\s*$/m, ROSTER_PATCH_BLOCK.trimEnd() + '\n');
|
|
60
|
+
return `${existing.endsWith('\n') ? existing : `${existing}\n`}\n${ROSTER_PATCH_BLOCK}`;
|
|
61
|
+
}
|
|
62
|
+
/** The template a missing profile patch starts from. */
|
|
63
|
+
const PATCH_TEMPLATE = `# Your patch layer for this dsh profile, applied after every bundle layer:
|
|
64
|
+
# a top-level YAML array of loader patch entries (id-targeted config
|
|
65
|
+
# overrides, disables, and insert lists; \`!!js\` expressions allowed).
|
|
66
|
+
[]
|
|
67
|
+
|
|
68
|
+
`;
|
|
69
|
+
/**
|
|
70
|
+
* Mount the roster in one profile, unless it is already composed.
|
|
71
|
+
*
|
|
72
|
+
* Idempotent: a profile whose patch already names the roster row (one bundling
|
|
73
|
+
* `@deepseek-ai/dsh-web-app`, for example) is left untouched. The write is a
|
|
74
|
+
* plain whole-file replace because the file is small and read once at boot; a
|
|
75
|
+
* half-written patch would only be seen by the next launch.
|
|
76
|
+
* @param home - the harness home carrying `profiles/`.
|
|
77
|
+
* @param profile - the profile to patch.
|
|
78
|
+
* @returns `present` when the row already exists, else `written`.
|
|
79
|
+
*/
|
|
80
|
+
export async function ensureRosterRows(home, profile) {
|
|
81
|
+
const path = rosterPatchPath(home, profile);
|
|
82
|
+
let existing = PATCH_TEMPLATE;
|
|
83
|
+
try {
|
|
84
|
+
existing = await readFile(path, 'utf8');
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Missing file: start from the template the launcher would have written.
|
|
88
|
+
}
|
|
89
|
+
const next = rosterPatchText(existing);
|
|
90
|
+
if (next === undefined)
|
|
91
|
+
return 'present';
|
|
92
|
+
await mkdir(dirname(path), { recursive: true });
|
|
93
|
+
await writeFile(path, next, 'utf8');
|
|
94
|
+
return 'written';
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=preset-rows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-rows.js","sourceRoot":"","sources":["../src/preset-rows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEzC;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;CAgBjC,CAAA;AAED,0DAA0D;AAC1D,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,OAAe;IAC3D,OAAO,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAA;AAC5D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,IAAI,2CAA2C,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAA;IAChF,IAAI,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC,OAAO,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAA;IACvH,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,KAAK,kBAAkB,EAAE,CAAA;AACzF,CAAC;AAED,wDAAwD;AACxD,MAAM,cAAc,GAAG;;;;;CAKtB,CAAA;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY,EAAE,OAAe;IAClE,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC3C,IAAI,QAAQ,GAAG,cAAc,CAAA;IAC7B,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;IAC3E,CAAC;IACD,MAAM,IAAI,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAA;IACtC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACxC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC/C,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;IACnC,OAAO,SAAS,CAAA;AAClB,CAAC"}
|
package/lib/reasoning.js
CHANGED
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
* Shared reasoning-effort defaults for the SSH TUI.
|
|
3
3
|
*
|
|
4
4
|
* OpenCode / third-party (llm-pi-ai) routes carry no adapter-level reasoning
|
|
5
|
-
* default
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* needed to render the collapsible `思考中` block. This helper picks a
|
|
5
|
+
* default. Without an explicit effort the model streams its thinking as plain
|
|
6
|
+
* `text` chunks instead of `reasoning` blocks, so the TUI never receives the
|
|
7
|
+
* data needed to render the collapsible `思考中` block. This helper picks a
|
|
9
8
|
* supported default so the fold has data to show.
|
|
9
|
+
*
|
|
10
|
+
* `llm-deepseek` is not covered here: its adapter always reports a
|
|
11
|
+
* `defaultEffort`, which the LLM runtime materializes into the request on its
|
|
12
|
+
* own. Since the 0.5.1 STORE compliance trim dropped the bundle patch's
|
|
13
|
+
* `reasoningEffort: max` row override, that adapter default is `high` unless a
|
|
14
|
+
* deployment sets it (or `/effort` picks one, remembered per route).
|
|
10
15
|
*/
|
|
11
16
|
/**
|
|
12
17
|
* Pick a default reasoning effort for a provider/model when none is selected.
|
package/lib/reasoning.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reasoning.js","sourceRoot":"","sources":["../src/reasoning.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"reasoning.js","sourceRoot":"","sources":["../src/reasoning.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,GAAe,EACf,QAAgB,EAChB,KAAa;IAEb,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;QACxD,MAAM,OAAO,GAAG,IAAI,EAAE,SAAS,EAAE,OAAO,IAAI,EAAE,CAAA;QAC9C,IAAI,IAAI,EAAE,SAAS,EAAE,aAAa,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC,aAAa,CAAA;QACrF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,KAAK,CAAC,CAAA;QAC5D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAA;QACzC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,KAAK,CAAC;eACvD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,OAAO,CAAC;eAC5C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,MAAM,CAAC;eAC3C,MAAM,CAAC,CAAC,CAAC,CAAA;QACd,OAAO,SAAS,EAAE,EAAE,CAAA;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC"}
|
package/lib/tui.js
CHANGED
|
@@ -31,6 +31,8 @@ import { buildReviewUserMessage, parseReviewOutput, reviewSystemPrompt } from '.
|
|
|
31
31
|
import { loadProviderCatalog, mergeProviderEntries } from './provider-catalog.js';
|
|
32
32
|
import { formatFooterCwd } from './session-list.js';
|
|
33
33
|
import { collectDiag, formatDiag } from './diag.js';
|
|
34
|
+
import { presetLabel, profileFromArgv } from './preset-label.js';
|
|
35
|
+
import { ensureRosterRows, rosterPatchPath } from './preset-rows.js';
|
|
34
36
|
import { SessionStatsTracker, statsRowOf } from './stats.js';
|
|
35
37
|
import { QUESTION_OPTION_KEYS, confirmAnswer, inspectClosesOn, moveQuestionCursor, optionsLength, questionSubmit, selectQuestionOptionByKey, } from './dialogs.js';
|
|
36
38
|
import { commandSuggestions, localizedCommands } from './commands.js';
|
|
@@ -417,7 +419,7 @@ export class SshTui {
|
|
|
417
419
|
disposers = [];
|
|
418
420
|
userQuestionDisposer;
|
|
419
421
|
presetId = 'standard';
|
|
420
|
-
presetName = t('mode.standard');
|
|
422
|
+
presetName = t('mode.preset.standard');
|
|
421
423
|
useAlternateScreen;
|
|
422
424
|
agentGone = false;
|
|
423
425
|
onboarding;
|
|
@@ -531,7 +533,7 @@ export class SshTui {
|
|
|
531
533
|
this.headlessDisplay = config.headlessDisplay === true;
|
|
532
534
|
this.disconnectPolicy = config.disconnectPolicy ?? this.readDisconnectPolicy();
|
|
533
535
|
this.presetId = config.presetId ?? 'standard';
|
|
534
|
-
this.presetName = config.presetName
|
|
536
|
+
this.presetName = presetLabel(this.presetId, config.presetName, config.presetTrust);
|
|
535
537
|
this.useAlternateScreen = process.env.DSH_TUI_NO_ALT_SCREEN !== '1' && process.env.DSH_TUI_NO_ALT_SCREEN !== 'true';
|
|
536
538
|
this.paintLink = detectSshSession() ? 'ssh' : 'local';
|
|
537
539
|
this.paintIntervalMs = resolvePaintIntervalMs(config.paintIntervalMs, process.env, {
|
|
@@ -540,6 +542,13 @@ export class SshTui {
|
|
|
540
542
|
this.pushRow({ kind: 'brand-logo' });
|
|
541
543
|
this.pushRow({ kind: 'system', text: t('boot.banner') });
|
|
542
544
|
this.pushRow({ kind: 'system', text: t('boot.help') });
|
|
545
|
+
// The roster is a profile-layer row, so an install that predates it (or an
|
|
546
|
+
// in-app update, which only runs `dsh plugin add`) boots without one. Say
|
|
547
|
+
// so at boot: the banner's localized default hides the missing service, and
|
|
548
|
+
// the loss is not only `/mode` — the preset-owned tools are absent too.
|
|
549
|
+
if (this.ctx.get('agentPresets') === undefined) {
|
|
550
|
+
this.pushRow({ kind: 'system', text: t('mode.bootMissing') });
|
|
551
|
+
}
|
|
543
552
|
if (config.cwdNotice !== undefined && config.cwdNotice !== '') {
|
|
544
553
|
this.pushRow({ kind: /进入|Entered/u.test(config.cwdNotice) ? 'system' : 'error', text: config.cwdNotice });
|
|
545
554
|
}
|
|
@@ -5176,8 +5185,28 @@ export class SshTui {
|
|
|
5176
5185
|
/** /mode: pick an agent preset (standard / minimal / ptc / cordis / routing-suite / ...). */
|
|
5177
5186
|
async runModeCommand(arg = '') {
|
|
5178
5187
|
const agentPresets = this.ctx.get('agentPresets');
|
|
5188
|
+
const direct = arg.trim().toLowerCase();
|
|
5189
|
+
if (direct === 'fix' || direct === 'repair') {
|
|
5190
|
+
await this.repairRoster();
|
|
5191
|
+
return;
|
|
5192
|
+
}
|
|
5179
5193
|
if (agentPresets === undefined) {
|
|
5180
|
-
|
|
5194
|
+
// A terminal profile built on dsh-base composes no roster, and the
|
|
5195
|
+
// plugin's bundle patch may not mount one; only the profile's user layer
|
|
5196
|
+
// can. Report the exact row and offer the in-app repair: the install
|
|
5197
|
+
// scripts are the checkout path, while an npm install and the in-app
|
|
5198
|
+
// `dsh plugin add` update never run them.
|
|
5199
|
+
const profile = profileFromArgv();
|
|
5200
|
+
this.pushRow({
|
|
5201
|
+
kind: 'error',
|
|
5202
|
+
text: [
|
|
5203
|
+
t('mode.missingService'),
|
|
5204
|
+
t('mode.missingServiceHint', {
|
|
5205
|
+
profile,
|
|
5206
|
+
patch: rosterPatchPath(resolveDshHome(), profile),
|
|
5207
|
+
}),
|
|
5208
|
+
].join('\n'),
|
|
5209
|
+
});
|
|
5181
5210
|
this.markDirty();
|
|
5182
5211
|
return;
|
|
5183
5212
|
}
|
|
@@ -5187,12 +5216,16 @@ export class SshTui {
|
|
|
5187
5216
|
this.markDirty();
|
|
5188
5217
|
return;
|
|
5189
5218
|
}
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5219
|
+
// Shipped presets resolve through the TUI dictionary; a user-authored one
|
|
5220
|
+
// keeps the name its own preset.yml published.
|
|
5221
|
+
const labels = presets.map(preset => presetLabel(preset.id, preset.name, preset.trust));
|
|
5222
|
+
const matchesDirect = (preset, label) => preset.id.toLowerCase() === direct
|
|
5223
|
+
|| (preset.name ?? '').trim().toLowerCase() === direct
|
|
5224
|
+
|| label.toLowerCase() === direct;
|
|
5225
|
+
let index = direct === ''
|
|
5226
|
+
? -1
|
|
5227
|
+
: presets.findIndex((preset, at) => matchesDirect(preset, labels[at] ?? preset.id));
|
|
5228
|
+
if (index < 0 && direct !== '') {
|
|
5196
5229
|
this.pushRow({
|
|
5197
5230
|
kind: 'error',
|
|
5198
5231
|
text: t('mode.unknown', { id: arg.trim(), available: presets.map(preset => preset.id).join(', ') }),
|
|
@@ -5200,20 +5233,31 @@ export class SshTui {
|
|
|
5200
5233
|
this.markDirty();
|
|
5201
5234
|
return;
|
|
5202
5235
|
}
|
|
5203
|
-
if (
|
|
5236
|
+
if (index < 0 && direct === '') {
|
|
5204
5237
|
const answer = await this.askQuestion({
|
|
5205
5238
|
id: 'mode-pick',
|
|
5206
5239
|
question: t('mode.pick'),
|
|
5207
|
-
options: presets.map(preset => ({
|
|
5208
|
-
label:
|
|
5209
|
-
description: `${preset.id === this.presetId ? t('mode.currentPrefix') : ''}${preset.description ?? ''}`.trim(),
|
|
5240
|
+
options: presets.map((preset, at) => ({
|
|
5241
|
+
label: labels[at] ?? preset.id,
|
|
5242
|
+
description: `${preset.id === this.presetId ? t('mode.currentPrefix') : ''}${preset.broken === undefined ? preset.description ?? '' : t('mode.brokenSuffix', { reason: preset.broken })}`.trim(),
|
|
5210
5243
|
})),
|
|
5211
5244
|
});
|
|
5212
|
-
|
|
5245
|
+
index = labels.indexOf(answer.selected[0] ?? '');
|
|
5213
5246
|
}
|
|
5247
|
+
if (index < 0)
|
|
5248
|
+
return;
|
|
5249
|
+
const selected = presets[index];
|
|
5214
5250
|
if (selected === undefined)
|
|
5215
5251
|
return;
|
|
5216
|
-
|
|
5252
|
+
if (selected.broken !== undefined) {
|
|
5253
|
+
this.pushRow({
|
|
5254
|
+
kind: 'error',
|
|
5255
|
+
text: t('mode.broken', { name: labels[index] ?? selected.id, reason: selected.broken }),
|
|
5256
|
+
});
|
|
5257
|
+
this.markDirty();
|
|
5258
|
+
return;
|
|
5259
|
+
}
|
|
5260
|
+
const selectedName = labels[index] ?? selected.id;
|
|
5217
5261
|
const hasWork = sessionEvents(this.agent.session).some(event => event.type === 'turn/start');
|
|
5218
5262
|
if (!hasWork) {
|
|
5219
5263
|
await agentPresets.recompose(this.agent.ctx, selected.id);
|
|
@@ -5230,6 +5274,28 @@ export class SshTui {
|
|
|
5230
5274
|
await this.ctx.get('settings')?.update(settingsNamespace('agent-presets'), { default: selected.id });
|
|
5231
5275
|
this.markDirty();
|
|
5232
5276
|
}
|
|
5277
|
+
/**
|
|
5278
|
+
* `/mode fix`: write the roster row into this profile's user patch layer.
|
|
5279
|
+
*
|
|
5280
|
+
* The write only takes effect on the next launch — the loader composes the
|
|
5281
|
+
* patch tree once at boot — so the report says so instead of pretending the
|
|
5282
|
+
* running session gained a roster.
|
|
5283
|
+
*/
|
|
5284
|
+
async repairRoster() {
|
|
5285
|
+
const profile = profileFromArgv();
|
|
5286
|
+
const patch = rosterPatchPath(resolveDshHome(), profile);
|
|
5287
|
+
try {
|
|
5288
|
+
const result = await ensureRosterRows(resolveDshHome(), profile);
|
|
5289
|
+
this.pushRow({
|
|
5290
|
+
kind: 'system',
|
|
5291
|
+
text: result === 'present' ? t('mode.fixPresent', { patch }) : t('mode.fixWritten', { patch }),
|
|
5292
|
+
});
|
|
5293
|
+
}
|
|
5294
|
+
catch (error) {
|
|
5295
|
+
this.pushRow({ kind: 'error', text: t('mode.fixFailed', { patch, error: errorChain(error) }) });
|
|
5296
|
+
}
|
|
5297
|
+
this.markDirty();
|
|
5298
|
+
}
|
|
5233
5299
|
/** Current provider route selected for the running agent. */
|
|
5234
5300
|
currentProvider() {
|
|
5235
5301
|
// `agent.options` is authoritative for the launched agent; the selection
|