@deepseek-ai/dsh-client-ui-commands 0.1.0-rc.7 → 0.1.0-rc.8
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.i18n.yaml +2 -2
- package/README.md +2 -0
- package/README.zh.md +2 -0
- package/lib/client.js +46 -17
- package/lib/types/client/locales.d.ts +2 -0
- package/lib/types/client/service.d.ts +11 -1
- package/package.json +21 -24
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/client/ui-commands/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 2140495a44110d5e4b33e4cc8f539959752ac185
|
|
6
|
+
README.zh.md: afa47cd18505b9afbd3e867d131e9796db598895
|
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@ Client command API (`ctx.commandUi`): the session-keyed command-directory cache,
|
|
|
8
8
|
|
|
9
9
|
`CommandDirectory` (`src/client/directory.ts`) is the one wire-derived cache, keyed by session. Ordinary sessions fetch through `command.list({sessionId})`, and the source's scope-birth `warm` hook prewarms the session's entry. Catalog-addressed continuable children resolve an empty command directory locally: `command.list` is Agent-bound, so prewarming it would activate a child merely to view persisted history. Entries are soft-invalidated by the forwarded `commands/change` owner event (old snapshots serve while the repull flies) and by forwarded `agent-preset/selected` for that one session (recomposing an agent registers nothing, so the registry-wide signal never fires for it), hard-invalidated by `connection/reset`, and epoch-guarded so a superseded pull can never overwrite a newer one. `matchSpace` answers synchronously from this cache only; `matchEnter` strong-waits it on the SubmitAttempt signal and rejects on warmup failure — a `/` line is never silently downgraded to a plain prompt.
|
|
10
10
|
|
|
11
|
+
`matchEnter` also enforces the submission envelope: when the composer submits with image attachments, only a host command declaring `input.images` proceeds (its claim carries `images: true` and its submit forwards the serialized payloads to `command.execute`); every other command route — contribution popup, decorated popup, non-declaring claim, bare detached execute — throws the localized `notice.imagesUnsupported` refusal, which the input machine publishes as one error notice and the composer renders as a transient Toast banner with the draft and images retained. An image-carrying submit whose host handler answers an error result maps to an error outcome so the composer keeps the images; imageless submits keep the plain success mapping because the durable flow node owns the outcome rendering.
|
|
12
|
+
|
|
11
13
|
After `command.execute` returns a matched command result, this browser emits local `command/executed(sessionId, name, result)`. Other clients receive the durable command nodes through the Host event stream but never this acknowledgment, so a browser-only side effect can select successful results from the client that submitted the command without treating Session replay as an action request. Listener failures are logged and contained one by one; they cannot change the already-admitted command result or prevent later listeners from running.
|
|
12
14
|
|
|
13
15
|
Menu queries fuzzy-match ordered, case-insensitive subsequences of command names. Prefixes rank first; separator boundaries, adjacent characters, and shorter gaps rank the remaining matches, with directory and contribution order breaking ties. This affects discovery only: space and Enter still require an exact command name. Rationale: [Web slash-command fuzzy discovery](../../../.agents/notes/implemented/feature/2026-08-04-web-slash-command-fuzzy-discovery.md).
|
package/README.zh.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
`CommandDirectory`(`src/client/directory.ts`)是唯一的 wire 派生缓存,以会话为 key。普通会话通过 `command.list({sessionId})` 拉取,source 的 scope 出生 `warm` 钩子会预热该会话的缓存项。由目录寻址的可继续子代理会在客户端解析为空命令目录:`command.list` 绑定 Agent,若预热它,就会仅因查看持久化历史而激活子代理。缓存项由转发的 owner 事件 `commands/change` 软失效(重拉在途期间旧快照继续服务),也由转发的 `agent-preset/selected` 对该会话单独软失效(重组 agent 不产生任何注册,注册表级信号不会为它触发),由 `connection/reset` 硬失效,并以 epoch 把关,被取代的旧拉取永远无法覆盖更新的结果。`matchSpace` 只凭该缓存同步应答;`matchEnter` 在 SubmitAttempt 信号上强等缓存,预热失败即拒绝——`/` 开头的一行绝不会被静默降级为普通提示词。
|
|
10
10
|
|
|
11
|
+
`matchEnter` 还强制执行提交信封:composer 携带图片附件提交时,只有声明了 `input.images` 的宿主命令继续(其 claim 携带 `images: true`,其 submit 把序列化载荷转交 `command.execute`);其余每条命令路径——contribution 弹窗、decoration 弹窗、未声明的 claim、bare 分离执行——都会抛出本地化的 `notice.imagesUnsupported` 拒绝,输入状态机发布一条错误通知,composer 以瞬态 Toast 横幅呈现它,草稿与图片原样保留。带图提交若宿主处理器返回错误结果,则映射为错误 outcome,composer 保留图片;不带图的提交维持原有的一律成功映射,因为结果呈现由持久化 flow 节点负责。
|
|
12
|
+
|
|
11
13
|
`command.execute` 返回已匹配的命令结果后,当前浏览器会发布本地 `command/executed(sessionId, name, result)`。其他客户端只会通过 Host 事件流收到持久命令节点,不会收到这条确认,因此浏览器专属副作用可以筛选由实际提交命令的客户端收到的成功结果,而不会把 Session 回放当成操作请求。监听器失败会逐项记录并隔离,不会改变已经准入的命令结果,也不会阻止后续监听器运行。
|
|
12
14
|
|
|
13
15
|
菜单查询会按顺序且不区分大小写地模糊匹配命令名的子序列。前缀排名最高;其余匹配项按分隔符边界优先、相邻字符优先、间隔越短越优先的规则排序,若仍同分,则以目录顺序和贡献项顺序打破平局。此行为只影响命令发现:space 和 Enter 仍要求命令名精确匹配。原理:[Web 斜杠命令模糊发现](../../../.agents/notes/implemented/feature/2026-08-04-web-slash-command-fuzzy-discovery.md)。
|
package/lib/client.js
CHANGED
|
@@ -500,12 +500,17 @@ window.__ModuleLoader__.load({
|
|
|
500
500
|
decorations: /* @__PURE__ */ new Map(),
|
|
501
501
|
popups: /* @__PURE__ */ new Map()
|
|
502
502
|
};
|
|
503
|
+
/** `command`-namespace translator (composer refusal notices). */
|
|
504
|
+
t;
|
|
503
505
|
/**
|
|
504
506
|
* @param ctx - owning root context (plugin fiber; the service registers
|
|
505
507
|
* itself as `command` and follows that fiber's lifetime).
|
|
506
508
|
*/
|
|
507
509
|
constructor(ctx) {
|
|
508
510
|
super(ctx, "commandUi");
|
|
511
|
+
const locale = ctx.get("locale");
|
|
512
|
+
if (locale === void 0) throw new Error("ui-commands: locale service unavailable");
|
|
513
|
+
this.t = locale.bind("command");
|
|
509
514
|
this.directory = new CommandDirectory(async (sessionId) => {
|
|
510
515
|
if (this.sessions().subagentAddress(sessionId) !== void 0) return [];
|
|
511
516
|
const result = await ctx.remote.commands.list(sessionId);
|
|
@@ -520,7 +525,7 @@ window.__ModuleLoader__.load({
|
|
|
520
525
|
candidates: (session, req) => this.candidates(session, req),
|
|
521
526
|
onPick: (pick) => this.dispatch(pick),
|
|
522
527
|
matchSpace: (session, token) => this.matchSpace(session, token),
|
|
523
|
-
matchEnter: (session, line, signal) => this.matchEnter(session, line, signal),
|
|
528
|
+
matchEnter: (session, line, signal, envelope) => this.matchEnter(session, line, signal, envelope),
|
|
524
529
|
warm: (session) => {
|
|
525
530
|
this.directory.warm(session.sessionId);
|
|
526
531
|
}
|
|
@@ -687,8 +692,14 @@ window.__ModuleLoader__.load({
|
|
|
687
692
|
* warmup failure rejects — never a silent downgrade). Contributions and
|
|
688
693
|
* bare host commands act on the bare token only; leadingInput claims
|
|
689
694
|
* args-tolerant.
|
|
695
|
+
*
|
|
696
|
+
* Envelope policy: an enter submission carrying images resolves only
|
|
697
|
+
* through a command declaring image acceptance. Every other command route —
|
|
698
|
+
* popup, non-accepting claim, bare detached execute — throws the refusal
|
|
699
|
+
* so the machine surfaces one composer notice and the draft and images
|
|
700
|
+
* stay in place; nothing executes and nothing is dropped.
|
|
690
701
|
*/
|
|
691
|
-
async matchEnter(session, line, signal) {
|
|
702
|
+
async matchEnter(session, line, signal, envelope) {
|
|
692
703
|
const trimmed = line.trim();
|
|
693
704
|
if (!trimmed.startsWith("/")) return void 0;
|
|
694
705
|
const ws = trimmed.search(/\s/);
|
|
@@ -696,9 +707,13 @@ window.__ModuleLoader__.load({
|
|
|
696
707
|
const bare = ws === -1;
|
|
697
708
|
const name = token.slice(1);
|
|
698
709
|
if (name === "") return void 0;
|
|
710
|
+
const refuseImages = () => {
|
|
711
|
+
throw new Error(this.t("notice.imagesUnsupported", { command: name }));
|
|
712
|
+
};
|
|
699
713
|
const contribution = this.live.contributions.get(name);
|
|
700
714
|
if (contribution !== void 0 && contribution.available(session)) {
|
|
701
715
|
if (!bare) return void 0;
|
|
716
|
+
if (envelope.images > 0) refuseImages();
|
|
702
717
|
this.openPopup(name, contribution.ui, session, {
|
|
703
718
|
via: "enter",
|
|
704
719
|
token
|
|
@@ -711,6 +726,7 @@ window.__ModuleLoader__.load({
|
|
|
711
726
|
if (bare) {
|
|
712
727
|
const decoration = this.live.decorations.get(name);
|
|
713
728
|
if (decoration !== void 0 && decoration.available(session)) {
|
|
729
|
+
if (envelope.images > 0) refuseImages();
|
|
714
730
|
this.openPopup(name, decoration.ui, session, {
|
|
715
731
|
via: "enter",
|
|
716
732
|
token
|
|
@@ -718,8 +734,12 @@ window.__ModuleLoader__.load({
|
|
|
718
734
|
return "handled";
|
|
719
735
|
}
|
|
720
736
|
}
|
|
721
|
-
if (desc.input !== void 0)
|
|
737
|
+
if (desc.input !== void 0) {
|
|
738
|
+
if (envelope.images > 0 && desc.input.images !== true) refuseImages();
|
|
739
|
+
return { claim: this.leadingClaim(desc, session) };
|
|
740
|
+
}
|
|
722
741
|
if (!bare) return void 0;
|
|
742
|
+
if (envelope.images > 0) refuseImages();
|
|
723
743
|
this.consumeVia(session.sessionId, {
|
|
724
744
|
via: "enter",
|
|
725
745
|
token
|
|
@@ -739,7 +759,8 @@ window.__ModuleLoader__.load({
|
|
|
739
759
|
return {
|
|
740
760
|
token,
|
|
741
761
|
...desc.input !== void 0 ? { hint: desc.input.hint } : {},
|
|
742
|
-
|
|
762
|
+
...desc.input?.images === true ? { images: true } : {},
|
|
763
|
+
submit: (args, _actx, images) => this.execute(session, token + args, images)
|
|
743
764
|
};
|
|
744
765
|
}
|
|
745
766
|
/**
|
|
@@ -749,16 +770,22 @@ window.__ModuleLoader__.load({
|
|
|
749
770
|
* plain success regardless of its handler outcome, because the host
|
|
750
771
|
* executor durably logged the lifecycle (`command/run`/`command/done`) and
|
|
751
772
|
* the outcome renders as a persistent flow node — the composer never
|
|
752
|
-
* echoes it.
|
|
773
|
+
* echoes it. A handler error result reports an error outcome so the
|
|
774
|
+
* composer keeps the submission (draft and images) for correction.
|
|
775
|
+
* Transport failures throw.
|
|
753
776
|
*/
|
|
754
|
-
async execute(session, line) {
|
|
755
|
-
const result = await this.ctx.remote.commands.execute(session.sessionId, line);
|
|
777
|
+
async execute(session, line, images = []) {
|
|
778
|
+
const result = await this.ctx.remote.commands.execute(session.sessionId, line, images);
|
|
756
779
|
if (!result.ok) throw new Error(`command.execute failed: ${result.error.code}: ${result.error.message}`);
|
|
757
780
|
if (result.value === void 0) return {
|
|
758
781
|
kind: "error",
|
|
759
782
|
text: `unknown or malformed command: ${line}`
|
|
760
783
|
};
|
|
761
784
|
this.notifyExecuted(session.sessionId, submittedCommandName(line), result.value.result);
|
|
785
|
+
if (images.length > 0 && result.value.result.kind === "error") return {
|
|
786
|
+
kind: "error",
|
|
787
|
+
text: result.value.result.text
|
|
788
|
+
};
|
|
762
789
|
return { kind: "success" };
|
|
763
790
|
}
|
|
764
791
|
/** Publish the local acknowledgment without letting an observer change command admission. */
|
|
@@ -855,18 +882,18 @@ window.__ModuleLoader__.load({
|
|
|
855
882
|
document.head.appendChild(tag);
|
|
856
883
|
}
|
|
857
884
|
var PopupSelectView_module_css_default = {
|
|
858
|
-
"
|
|
859
|
-
"
|
|
860
|
-
"
|
|
885
|
+
"card": "mufS8W_card",
|
|
886
|
+
"check": "mufS8W_check",
|
|
887
|
+
"detail": "mufS8W_detail",
|
|
861
888
|
"error": "mufS8W_error",
|
|
862
889
|
"errorText": "mufS8W_errorText",
|
|
863
|
-
"
|
|
890
|
+
"label": "mufS8W_label",
|
|
864
891
|
"retry": "mufS8W_retry",
|
|
865
892
|
"row": "mufS8W_row",
|
|
866
|
-
"
|
|
867
|
-
"
|
|
868
|
-
"
|
|
869
|
-
"
|
|
893
|
+
"rowActive": "mufS8W_rowActive",
|
|
894
|
+
"search": "mufS8W_search",
|
|
895
|
+
"status": "mufS8W_status",
|
|
896
|
+
"viewport": "mufS8W_viewport"
|
|
870
897
|
};
|
|
871
898
|
//#endregion
|
|
872
899
|
//#region lib/types/client/PopupSelectView.js
|
|
@@ -1047,7 +1074,8 @@ window.__ModuleLoader__.load({
|
|
|
1047
1074
|
"status.applying": "正在应用…",
|
|
1048
1075
|
"status.empty": "无选项",
|
|
1049
1076
|
"overlay.aria": "/{command} 选项",
|
|
1050
|
-
"listbox.aria": "/{command} 匹配项"
|
|
1077
|
+
"listbox.aria": "/{command} 匹配项",
|
|
1078
|
+
"notice.imagesUnsupported": "/{command} 不接受图片附件,请先移除图片"
|
|
1051
1079
|
};
|
|
1052
1080
|
/** English dictionary, checked complete against the zh key set. */
|
|
1053
1081
|
const en = {
|
|
@@ -1057,7 +1085,8 @@ window.__ModuleLoader__.load({
|
|
|
1057
1085
|
"status.applying": "Applying…",
|
|
1058
1086
|
"status.empty": "No options",
|
|
1059
1087
|
"overlay.aria": "/{command} options",
|
|
1060
|
-
"listbox.aria": "/{command} matches"
|
|
1088
|
+
"listbox.aria": "/{command} matches",
|
|
1089
|
+
"notice.imagesUnsupported": "/{command} does not accept image attachments; remove them first"
|
|
1061
1090
|
};
|
|
1062
1091
|
//#endregion
|
|
1063
1092
|
//#region lib/types/client/index.js
|
|
@@ -8,6 +8,7 @@ export declare const zh: {
|
|
|
8
8
|
'status.empty': string;
|
|
9
9
|
'overlay.aria': string;
|
|
10
10
|
'listbox.aria': string;
|
|
11
|
+
'notice.imagesUnsupported': string;
|
|
11
12
|
};
|
|
12
13
|
/** The command namespace key union. */
|
|
13
14
|
export type CommandKey = keyof typeof zh;
|
|
@@ -20,5 +21,6 @@ export declare const en: {
|
|
|
20
21
|
'status.empty': string;
|
|
21
22
|
'overlay.aria': string;
|
|
22
23
|
'listbox.aria': string;
|
|
24
|
+
'notice.imagesUnsupported': string;
|
|
23
25
|
};
|
|
24
26
|
//# sourceMappingURL=locales.d.ts.map
|
|
@@ -33,6 +33,8 @@ export declare class CommandUiRuntime extends Service implements CommandUiContra
|
|
|
33
33
|
static inject: string[];
|
|
34
34
|
private readonly directory;
|
|
35
35
|
private readonly live;
|
|
36
|
+
/** `command`-namespace translator (composer refusal notices). */
|
|
37
|
+
private readonly t;
|
|
36
38
|
/**
|
|
37
39
|
* @param ctx - owning root context (plugin fiber; the service registers
|
|
38
40
|
* itself as `command` and follows that fiber's lifetime).
|
|
@@ -81,6 +83,12 @@ export declare class CommandUiRuntime extends Service implements CommandUiContra
|
|
|
81
83
|
* warmup failure rejects — never a silent downgrade). Contributions and
|
|
82
84
|
* bare host commands act on the bare token only; leadingInput claims
|
|
83
85
|
* args-tolerant.
|
|
86
|
+
*
|
|
87
|
+
* Envelope policy: an enter submission carrying images resolves only
|
|
88
|
+
* through a command declaring image acceptance. Every other command route —
|
|
89
|
+
* popup, non-accepting claim, bare detached execute — throws the refusal
|
|
90
|
+
* so the machine surfaces one composer notice and the draft and images
|
|
91
|
+
* stay in place; nothing executes and nothing is dropped.
|
|
84
92
|
*/
|
|
85
93
|
private matchEnter;
|
|
86
94
|
/** Open the session's popup for one contribution or decoration (menu pick / bare enter). */
|
|
@@ -94,7 +102,9 @@ export declare class CommandUiRuntime extends Service implements CommandUiContra
|
|
|
94
102
|
* plain success regardless of its handler outcome, because the host
|
|
95
103
|
* executor durably logged the lifecycle (`command/run`/`command/done`) and
|
|
96
104
|
* the outcome renders as a persistent flow node — the composer never
|
|
97
|
-
* echoes it.
|
|
105
|
+
* echoes it. A handler error result reports an error outcome so the
|
|
106
|
+
* composer keeps the submission (draft and images) for correction.
|
|
107
|
+
* Transport failures throw.
|
|
98
108
|
*/
|
|
99
109
|
private execute;
|
|
100
110
|
/** Publish the local acknowledgment without letting an observer change command admission. */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-ui-commands",
|
|
3
3
|
"description": "Client command surface: global directory cache, '/' source, three command UI kinds, popupSelect registry",
|
|
4
|
-
"version": "0.1.0-rc.
|
|
4
|
+
"version": "0.1.0-rc.8",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -46,33 +46,30 @@
|
|
|
46
46
|
"clsx": "^2.0.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"
|
|
50
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.
|
|
51
|
-
"@deepseek-ai/dsh-client-
|
|
52
|
-
"@deepseek-ai/dsh-client-
|
|
53
|
-
"@deepseek-ai/dsh-client-ui-
|
|
54
|
-
"@deepseek-ai/dsh-
|
|
55
|
-
"@deepseek-ai/
|
|
56
|
-
"@deepseek-ai/dsh-
|
|
57
|
-
"@deepseek-ai/dsh-commands": "^0.1.0-rc.7",
|
|
58
|
-
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.7",
|
|
59
|
-
"@deepseek-ai/cordis": "^4.0.1"
|
|
49
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
|
|
50
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.8",
|
|
51
|
+
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
|
|
52
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.8",
|
|
53
|
+
"@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.0-rc.8",
|
|
54
|
+
"@deepseek-ai/dsh-commands": "^0.1.0-rc.8",
|
|
55
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
56
|
+
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8"
|
|
60
57
|
},
|
|
61
58
|
"devDependencies": {
|
|
62
59
|
"@types/react": "~18.3.1",
|
|
63
60
|
"react": "^18.2.0",
|
|
64
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.
|
|
65
|
-
"@deepseek-ai/dsh-client-connection": "^0.1.0-rc.
|
|
66
|
-
"@deepseek-ai/dsh-client-
|
|
67
|
-
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.
|
|
68
|
-
"@deepseek-ai/dsh-client-
|
|
69
|
-
"@deepseek-ai/dsh-client-
|
|
70
|
-
"@deepseek-ai/dsh-client-ui-
|
|
71
|
-
"@deepseek-ai/dsh-client-ui-
|
|
72
|
-
"@deepseek-ai/dsh-
|
|
73
|
-
"@deepseek-ai/dsh-
|
|
74
|
-
"@deepseek-ai/
|
|
75
|
-
"@deepseek-ai/
|
|
61
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.8",
|
|
62
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.0-rc.8",
|
|
63
|
+
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
|
|
64
|
+
"@deepseek-ai/dsh-client-test-runtime": "^0.1.0-rc.8",
|
|
65
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.8",
|
|
66
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
|
|
67
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.8",
|
|
68
|
+
"@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.0-rc.8",
|
|
69
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.8",
|
|
70
|
+
"@deepseek-ai/dsh-commands": "^0.1.0-rc.8",
|
|
71
|
+
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
|
|
72
|
+
"@deepseek-ai/cordis": "^4.0.1"
|
|
76
73
|
},
|
|
77
74
|
"files": [
|
|
78
75
|
"lib/index.js",
|