@xiaoso/dsh-tool-plus 0.1.9 → 0.1.10-beta.2

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.
Files changed (40) hide show
  1. package/README.en.md +12 -5
  2. package/README.md +11 -4
  3. package/THIRD_PARTY_NOTICES.md +36 -0
  4. package/lib/client.js +377 -906
  5. package/lib/index.mjs +885 -1087
  6. package/lib/{scrapers-mhvFrvjH.mjs → scrapers-DSArdJ6N.mjs} +1 -1
  7. package/lib/types/client/ToolPlusSection.d.ts +3 -3
  8. package/lib/types/client/forms.d.ts +6 -6
  9. package/lib/types/client/index.d.ts +29 -12
  10. package/lib/types/client/locales.d.ts +1 -1
  11. package/lib/types/client/preset-panel.d.ts +54 -42
  12. package/lib/types/config/settings.d.ts +202 -21
  13. package/lib/types/host/browser-probe-rpc.d.ts +2 -1
  14. package/lib/types/host/preset-host.d.ts +103 -0
  15. package/lib/types/host/preset-rpc.d.ts +37 -72
  16. package/lib/types/presets/compare.d.ts +14 -37
  17. package/lib/types/presets/conflicts.d.ts +59 -0
  18. package/lib/types/presets/delta.d.ts +23 -18
  19. package/lib/types/presets/plan.d.ts +55 -0
  20. package/lib/types/presets/rows.d.ts +84 -0
  21. package/lib/types/presets/status.d.ts +56 -47
  22. package/lib/types/tools/bash/background.d.ts +20 -5
  23. package/lib/types/tools/bash/streaming-output.d.ts +4 -2
  24. package/lib/types/tools/shared/browser-rpc-channel.d.ts +84 -42
  25. package/lib/types/web/client/labels.d.ts +1 -1
  26. package/lib/types/web/client/row-utils.d.ts +26 -1
  27. package/lib/{utils-eiUz9n8I.mjs → utils-DA8R1_Q8.mjs} +1 -1
  28. package/package.json +166 -151
  29. package/presets/tool-plus-ptc.patch.yml +164 -0
  30. package/presets/tool-plus-standard.patch.yml +158 -0
  31. package/lib/types/client/BashPlusCard.d.ts +0 -53
  32. package/lib/types/presets/install.d.ts +0 -44
  33. package/lib/types/presets/paths.d.ts +0 -41
  34. package/lib/types/presets/rewrite.d.ts +0 -86
  35. package/presets/install-presets.mjs +0 -143
  36. package/presets/package.json +0 -42
  37. package/presets/tool-plus-ptc/agent.cordis.yml +0 -284
  38. package/presets/tool-plus-ptc/preset.yml +0 -3
  39. package/presets/tool-plus-standard/agent.cordis.yml +0 -269
  40. package/presets/tool-plus-standard/preset.yml +0 -3
@@ -1,56 +1,65 @@
1
- import { type PresetDeps } from './paths.ts';
2
- import { type PresetDelta } from './delta.ts';
3
- /** 预设来源:我们随包的两份 / 用户自建 / 官方随附。 */
4
- export type PresetSource = 'ours' | 'user' | 'shipped';
5
1
  /**
6
- * roster 的一行(调用方从 `ctx.agentPresets.list()` 映射而来,字段名对齐
7
- * `dsh-agent-presets` 的 `AgentPreset`:`path` 是**组合文件**的绝对路径)。
2
+ * preset 状态:把宿主交给我们的事实折成设置页要的清单。同步、只读。
3
+ *
4
+ * 事实从哪来(0.1.7 起官方只提供这两处,`path` / `trust` 都已不存在):
5
+ * - `ctx.configEditor.configuration()` —— 每个**可寻址**的 profile 行给
6
+ * `{entry, inherited, override}`。`inherited` 是随包声明那一层(我们的
7
+ * 模板),`override` 是用户写进 profile 补丁那一层。bundle 补丁 insert 的
8
+ * `preset-*` 行也是根 include 的子行,所以这里拿得到。
9
+ * - `ctx.agentPresets.list()` —— 名册身份与激活诊断(`broken`),**没有**
10
+ * `path`,也**没有** `trust`。
11
+ *
12
+ * 三种来源的旧口径已失效,现在的区分只剩"是不是本插件声明的":
13
+ * - `ours`:id 在 {@link BUNDLED_PRESET_IDS} 里 → 三个动作齐全;
14
+ * - `other`:别人的声明(官方随附,或用户装的别的 bundle)→ 只给"最小更新",
15
+ * 因为我们只该修冲突行,不该替别人重写整份内容。
16
+ * @module @xiaoso/dsh-tool-plus/presets/status
8
17
  */
9
- export interface PresetRosterEntry {
10
- id: string;
11
- trust: 'system' | 'user';
12
- path: string;
13
- name?: string;
14
- description?: string;
15
- broken?: string;
16
- }
17
- /** 一个预设的设置页状态。 */
18
- export interface PresetStatus {
18
+ import { type PresetStatusListValue, type PresetStatusValue } from '../tools/shared/browser-rpc-channel.ts';
19
+ /**
20
+ * 一个 preset 的原始事实(宿主层收集,本模块纯消费)。
21
+ *
22
+ * `inherited` / `override` 是**完整 config 对象**(`{id,name,description,order,plugins}`),
23
+ * 不是 plugins 列表 —— 因为 profile 补丁按行 id 覆盖时替换的是**整份 `config`**,
24
+ * 判断"改没改过"必须看整份,不能只看 plugins。
25
+ */
26
+ export interface PresetFacts {
27
+ /** 预设身份(`config.id`)。 */
19
28
  id: string;
29
+ /** profile 补丁里那一行的 id(`preset-<id>`);本 profile 没有这一行时为 undefined。 */
30
+ entryId?: string;
20
31
  name?: string;
21
32
  description?: string;
22
- source: PresetSource;
23
- /** 组合文件绝对路径(`agent.cordis.yml`)。 */
24
- path: string;
25
- /** 仍挂着的官方冲突行(空 = 本插件的工具已生效)。 */
26
- conflicts: string[];
27
- /** 无冲突且形状可识别。 */
28
- clean: boolean;
29
- /** 组合文件**可读但**不成顶层行列表 → 一律不处理(内容问题,§5.5)。 */
30
- unrecognized: boolean;
31
- /** roster 报告的不可挂载原因,原样带出。 */
33
+ isDefault: boolean;
34
+ /** 宿主报告的不可挂载原因(行名解析不到、缺服务…),原样带出。 */
32
35
  broken?: string;
33
- /**
34
- * 用户根(`<dshHome>/.agent-presets/<id>`)里有这个预设的副本。
35
- * 设置页对我们随包的两份据此显示"已安装 / 未安装";官方随附的预设不在用户
36
- * 根,故恒为 false。
37
- */
38
- installed: boolean;
39
- /** 包内存在这个 id 的模板(只有我们随包的两份才有)。 */
40
- templatePresent: boolean;
41
- /** 已安装且与模板逐字节不同 → 本地有改动(重置会覆盖它)。 */
42
- templateDiffers: boolean;
43
- /**
44
- * 与模板**具体**差在哪些键(只读列出;`templateDiffers` 只说明"有差异",
45
- * 面板需要能说清"差在哪",否则和"工具行无需调整"并列时会自相矛盾)。
46
- * 无模板或不可比较时为 undefined。
47
- */
48
- templateDelta?: PresetDelta;
36
+ /** 生效的完整 config(宿主的 `entry.options.config`);取不到时为 undefined。 */
37
+ effective?: Record<string, unknown>;
38
+ /** 随包声明那一层的 config(`configEditor` 的 `inherited`);取不到时为 undefined。 */
39
+ inherited?: Record<string, unknown>;
40
+ /** 用户在 profile 层写的 config(`configEditor` 的 `override`);空对象 = 没有覆盖。 */
41
+ override?: Record<string, unknown>;
42
+ /** 这个 preset 是不是本插件声明的(id ∈ {@link BUNDLED_PRESET_IDS})。 */
43
+ ours: boolean;
49
44
  }
50
45
  /**
51
- * 列出每个 roster 行的状态(顺序与入参一致)。
52
- * @param roster - 调用方映射好的预设清单(`path` = 组合文件路径)。
53
- * @param deps - 路径注入(fs 层测试用)。
54
- * @returns 逐行的设置页状态。
46
+ * 把一个 preset 折成面板状态。
47
+ * @param facts - 宿主收集的原始事实。
48
+ * @param templateIds - 随包声明的模板清单(用于"与随包声明是否一致")。
49
+ * @returns 面板状态行。
50
+ */
51
+ export declare function toPresetStatus(facts: PresetFacts, templateIds: readonly string[]): PresetStatusValue;
52
+ /**
53
+ * 列出每个事实的状态,并给出模板清单。
54
+ * @param facts - 宿主收集的原始事实(顺序即面板顺序)。
55
+ * @param writable - 本部署有没有可编辑的 profile(`configEditor` 是否在场)。
56
+ * @returns `presets/status` 的完整返回值。
57
+ */
58
+ export declare function listPresetStatuses(facts: readonly PresetFacts[], writable: boolean): PresetStatusListValue;
59
+ /**
60
+ * 取某个模板声明的 plugins(面板"对比 / 对齐"的右侧)。
61
+ * @param facts - 同一个事实集合。
62
+ * @param templateId - 模板 id(本插件声明的某个 preset)。
63
+ * @returns 该模板的 plugins;不是我们的模板时 undefined。
55
64
  */
56
- export declare function listPresetStatuses(roster: readonly PresetRosterEntry[], deps?: PresetDeps): PresetStatus[];
65
+ export declare function templatePlugins(facts: readonly PresetFacts[], templateId: string): unknown;
@@ -2,11 +2,13 @@
2
2
  * Background-job adaptation for the `ctx.jobs` seam: one managed job runs its
3
3
  * command on its OWN `:async:` Shell (never the session's persistent shell),
4
4
  * so background work never blocks the foreground session. The registry owns
5
- * identity, lifecycle state, and completion notices (`dsh-tool-jobs` injects
6
- * them); this module owns the execution resources and their hooks.
5
+ * identity, lifecycle state, the output ring, and completion notices
6
+ * (`dsh-tool-jobs` injects them); this module owns the execution resources,
7
+ * streams the run into that ring, and appends the plugin's completion message
8
+ * once the run ends.
7
9
  * @module @xiaoso/dsh-tool-plus/background
8
10
  */
9
- import type { JobHooks } from '@deepseek-ai/dsh-jobs';
11
+ import type { JobHandle, JobHooks } from '@deepseek-ai/dsh-jobs';
10
12
  import type { BashForegroundOutput, ResolvedConfig } from './types.ts';
11
13
  export interface ManagedBashJob {
12
14
  /** Hooks handed to `ctx.jobs.start`'s `run()`. */
@@ -29,11 +31,24 @@ export interface StartBashJobOptions {
29
31
  /** Command-scoped environment (already merged with dshEnv). */
30
32
  env: Record<string, string> | undefined;
31
33
  config: ResolvedConfig;
34
+ /**
35
+ * The registry's producer face, when this run is a registered job: every
36
+ * streamed chunk lands in the job's output ring, and so does the settled
37
+ * completion message. Absent for a run no registry owns (unit callers).
38
+ */
39
+ job?: JobHandle;
40
+ /**
41
+ * Builds the settled completion message from the bounded preview and the
42
+ * run's spill file (the plugin's `outputTruncate` policy). Absent keeps the
43
+ * preview verbatim.
44
+ */
45
+ formatCompletion?: (previewText: string, spillPath: string | undefined) => string;
32
46
  }
33
47
  /**
34
48
  * Start one managed background bash job. The producer owns a per-job
35
- * `AbortController` (cancellation kills only this job's shell), a bounded
36
- * preview tail, and a consuming delta cursor for `readOutput`.
49
+ * `AbortController` (cancellation kills only this job's shell) and a bounded
50
+ * preview tail; the registry owns the model's consuming cursor over the ring
51
+ * this job writes to.
37
52
  * @param options - job identity and execution parameters.
38
53
  * @returns the registry hooks plus the tool-result completion promise.
39
54
  */
@@ -17,8 +17,10 @@ export declare class OutputSink extends VerbatimOutputSink {
17
17
  }
18
18
  /**
19
19
  * Composition wrapper over the verbatim {@link TailBuffer}, adding the DSH
20
- * `reset()` increment. The verbatim class keeps its state in private fields,
21
- * so reset is modeled as a fresh window with the same budget.
20
+ * `reset()` increment: the consuming-read cursor reset the bash background
21
+ * producer used while `JobHooks.readOutput` existed. 0.1.7 moves the model's
22
+ * cursor onto the job registry's output ring, so the increment is no longer
23
+ * part of that path; the preview buffer still offers it.
22
24
  */
23
25
  export declare class TailBuffer {
24
26
  #private;
@@ -41,80 +41,104 @@ export type RmSafeStatusValue = {
41
41
  /** Endpoint that lists agent presets with their status (roster + conflicts + template diff). */
42
42
  export declare const PRESET_STATUS_ENDPOINT = "presets/status";
43
43
  /**
44
- * The preset ids this plugin ships a template for. Single source of truth for
45
- * both halves: the host uses it for the startup bootstrap and the template
46
- * lookup, the panel uses it to synthesize the "not installed" rows for a
47
- * deployment whose roster does not carry them yet.
44
+ * The preset ids this plugin **declares**. Single source of truth for both
45
+ * halves: the host picks its own declarations out of the profile's composed
46
+ * configuration, and the panel uses the list to tell "ours" from everything else.
47
+ *
48
+ * They ship as `@deepseek-ai/dsh-agent-preset` declaration rows in this
49
+ * package's own bundle patches (`presets/<id>.patch.yml`), so installing the
50
+ * plugin as a profile bundle declares them: no install step, no file copy, and
51
+ * nothing written at startup.
48
52
  */
49
53
  export declare const BUNDLED_PRESET_IDS: readonly string[];
50
- /** Endpoint that applies one preset action to a user-root preset. */
54
+ /** Endpoint that applies one preset action to the active profile. */
51
55
  export declare const PRESET_ACTION_ENDPOINT = "presets/apply";
52
- /** Endpoint that compares one preset against one of our templates (read-only). */
56
+ /** Endpoint that compares one preset against one of our declarations (read-only). */
53
57
  export declare const PRESET_COMPARE_ENDPOINT = "presets/compare";
58
+ /**
59
+ * Loader row id a preset declaration is addressed by. A profile patch overrides
60
+ * a declaration **by this id** — the harness's config editor locates the row by
61
+ * id, and the override replaces the row's whole `config`.
62
+ * @param presetId - The preset identity (the declaration's `config.id`).
63
+ * @returns The row id as it appears in profile patches.
64
+ */
65
+ export declare function profileRowId(presetId: string): string;
54
66
  /** One preset action the settings panel may ask for. */
55
- export type PresetActionValue = 'upgrade' | 'reset';
67
+ export type PresetActionValue = 'upgrade' | 'align' | 'revert';
56
68
  /** Payload of a presets/apply call. */
57
69
  export interface PresetActionPayload {
58
- /** Preset id (a directory name under the user preset root, or a roster id). */
70
+ /** The preset to act on. */
59
71
  id: string;
60
- /** `upgrade` disables conflicting rows only; `reset` overwrites from our template. */
61
- action: PresetActionValue;
62
72
  /**
63
- * Which of our templates `reset` should align this preset to. Absent means
64
- * "the template with the same id" (historical behaviour); the panel always
65
- * sends the template the user picked in the compare picker, so a preset can
66
- * be aligned to a different template than its own.
73
+ * `upgrade` disables the still-mounted official tool rows and nothing else;
74
+ * `align` replaces the whole plugin list with one of our declarations;
75
+ * `revert` removes the profile override so the row falls back to its bundled
76
+ * declaration.
67
77
  */
78
+ action: PresetActionValue;
79
+ /** Required for `align`: which of our declarations to align this preset to. */
68
80
  templateId?: string;
69
81
  }
70
- /** Which layer a preset comes from: our shipped templates, the user root, or the shipped set. */
71
- export type PresetSourceValue = 'ours' | 'user' | 'shipped';
72
- /** One preset change produced by a rewrite. */
82
+ /**
83
+ * Where a preset comes from. dsh 0.1.7 dropped the registry's `trust` and `path`
84
+ * fields, so the only distinction left is "this plugin declared it" versus
85
+ * "something else did" (the shipped set, or another user-installed bundle).
86
+ */
87
+ export type PresetSourceValue = 'ours' | 'other';
88
+ /** One preset change produced by an upgrade. */
73
89
  export interface PresetChangeValue {
74
90
  /** The official tool row id that was handled. */
75
91
  id: string;
76
- /** `disabled` = row had no switch and got one; `flipped` = `disabled: false` -> true; `absent` = nothing to do. */
77
- action: 'disabled' | 'flipped' | 'absent';
92
+ /** `disabled` = the row had no switch and got one; `flipped` = an existing value became `true`. */
93
+ action: 'disabled' | 'flipped';
78
94
  }
79
95
  /** One agent preset as surfaced to the settings panel. */
80
96
  export interface PresetStatusValue {
81
97
  id: string;
98
+ /** Loader row id in the profile patch (`preset-<id>`); absent when this profile has no such row. */
99
+ entryId?: string;
82
100
  name?: string;
83
101
  description?: string;
84
102
  source: PresetSourceValue;
85
- path: string;
86
- /** Official tool rows this preset still mounts (empty = nothing to disable). */
103
+ /** Whether a session naming no preset composes this one. */
104
+ isDefault: boolean;
105
+ /** Official tool rows this preset still mounts enabled (empty = nothing to disable). */
87
106
  conflicts: string[];
88
107
  clean: boolean;
89
- /** Composition could not be parsed as a top-level row list — never rewritten. */
108
+ /** The plugin list is not a readable row list — never rewritten. */
90
109
  unrecognized: boolean;
110
+ /** Why the registry cannot mount this preset, when it reported one. */
91
111
  broken?: string;
92
- /** Whether the preset directory exists in the writable user root. */
93
- installed: boolean;
94
- /** Whether our package ships a template for this id. */
95
- templatePresent: boolean;
96
- /** For template-backed presets: whether the local copy differs from the template. */
112
+ /** The user wrote an override for this row in the profile patch. */
113
+ customized: boolean;
114
+ /** For our own presets: whether the effective content differs from the bundled declaration. */
97
115
  templateDiffers: boolean;
98
116
  /**
99
- * For template-backed presets: *what* differs (so the panel can say it instead
100
- * of the bare "differs", which reads as contradictory next to "nothing to
101
- * adjust"). Absent when there is nothing to compare.
117
+ * For our own presets: *what* differs (so the panel can say it instead of the
118
+ * bare "differs", which reads as contradictory next to "nothing to adjust").
119
+ * Absent when there is nothing to compare.
102
120
  */
103
121
  templateDelta?: PresetDeltaValue;
122
+ /** Rows in the effective plugin list, group children included (scale hint). */
123
+ rowCount: number;
104
124
  }
105
- /** One concrete difference between the local copy and the bundled template. */
125
+ /** One concrete difference between a preset's effective content and a template. */
106
126
  export interface PresetDeltaItemValue {
107
127
  kind: 'changed' | 'only-yours' | 'only-template' | 'row-only-yours' | 'row-only-template';
108
- /** Top-level row id. */
128
+ /**
129
+ * Row path: a top-level row id (`tool-web`), or a group child
130
+ * (`delegation/tool-ralph`). A bare id would point at the wrong row when a
131
+ * nested row shares its id.
132
+ */
109
133
  row: string;
110
134
  /** Dotted path inside that row (e.g. `config.thresholdRatio`); empty for a row-level difference. */
111
135
  path: string;
112
- /** Value in the local copy (`only-template` / `row-only-template` omit it). */
136
+ /** Value in the preset's effective content (`only-template` / `row-only-template` omit it). */
113
137
  yours?: string;
114
138
  /** Value in the template (`only-yours` / `row-only-yours` omit it). */
115
139
  template?: string;
116
140
  }
117
- /** Concrete local-vs-template differences; `items` is capped, `total` is not. */
141
+ /** Concrete preset-vs-template differences; `items` is capped, `total` is not. */
118
142
  export interface PresetDeltaValue {
119
143
  items: PresetDeltaItemValue[];
120
144
  total: number;
@@ -130,31 +154,31 @@ export interface PresetDeltaValue {
130
154
  * @returns True for the "yours" group.
131
155
  */
132
156
  export declare function isYoursDelta(kind: PresetDeltaItemValue['kind']): boolean;
133
- /** One template we ship, as the panel's "compare with" picker needs it. */
157
+ /** One declaration we ship, as the panel's "compare with" picker needs it. */
134
158
  export interface PresetTemplateValue {
135
159
  id: string;
136
160
  name?: string;
137
161
  }
138
162
  /** presets/compare request. */
139
163
  export interface PresetComparePayloadValue {
140
- /** The preset being inspected (any roster id). */
164
+ /** The preset being inspected (any preset in the roster). */
141
165
  presetId: string;
142
- /** The template we ship to compare it against. */
166
+ /** The declaration we ship to compare it against. */
143
167
  templateId: string;
144
168
  }
145
169
  /**
146
170
  * presets/compare result: the full, uncapped comparison between one preset and
147
- * one of our templates. `conflicts` answers "are our tools wired in", `items`
171
+ * one of our declarations. `conflicts` answers "are our tools wired in", `items`
148
172
  * answers "how else does the content differ" (and is what the diff dialog shows).
149
173
  */
150
174
  export interface PresetCompareValue {
151
175
  presetId: string;
152
176
  templateId: string;
153
- /** `unreadable` when either side cannot be read/parsed — nothing to compare. */
177
+ /** `unreadable` when either side cannot be resolved — nothing to compare. */
154
178
  status: 'ok' | 'unreadable';
155
179
  /** Official tool rows still mounted (empty = the plugin's tools are wired in). */
156
180
  conflicts: string[];
157
- /** Content is structurally identical (comments/formatting ignored). */
181
+ /** Content is structurally identical (row order and formatting ignored). */
158
182
  identical: boolean;
159
183
  /** Full-population counts: your own edits vs the template being ahead. */
160
184
  yoursCount: number;
@@ -165,14 +189,32 @@ export interface PresetCompareValue {
165
189
  /** presets/status result. */
166
190
  export interface PresetStatusListValue {
167
191
  presets: PresetStatusValue[];
168
- /** Templates this package ships (the "compare with" picker's options). */
192
+ /** Declarations this package ships (the "compare with" picker's options). */
169
193
  templates: PresetTemplateValue[];
194
+ /**
195
+ * Whether this deployment has an editable profile at all (`ctx.configEditor`
196
+ * is present). Without it the panel can still show state but cannot write.
197
+ */
198
+ writable: boolean;
199
+ /**
200
+ * Absolute path of a legacy `$DSH_HOME/.agent-presets` directory, present only
201
+ * when one still exists and is non-empty. dsh 0.1.7 reads no such directory —
202
+ * it is a cleanup hint for users upgrading from the directory mechanism, not a
203
+ * feature of this deployment.
204
+ */
205
+ legacyPresetRoot?: string;
170
206
  }
171
- /** presets/apply result (mirrors PresetInstallResult from presets/install). */
207
+ /** presets/apply result. */
172
208
  export interface PresetActionResultValue {
173
209
  ok: boolean;
174
210
  changed: boolean;
211
+ /** Why nothing was written, or why the write failed (one line for the panel). */
175
212
  reason?: string;
213
+ /**
214
+ * The `<profile patch>.bak-<version>` copy taken before this write, when the
215
+ * patch already existed. One backup per profile, written once and never
216
+ * overwritten, so it always holds the earliest pre-change state.
217
+ */
176
218
  backupPath?: string;
177
219
  changes: PresetChangeValue[];
178
220
  }
@@ -17,7 +17,7 @@
17
17
  /** Locale namespace of the tool-card bundle (registered by `registerToolCards`). */
18
18
  export declare const CARD_LOCALE_NS = "tool-plus-cards";
19
19
  /** Every copy key of the tool-card namespace. */
20
- export type ToolCardLocaleKey = 'copy' | 'copied' | 'collapse' | 'expand' | 'collapseAria' | 'expandAria' | 'expandRest' | 'files.one' | 'files.other' | 'running' | 'done' | 'failed' | 'stopped' | 'cancelled' | 'timedOut' | 'noOutput' | 'title.bash' | 'title.read' | 'title.write' | 'title.edit' | 'title.grep' | 'title.glob' | 'title.astGrep' | 'title.astEdit' | 'write.lines' | 'write.madeExecutable' | 'astEdit.replacements' | 'astEdit.files' | 'astEdit.parseErrors' | 'astEdit.rules' | 'astEdit.rulesRest' | 'read.window' | 'read.collapseAria' | 'read.expandAria' | 'read.expandRest' | 'search.paths' | 'search.paths.truncated' | 'search.matches' | 'search.matches.truncated' | 'search.noResults' | 'search.scope' | 'search.collapseAria' | 'search.expandAria' | 'search.expandRest' | 'terminal.signal' | 'terminal.exitCode' | 'terminal.running' | 'terminal.failed' | 'terminal.done' | 'terminal.noOutput' | 'bash.background' | 'image.loading' | 'image.loadFailed' | 'image.open' | 'image.openNamed' | 'image.dialog' | 'image.close' | 'image.unnamed' | 'image.caption' | 'generic.params' | 'generic.noDetail' | 'inspect';
20
+ export type ToolCardLocaleKey = 'copy' | 'copied' | 'collapse' | 'expand' | 'collapseAria' | 'expandAria' | 'expandRest' | 'files.one' | 'files.other' | 'codeBlock.title' | 'codeBlock.wrap' | 'codeBlock.unwrap' | 'running' | 'done' | 'failed' | 'stopped' | 'cancelled' | 'timedOut' | 'noOutput' | 'title.bash' | 'title.read' | 'title.write' | 'title.edit' | 'title.grep' | 'title.glob' | 'title.astGrep' | 'title.astEdit' | 'write.lines' | 'write.madeExecutable' | 'astEdit.replacements' | 'astEdit.files' | 'astEdit.parseErrors' | 'astEdit.rules' | 'astEdit.rulesRest' | 'read.window' | 'read.collapseAria' | 'read.expandAria' | 'read.expandRest' | 'search.paths' | 'search.paths.truncated' | 'search.matches' | 'search.matches.truncated' | 'search.noResults' | 'search.scope' | 'search.collapseAria' | 'search.expandAria' | 'search.expandRest' | 'terminal.signal' | 'terminal.exitCode' | 'terminal.running' | 'terminal.failed' | 'terminal.done' | 'terminal.noOutput' | 'terminal.noExitCode' | 'bash.background' | 'image.loading' | 'image.loadFailed' | 'image.open' | 'image.openNamed' | 'image.dialog' | 'image.close' | 'image.unnamed' | 'image.caption' | 'generic.params' | 'generic.noDetail' | 'inspect';
21
21
  /** Simplified Chinese copy (the source dictionary). */
22
22
  export declare const zh: Record<ToolCardLocaleKey, string>;
23
23
  /** English dictionary, checked complete against the zh key set. */
@@ -14,7 +14,7 @@
14
14
  * nothing here touches a raw `meta` field directly.
15
15
  * @module @xiaoso/dsh-tool-plus/web/client/row-utils
16
16
  */
17
- import type { DiffBlockLabels, ReadBlockLabels, SearchBlockLabels, TerminalBlockLabels } from '@deepseek-ai/dsh-client-ui-primitives';
17
+ import type { CodeToolbarLabels, DiffBlockLabels, ReadBlockLabels, SearchBlockLabels, TerminalBlockLabels } from '@deepseek-ai/dsh-client-ui-primitives';
18
18
  import type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots';
19
19
  import type { CARD_LOCALE_NS, ToolCardLocaleKey } from './labels.ts';
20
20
  /** Translate seat of this plugin's card namespace. */
@@ -48,6 +48,20 @@ export interface CardBlockView {
48
48
  export type CardState = 'running' | 'ok' | 'error' | 'warning';
49
49
  /** Whether a block is the settled half of a call pair. */
50
50
  export declare function isSettled(block: CardBlockView): boolean;
51
+ /**
52
+ * Whether a call already landed as a failed result.
53
+ *
54
+ * Only a settled result can be wrong about its work: the union's error state is
55
+ * `ToolResultNode.isError` alone, and the shipped row model reads it the same
56
+ * way — settle on the `kind` discriminant (`'kind' in block`) and only then look
57
+ * at `error`, `isError`. A preparing or dispatched call has no result yet, so it
58
+ * is never an error; the rows that fall back to the generic shell keep exactly
59
+ * the criterion they have always had (`isError === true`), stated where the
60
+ * union still allows it.
61
+ * @param block - the frozen running-or-settled call node.
62
+ * @returns whether the call settled as a failure.
63
+ */
64
+ export declare function isErrorResult(block: CardBlockView): boolean;
51
65
  /**
52
66
  * The call's original argument text.
53
67
  *
@@ -187,6 +201,17 @@ export interface ShellStatus {
187
201
  * @returns the marker-free output and the facts the markers named.
188
202
  */
189
203
  export declare function parseShellStatus(text: string): ShellStatus;
204
+ /**
205
+ * Bind the card dictionary to the shared code-card toolbar labels.
206
+ *
207
+ * The toolbar rides every code surface the primitives draw (the diff block, the
208
+ * read block), and the host's own adapter for it reads the base locale's
209
+ * `codeBlock.*` wording; this dictionary carries the same three strings, so a
210
+ * takeover's code cards read exactly as the shipped ones do.
211
+ * @param t - the card translate seat.
212
+ * @returns the language fallback and wrapping actions.
213
+ */
214
+ export declare function codeToolbarLabels(t: CardTranslate): CodeToolbarLabels;
190
215
  /** Bind the card dictionary to the diff primitive's chrome labels. */
191
216
  export declare function diffBlockLabels(t: CardTranslate): DiffBlockLabels;
192
217
  /** Bind the card dictionary to the read primitive's chrome labels. */
@@ -6267,7 +6267,7 @@ const settings = {
6267
6267
  };
6268
6268
  //#endregion
6269
6269
  //#region package.json
6270
- var version = "0.1.9";
6270
+ var version = "0.1.10-beta.1";
6271
6271
  /** `.tmp` files older than this are treated as orphaned writes and swept. */
6272
6272
  const TMP_ORPHAN_MAX_AGE_MS = 3e5;
6273
6273
  function markitConversionCacheKey(bytes, extension) {