@deepseek-ai/dsh-client-ui-goal 0.1.3-alpha.2 → 0.1.5-alpha.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.
- package/README.i18n.yaml +2 -2
- package/README.md +4 -5
- package/README.zh.md +4 -5
- package/lib/client.js +177 -34
- package/lib/types/client/GoalBar.d.ts +15 -13
- package/lib/types/client/activation-source.d.ts +29 -0
- package/lib/types/client/index.d.ts +5 -7
- package/lib/types/client/locales.d.ts +2 -0
- package/lib/types/client/slots.d.ts +23 -4
- package/package.json +15 -15
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-goal/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 1fbc1c7df944f1106fedcff79f6b9df35bda480d
|
|
6
|
+
README.zh.md: 01f8902377c98dc39d64b3bfe26108b68cb29fe0
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
|
|
9
9
|
|
|
10
10
|
## Summary
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
The Web GUI goal surface shows both the durable goal state and its current process-local activation, and lets users edit, pause, resume, or clear the goal; rejected changes appear inline. It displays durable `/goal` runs as `Command input` bubbles so commands from users or the model remain visible after reload. Goal creation remains outside this package. Shipped Web presets other than `minimal` make `/goal` available to agents.
|
|
13
13
|
|
|
14
14
|
## Table of Contents
|
|
15
15
|
|
|
@@ -25,7 +25,7 @@ This package renders the goal surface in the Web GUI: a strip in the composer-co
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## Use this package
|
|
27
27
|
|
|
28
|
-
Mount this plugin alongside `ui-conversation` and the goal domain package; the strip then appears as the second card in the composer-context stack (after Todo, before Queue) whenever the session has a goal. An active goal offers pause;
|
|
28
|
+
Mount this plugin alongside `ui-conversation` and the goal domain package; the strip then appears as the second card in the composer-context stack (after Todo, before Queue) whenever the session has a goal. An armed active goal offers pause; an active-but-disarmed or paused goal offers resume; edit rewrites the objective; clear removes the goal and suppresses the strip until the projection catches up.
|
|
29
29
|
|
|
30
30
|
### The command-input bubble
|
|
31
31
|
|
|
@@ -43,7 +43,7 @@ A rejected mutation surfaces the Remote error inline on the strip; loading, abse
|
|
|
43
43
|
<details>
|
|
44
44
|
<summary>Implementation internals — click to expand</summary>
|
|
45
45
|
|
|
46
|
-
The
|
|
46
|
+
The durable goal arrives through `useProjection('goal')` (seeded by the history tail page and updated by `session/projection` frames). The inject face carries a registrant-private activation hook source plus the four mutation verbs. That source starts only while the framework hook observes it, reads `ctx.remote.goals.get`, subscribes to `goal/activation-changed`, and refreshes on running-state or connection resets. Live-event epochs invalidate in-flight reads, so a stale HTTP result cannot overwrite a newer activation edge; running refreshes retain the last known activation until the read resolves. The strip owns no domain store or cross-plugin cache. Each mutation reads the CAS ref from the session's current projected value at call time, and the RPC's compare-and-set is the staleness guard. The strip single-flights mutations synchronously because a pending render cannot fence same-frame clicks. The command-input projection is a separate Conversation Definition that builds a `command-input` Chat Node before the generic command result Node; it never creates `user/message` or a model turn.
|
|
47
47
|
|
|
48
48
|
</details>
|
|
49
49
|
|
|
@@ -76,7 +76,6 @@ None unless the queued goal context is admitted. An admitted context extends the
|
|
|
76
76
|
|
|
77
77
|
These limits define the current goal surface. They are current package constraints, not a goal-domain comparison or a task backlog.
|
|
78
78
|
|
|
79
|
-
- **Durable phase only** — the projection omits process-local activation, so the strip cannot distinguish an active-but-disarmed goal from an armed one; resume re-arms through the RPC side. There is no host-live activation channel.
|
|
80
79
|
- **Preset-independent host state** — switching an active session to `minimal` leaves its host-owned goal intact. `/goal` and goal tools disappear, while this strip can still edit, pause, resume, or clear the goal.
|
|
81
80
|
|
|
82
81
|
<a id="dev-note"></a>
|
|
@@ -89,4 +88,4 @@ None.
|
|
|
89
88
|
|
|
90
89
|
</details>
|
|
91
90
|
|
|
92
|
-
**Runtime invariant:** No companion is published. A single GoalBar dock registration whose disposal is proven by the HMR-safety spec —
|
|
91
|
+
**Runtime invariant:** No companion is published. A single GoalBar dock registration whose disposal is proven by the HMR-safety spec — durable state arrives on the goal projection, process-local activation arrives through the entry's private hook source, and that source subscribes only while the framework hook observes it.
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@ kind: "package-reference"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Web GUI 的 goal 表面同时显示持久 goal 状态及当前的进程本地激活状态,供用户编辑、暂停、恢复或清除 goal;被拒绝的变更会内联显示。它把持久的 `/goal` 运行显示为 `Command input` 气泡,让用户或模型发出的命令在重新加载后仍然可见。goal 创建仍不归本包。除 `minimal` 外,随附的 Web preset 都会向 agent 提供 `/goal`。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -25,7 +25,7 @@ kind: "package-reference"
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## 使用本包
|
|
27
27
|
|
|
28
|
-
与 `ui-conversation` 及 goal 领域包一起挂载本插件;只要会话存在目标,条带就会作为 composer 上下文堆栈的第二张卡片出现(位于 Todo 之后、Queue
|
|
28
|
+
与 `ui-conversation` 及 goal 领域包一起挂载本插件;只要会话存在目标,条带就会作为 composer 上下文堆栈的第二张卡片出现(位于 Todo 之后、Queue 之前)。已 armed 的 active goal 提供暂停动作;active-but-disarmed 或 paused 的 goal 提供恢复;编辑重写目标文本;清除移除目标,并在投影追上之前抑制条带。
|
|
29
29
|
|
|
30
30
|
### 指令输入气泡
|
|
31
31
|
|
|
@@ -43,7 +43,7 @@ kind: "package-reference"
|
|
|
43
43
|
<details>
|
|
44
44
|
<summary>实现细节——点击展开</summary>
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
持久 goal 经 `useProjection('goal')` 到达(由历史尾页播种、`session/projection` 帧更新)。注入面携带 registrant-private 的 activation hook source 与四个变更动词。该 source 仅在框架 hook 观察期间订阅,读取 `ctx.remote.goals.get`、订阅 `goal/activation-changed`,并在 running 状态或连接 reset 时刷新。live event epoch 会让在途读取失效,因此较旧的 HTTP 响应不能覆盖更新的 activation 边界;running 刷新会保留最后一次已知 activation,直到读取完成。条带不持有领域 store 或跨插件缓存。每个变更在调用时从会话当前投影值读取 CAS ref,比较并交换(RPC 的 CAS)就是陈旧性护栏。由于 React 的 pending 渲染无法拦住同一帧内的点击,条带会同步为变更建立 single-flight 防护。指令输入投影是独立的 Conversation Definition,在通用命令结果 Node 之前构建 `command-input` Chat Node;它绝不创建 `user/message` 或模型轮次。
|
|
47
47
|
|
|
48
48
|
</details>
|
|
49
49
|
|
|
@@ -76,7 +76,6 @@ kind: "package-reference"
|
|
|
76
76
|
|
|
77
77
|
这些限制界定了当前 goal 表面。它们是当前包约束,不是 goal 领域对比或任务积压。
|
|
78
78
|
|
|
79
|
-
- **只反映持久阶段**——投影省略进程本地的激活状态,因此条带无法区分已激活但未武装的 goal 与已武装的 goal;恢复经 RPC 侧重新武装。不存在宿主实时激活通道。
|
|
80
79
|
- **Host 状态与 preset 无关**——把活跃会话切换到 `minimal` 后,Host 拥有的 goal 仍会保留。`/goal` 与 goal 工具会消失,但该条带仍可编辑、暂停、恢复或清除 goal。
|
|
81
80
|
|
|
82
81
|
<a id="dev-note"></a>
|
|
@@ -89,4 +88,4 @@ kind: "package-reference"
|
|
|
89
88
|
|
|
90
89
|
</details>
|
|
91
90
|
|
|
92
|
-
**运行时不变式:** 不发布伴生入口。插件只注册一个 GoalBar dock,HMR
|
|
91
|
+
**运行时不变式:** 不发布伴生入口。插件只注册一个 GoalBar dock,HMR 测试覆盖释放;持久状态来自 goal projection,进程本地 activation 来自入口私有 hook source,且该 source 只在框架 hook 观察期间订阅。
|
package/lib/client.js
CHANGED
|
@@ -7,6 +7,122 @@ window.__ModuleLoader__.load({
|
|
|
7
7
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
8
|
let react = require("react");
|
|
9
9
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
10
|
+
//#region lib/types/client/activation-source.js
|
|
11
|
+
/** Goal activation observable that orders Remote reads and live activation events. */
|
|
12
|
+
/** Compare two empty-or-populated activation snapshots by value. */
|
|
13
|
+
function sameSnapshot(left, right) {
|
|
14
|
+
return left.id === right.id && left.revision === right.revision && left.activation === right.activation;
|
|
15
|
+
}
|
|
16
|
+
/** Return the current active CAS ref, or undefined when the goal is not active. */
|
|
17
|
+
function activeRef(projection) {
|
|
18
|
+
return projection?.goal.phase === "active" ? projection.goal : void 0;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create one registrant-private activation source. The source subscribes only
|
|
22
|
+
* while a framework hook observes it, so unmount releases the Remote event,
|
|
23
|
+
* projection, running-snapshot, and reset listeners.
|
|
24
|
+
* @param deps - projection, session, Remote read, and live-event inputs.
|
|
25
|
+
* @returns stable snapshot source consumed by `useGoalActivation`.
|
|
26
|
+
*/
|
|
27
|
+
function createGoalActivationSource(deps) {
|
|
28
|
+
let snapshot = {};
|
|
29
|
+
let subscriptions = 0;
|
|
30
|
+
let disposers = [];
|
|
31
|
+
let running = deps.session.getSnapshot().running;
|
|
32
|
+
let eventEpoch = 0;
|
|
33
|
+
let projectionEpoch = 0;
|
|
34
|
+
let readEpoch = 0;
|
|
35
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
36
|
+
const publish = (next) => {
|
|
37
|
+
if (sameSnapshot(snapshot, next)) return;
|
|
38
|
+
snapshot = next;
|
|
39
|
+
for (const listener of listeners) listener();
|
|
40
|
+
};
|
|
41
|
+
const startRead = (ref) => {
|
|
42
|
+
if (ref === void 0) return;
|
|
43
|
+
const read = ++readEpoch;
|
|
44
|
+
const startedAtEvent = eventEpoch;
|
|
45
|
+
const startedAtProjection = projectionEpoch;
|
|
46
|
+
deps.getGoal().then((result) => {
|
|
47
|
+
if (read !== readEpoch || startedAtEvent !== eventEpoch || startedAtProjection !== projectionEpoch) return;
|
|
48
|
+
if (!result.ok) return;
|
|
49
|
+
const goal = result.value;
|
|
50
|
+
/* v8 ignore next 4 -- projection drive is the authoritative clear edge; an active projection with no live goal is transient. */
|
|
51
|
+
if (goal === void 0) {
|
|
52
|
+
if (activeRef(deps.projection.getSnapshot()) === void 0) publish({});
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
publish({
|
|
56
|
+
id: goal.id,
|
|
57
|
+
revision: goal.revision,
|
|
58
|
+
activation: goal.activation
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
const refreshProjection = () => {
|
|
63
|
+
projectionEpoch++;
|
|
64
|
+
const ref = activeRef(deps.projection.getSnapshot());
|
|
65
|
+
if (ref === void 0) {
|
|
66
|
+
/* v8 ignore next -- clearing an already-empty activation snapshot is idempotent. */
|
|
67
|
+
if (snapshot.id !== void 0) publish({});
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (snapshot.id !== ref.id || snapshot.revision !== ref.revision) publish({
|
|
71
|
+
id: ref.id,
|
|
72
|
+
revision: ref.revision
|
|
73
|
+
});
|
|
74
|
+
startRead(ref);
|
|
75
|
+
};
|
|
76
|
+
const onActivation = (goal) => {
|
|
77
|
+
eventEpoch++;
|
|
78
|
+
readEpoch++;
|
|
79
|
+
publish(goal === void 0 ? {} : {
|
|
80
|
+
id: goal.id,
|
|
81
|
+
revision: goal.revision,
|
|
82
|
+
activation: goal.activation
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
const onRunning = () => {
|
|
86
|
+
const next = deps.session.getSnapshot().running;
|
|
87
|
+
if (next === running) return;
|
|
88
|
+
running = next;
|
|
89
|
+
startRead(activeRef(deps.projection.getSnapshot()));
|
|
90
|
+
};
|
|
91
|
+
const onReset = () => {
|
|
92
|
+
eventEpoch++;
|
|
93
|
+
projectionEpoch++;
|
|
94
|
+
startRead(activeRef(deps.projection.getSnapshot()));
|
|
95
|
+
};
|
|
96
|
+
const start = () => {
|
|
97
|
+
disposers = [
|
|
98
|
+
deps.projection.subscribe(refreshProjection),
|
|
99
|
+
deps.session.subscribe(onRunning),
|
|
100
|
+
deps.subscribeActivation(onActivation),
|
|
101
|
+
deps.subscribeReset(onReset)
|
|
102
|
+
];
|
|
103
|
+
running = deps.session.getSnapshot().running;
|
|
104
|
+
refreshProjection();
|
|
105
|
+
};
|
|
106
|
+
const stop = () => {
|
|
107
|
+
for (const dispose of disposers) dispose();
|
|
108
|
+
disposers = [];
|
|
109
|
+
readEpoch++;
|
|
110
|
+
};
|
|
111
|
+
return {
|
|
112
|
+
getSnapshot: () => snapshot,
|
|
113
|
+
subscribe(listener) {
|
|
114
|
+
listeners.add(listener);
|
|
115
|
+
if (subscriptions === 0) start();
|
|
116
|
+
subscriptions++;
|
|
117
|
+
return () => {
|
|
118
|
+
listeners.delete(listener);
|
|
119
|
+
subscriptions--;
|
|
120
|
+
if (subscriptions === 0) stop();
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
10
126
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-goal/src/client/GoalBar.module.css.mjs
|
|
11
127
|
const css$1 = ".nLMEza_dock{box-sizing:border-box;width:calc(100% - var(--dsh-composer-side-clearance) - var(--dsh-composer-side-clearance) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));margin:0 auto}.nLMEza_bar{box-sizing:border-box;width:100%;max-width:calc(var(--dsh-composer-card-max-width) - 4 * var(--dsh-composer-dock-inset));border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-specific-tip);border-radius:12px;align-items:center;gap:10px;height:36px;margin:0 auto;padding:4px 5px 4px 12px;display:flex}.nLMEza_goalGlyph{color:var(--dsw-alias-label-tertiary);flex:none;display:inline-flex}.nLMEza_label{color:var(--dsw-alias-label-primary);flex:none;font-size:13px;font-weight:500;line-height:24px}.nLMEza_objective{min-width:0;color:var(--dsw-alias-label-primary-dimmed);text-overflow:ellipsis;white-space:nowrap;flex:1;font-size:13px;line-height:20px;overflow:hidden}.nLMEza_error{min-width:0;color:var(--dsw-alias-state-error-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;font-size:12px;line-height:20px;overflow:hidden}.nLMEza_objectiveInput{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-base);min-width:0;height:26px;color:var(--dsw-alias-label-primary);border-radius:6px;outline:none;flex:1;padding:0 8px;font-size:13px;line-height:20px}.nLMEza_objectiveInput:focus{border-color:var(--dsw-alias-state-business-primary)}.nLMEza_objectiveInput::placeholder{color:var(--dsw-alias-label-caption)}.nLMEza_actions{flex:none;align-items:center;gap:10px;display:flex}.nLMEza_iconBtn{corner-shape:round;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:999px;justify-content:center;align-items:center;padding:0;display:inline-flex}.nLMEza_iconBtn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.nLMEza_iconBtn:disabled{opacity:.4;cursor:default}";
|
|
12
128
|
const tagId$1 = "@deepseek-ai/dsh-client-ui-goal/GoalBar.module.css";
|
|
@@ -33,11 +149,11 @@ window.__ModuleLoader__.load({
|
|
|
33
149
|
/**
|
|
34
150
|
* GoalBar: the goal indicator docked above the message composer (input dock
|
|
35
151
|
* strip). A present goal shows a goal glyph, a phase label, the truncated
|
|
36
|
-
* objective, and icon actions — resume when paused, edit
|
|
37
|
-
* same strip), and clear. Goal creation lives on the
|
|
38
|
-
* here: loading (undefined), no goal (null), and complete
|
|
39
|
-
* nothing.
|
|
40
|
-
* the injected
|
|
152
|
+
* objective, and icon actions — resume when active-disarmed or paused, edit
|
|
153
|
+
* (inline form in the same strip), and clear. Goal creation lives on the
|
|
154
|
+
* `/goal` command, not here: loading (undefined), no goal (null), and complete
|
|
155
|
+
* goals render nothing. Durable state arrives as the projected whole snapshot;
|
|
156
|
+
* process-local activation arrives through the injected activation hook.
|
|
41
157
|
*/
|
|
42
158
|
/** Strip label keys per visible phase; complete goals render nothing. */
|
|
43
159
|
const PHASE_LABELS = {
|
|
@@ -45,7 +161,12 @@ window.__ModuleLoader__.load({
|
|
|
45
161
|
paused: "phase.paused",
|
|
46
162
|
blocked: "phase.blocked"
|
|
47
163
|
};
|
|
48
|
-
|
|
164
|
+
/** Strip label for an active goal using its process-local activation. */
|
|
165
|
+
function activeLabel(activation, t) {
|
|
166
|
+
if (activation === "disarmed") return t("phase.active.disarmed");
|
|
167
|
+
return t(PHASE_LABELS.active);
|
|
168
|
+
}
|
|
169
|
+
function GoalBar({ goal, activation, onEdit, onPause, onResume, onClear, t }) {
|
|
49
170
|
const [editing, setEditing] = (0, react.useState)(false);
|
|
50
171
|
const [draft, setDraft] = (0, react.useState)("");
|
|
51
172
|
const [pending, setPending] = (0, react.useState)(false);
|
|
@@ -143,6 +264,8 @@ window.__ModuleLoader__.load({
|
|
|
143
264
|
})
|
|
144
265
|
});
|
|
145
266
|
const title = goal.phase === "blocked" ? goal.blockedReason?.message : void 0;
|
|
267
|
+
const label = goal.phase === "active" ? activeLabel(activation, t) : t(PHASE_LABELS[goal.phase]);
|
|
268
|
+
const showResume = goal.phase === "paused" || goal.phase === "active" && activation === "disarmed";
|
|
146
269
|
return (0, react_jsx_runtime.jsx)("div", {
|
|
147
270
|
className: GoalBar_module_css_default.dock,
|
|
148
271
|
"data-goal-bar": true,
|
|
@@ -156,7 +279,7 @@ window.__ModuleLoader__.load({
|
|
|
156
279
|
}),
|
|
157
280
|
(0, react_jsx_runtime.jsx)("span", {
|
|
158
281
|
className: GoalBar_module_css_default.label,
|
|
159
|
-
children:
|
|
282
|
+
children: label
|
|
160
283
|
}),
|
|
161
284
|
(0, react_jsx_runtime.jsx)("span", {
|
|
162
285
|
className: GoalBar_module_css_default.objective,
|
|
@@ -170,7 +293,7 @@ window.__ModuleLoader__.load({
|
|
|
170
293
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
171
294
|
className: GoalBar_module_css_default.actions,
|
|
172
295
|
children: [
|
|
173
|
-
goal.phase === "active" && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
296
|
+
goal.phase === "active" && activation === "armed" && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
174
297
|
label: t("action.pause"),
|
|
175
298
|
side: "bottom",
|
|
176
299
|
delayMs: 500,
|
|
@@ -185,7 +308,7 @@ window.__ModuleLoader__.load({
|
|
|
185
308
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPauseOutline16, { size: 14 })
|
|
186
309
|
})
|
|
187
310
|
}),
|
|
188
|
-
|
|
311
|
+
showResume && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
189
312
|
label: t("action.resume"),
|
|
190
313
|
side: "bottom",
|
|
191
314
|
delayMs: 500,
|
|
@@ -237,11 +360,16 @@ window.__ModuleLoader__.load({
|
|
|
237
360
|
})
|
|
238
361
|
});
|
|
239
362
|
}
|
|
240
|
-
/** Dock adapter:
|
|
241
|
-
function GoalDock({ useProjection, onEdit, onPause, onResume, onClear, t }) {
|
|
363
|
+
/** Dock adapter: overlays process-local activation on the durable goal projection. */
|
|
364
|
+
function GoalDock({ useProjection, useGoalActivation, onEdit, onPause, onResume, onClear, t }) {
|
|
242
365
|
const projection = useProjection("goal");
|
|
366
|
+
const goal = projection === void 0 || projection === null ? projection : projection.goal;
|
|
367
|
+
const goalId = goal?.id;
|
|
368
|
+
const revision = goal?.revision;
|
|
369
|
+
const activation = useGoalActivation((next) => next.id === goalId && next.revision === revision ? next.activation : void 0);
|
|
243
370
|
return (0, react_jsx_runtime.jsx)(GoalBar, {
|
|
244
|
-
goal
|
|
371
|
+
goal,
|
|
372
|
+
...activation === void 0 ? {} : { activation },
|
|
245
373
|
onEdit,
|
|
246
374
|
onPause,
|
|
247
375
|
onResume,
|
|
@@ -346,6 +474,7 @@ window.__ModuleLoader__.load({
|
|
|
346
474
|
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
347
475
|
const zh = {
|
|
348
476
|
"phase.active": "进行中的目标",
|
|
477
|
+
"phase.active.disarmed": "未运行的目标",
|
|
349
478
|
"phase.paused": "已暂停的目标",
|
|
350
479
|
"phase.blocked": "受阻的目标",
|
|
351
480
|
"objective.aria": "目标内容",
|
|
@@ -360,6 +489,7 @@ window.__ModuleLoader__.load({
|
|
|
360
489
|
/** English dictionary, checked complete against the zh key set. */
|
|
361
490
|
const en = {
|
|
362
491
|
"phase.active": "Ongoing Goal",
|
|
492
|
+
"phase.active.disarmed": "Inactive Goal",
|
|
363
493
|
"phase.paused": "Paused Goal",
|
|
364
494
|
"phase.blocked": "Blocked Goal",
|
|
365
495
|
"objective.aria": "Goal objective",
|
|
@@ -421,28 +551,41 @@ window.__ModuleLoader__.load({
|
|
|
421
551
|
id: "goal",
|
|
422
552
|
order: 10,
|
|
423
553
|
locale: NS,
|
|
424
|
-
inject: (sessionId) =>
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
554
|
+
inject: (sessionId) => {
|
|
555
|
+
const binding = sessions.binding(sessionId);
|
|
556
|
+
if (binding === void 0) throw new Error(`ui-goal: session "${sessionId}" is unavailable`);
|
|
557
|
+
return {
|
|
558
|
+
hooks: { goalActivation: createGoalActivationSource({
|
|
559
|
+
projection: binding.session.projections.faceOf("goal"),
|
|
560
|
+
session: binding.session,
|
|
561
|
+
getGoal: () => ctx.remote.goals.get(sessionId),
|
|
562
|
+
subscribeActivation: (listener) => ctx.remote.$on("goal/activation-changed", (event) => {
|
|
563
|
+
if (event.sessionId === sessionId) listener(event.goal);
|
|
564
|
+
}),
|
|
565
|
+
subscribeReset: (listener) => ctx.on("connection/reset", listener)
|
|
566
|
+
}) },
|
|
567
|
+
onEdit: async (objective) => {
|
|
568
|
+
const ref = refOf(sessionId);
|
|
569
|
+
if (ref === void 0) return noCurrentGoal;
|
|
570
|
+
return await ctx.remote.goals.edit(sessionId, ref, { objective });
|
|
571
|
+
},
|
|
572
|
+
onPause: async () => {
|
|
573
|
+
const ref = refOf(sessionId);
|
|
574
|
+
if (ref === void 0) return noCurrentGoal;
|
|
575
|
+
return await ctx.remote.goals.pause(sessionId, ref);
|
|
576
|
+
},
|
|
577
|
+
onResume: async () => {
|
|
578
|
+
const ref = refOf(sessionId);
|
|
579
|
+
if (ref === void 0) return noCurrentGoal;
|
|
580
|
+
return await ctx.remote.goals.resume(sessionId, ref);
|
|
581
|
+
},
|
|
582
|
+
onClear: async () => {
|
|
583
|
+
const ref = refOf(sessionId);
|
|
584
|
+
if (ref === void 0) return noCurrentGoal;
|
|
585
|
+
return await ctx.remote.goals.clear(sessionId, ref);
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
}
|
|
446
589
|
}, GoalDock));
|
|
447
590
|
}
|
|
448
591
|
//#endregion
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GoalBar: the goal indicator docked above the message composer (input dock
|
|
3
3
|
* strip). A present goal shows a goal glyph, a phase label, the truncated
|
|
4
|
-
* objective, and icon actions — resume when paused, edit
|
|
5
|
-
* same strip), and clear. Goal creation lives on the
|
|
6
|
-
* here: loading (undefined), no goal (null), and complete
|
|
7
|
-
* nothing.
|
|
8
|
-
* the injected
|
|
4
|
+
* objective, and icon actions — resume when active-disarmed or paused, edit
|
|
5
|
+
* (inline form in the same strip), and clear. Goal creation lives on the
|
|
6
|
+
* `/goal` command, not here: loading (undefined), no goal (null), and complete
|
|
7
|
+
* goals render nothing. Durable state arrives as the projected whole snapshot;
|
|
8
|
+
* process-local activation arrives through the injected activation hook.
|
|
9
9
|
*/
|
|
10
|
-
import type { GoalSnapshot } from '@deepseek-ai/dsh-goal/client';
|
|
11
|
-
import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
|
|
12
|
-
import type { GoalBarActions } from './slots.ts';
|
|
10
|
+
import type { GoalActivation, GoalSnapshot } from '@deepseek-ai/dsh-goal/client';
|
|
11
|
+
import type { InjectFace, PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
|
|
12
|
+
import type { GoalBarActions, GoalBarInjected } from './slots.ts';
|
|
13
13
|
export interface GoalBarProps extends GoalBarActions {
|
|
14
14
|
/** Current goal snapshot; undefined = capability absent or loading, null = no goal set. */
|
|
15
15
|
goal: GoalSnapshot | null | undefined;
|
|
16
|
+
/** Process-local continuation activation; absent while the live read is pending. */
|
|
17
|
+
activation?: GoalActivation;
|
|
16
18
|
}
|
|
17
|
-
export declare function GoalBar({ goal, onEdit, onPause, onResume, onClear, t }: GoalBarProps & PropsLocale<'goal'>): import("react").JSX.Element | null;
|
|
18
|
-
/** Full props of the dock entry: InputZone owner share +
|
|
19
|
-
export type GoalDockProps = import('@deepseek-ai/dsh-client-ui-slots').PropsRuntime<'conversation.input.dock'> &
|
|
20
|
-
/** Dock adapter:
|
|
21
|
-
export declare function GoalDock({ useProjection, onEdit, onPause, onResume, onClear, t }: GoalDockProps): import("react").JSX.Element;
|
|
19
|
+
export declare function GoalBar({ goal, activation, onEdit, onPause, onResume, onClear, t }: GoalBarProps & PropsLocale<'goal'>): import("react").JSX.Element | null;
|
|
20
|
+
/** Full props of the dock entry: InputZone owner share + injected verbs/activation hook + the locale seat. */
|
|
21
|
+
export type GoalDockProps = import('@deepseek-ai/dsh-client-ui-slots').PropsRuntime<'conversation.input.dock'> & InjectFace<GoalBarInjected> & PropsLocale<'goal'>;
|
|
22
|
+
/** Dock adapter: overlays process-local activation on the durable goal projection. */
|
|
23
|
+
export declare function GoalDock({ useProjection, useGoalActivation, onEdit, onPause, onResume, onClear, t, }: GoalDockProps): import("react").JSX.Element;
|
|
22
24
|
//# sourceMappingURL=GoalBar.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Goal activation observable that orders Remote reads and live activation events. */
|
|
2
|
+
import type { RemoteResult } from '@deepseek-ai/dsh-api-remotes/client';
|
|
3
|
+
import type { HostObservable } from '@deepseek-ai/dsh-client-ui-slots';
|
|
4
|
+
import type { GoalActivationChanged, GoalProjection, GoalView } from '@deepseek-ai/dsh-goal/client';
|
|
5
|
+
import type { GoalActivationSnapshot } from './slots.ts';
|
|
6
|
+
/** Live inputs for one Session's goal activation source. */
|
|
7
|
+
export interface GoalActivationDeps {
|
|
8
|
+
/** Durable projection used to derive the current active goal ref. */
|
|
9
|
+
readonly projection: HostObservable<GoalProjection | null | undefined>;
|
|
10
|
+
/** Session snapshot; running flips trigger a fresh authoritative read. */
|
|
11
|
+
readonly session: HostObservable<{
|
|
12
|
+
readonly running: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
/** Read the current live goal at call time. */
|
|
15
|
+
readonly getGoal: () => Promise<RemoteResult<GoalView | undefined>>;
|
|
16
|
+
/** Subscribe to activation edges after the transport delivers them. */
|
|
17
|
+
readonly subscribeActivation: (listener: (goal: GoalActivationChanged['goal']) => void) => () => void;
|
|
18
|
+
/** Refresh after a connection-generation reset. */
|
|
19
|
+
readonly subscribeReset: (listener: () => void) => () => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create one registrant-private activation source. The source subscribes only
|
|
23
|
+
* while a framework hook observes it, so unmount releases the Remote event,
|
|
24
|
+
* projection, running-snapshot, and reset listeners.
|
|
25
|
+
* @param deps - projection, session, Remote read, and live-event inputs.
|
|
26
|
+
* @returns stable snapshot source consumed by `useGoalActivation`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function createGoalActivationSource(deps: GoalActivationDeps): HostObservable<GoalActivationSnapshot>;
|
|
29
|
+
//# sourceMappingURL=activation-source.d.ts.map
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Goal surface plugin, browser half: the GoalBar entry in the
|
|
3
|
-
* conversation.input.dock strip.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* four mutation verbs through the generated Goal Remote API;
|
|
8
|
-
* their CAS ref reads the session's current projected value at call time.
|
|
3
|
+
* conversation.input.dock strip. The durable goal arrives through
|
|
4
|
+
* `useProjection('goal')`. A registrant-private activation hook source owns
|
|
5
|
+
* the live Remote read and event subscription; the inject face carries that
|
|
6
|
+
* hook plus the four mutation verbs through the generated Goal Remote API.
|
|
9
7
|
* This plugin does not create goals; deployments may expose /goal separately.
|
|
10
8
|
*/
|
|
11
9
|
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
12
10
|
import { type GoalKey } from './locales.ts';
|
|
13
11
|
export { GoalBar, GoalDock } from './GoalBar.tsx';
|
|
14
|
-
export type { GoalActionResult, GoalBarActions } from './slots.ts';
|
|
12
|
+
export type { GoalActionResult, GoalBarActions, } from './slots.ts';
|
|
15
13
|
export type { GoalKey } from './locales.ts';
|
|
16
14
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
17
15
|
interface LocaleNamespaceMap {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
3
3
|
export declare const zh: {
|
|
4
4
|
'phase.active': string;
|
|
5
|
+
'phase.active.disarmed': string;
|
|
5
6
|
'phase.paused': string;
|
|
6
7
|
'phase.blocked': string;
|
|
7
8
|
'objective.aria': string;
|
|
@@ -18,6 +19,7 @@ export type GoalKey = keyof typeof zh;
|
|
|
18
19
|
/** English dictionary, checked complete against the zh key set. */
|
|
19
20
|
export declare const en: {
|
|
20
21
|
'phase.active': string;
|
|
22
|
+
'phase.active.disarmed': string;
|
|
21
23
|
'phase.paused': string;
|
|
22
24
|
'phase.blocked': string;
|
|
23
25
|
'objective.aria': string;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GoalBar's injected face. The target 'conversation.input.dock' slot is
|
|
3
3
|
* declared (children table) and typed by ui-conversation; this package only
|
|
4
|
-
* contributes the entry, so no SlotMap merge lives here. The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* contributes the entry, so no SlotMap merge lives here. The durable goal
|
|
5
|
+
* value arrives through `useProjection('goal')` (the framework standard kit);
|
|
6
|
+
* the injected face carries mutation verbs and a registrant-private
|
|
7
|
+
* activation hook source.
|
|
8
8
|
*/
|
|
9
9
|
import type { RemoteResult } from '@deepseek-ai/dsh-api-remotes/client';
|
|
10
|
+
import type { HostObservable } from '@deepseek-ai/dsh-client-ui-slots';
|
|
11
|
+
import type { GoalActivation, GoalId } from '@deepseek-ai/dsh-goal/client';
|
|
10
12
|
/**
|
|
11
13
|
* The one failure the strip reports without a wire call: the session projects
|
|
12
14
|
* no goal, so no CAS ref exists to address a mutation to.
|
|
@@ -24,6 +26,21 @@ export interface GoalLocalFailure {
|
|
|
24
26
|
* projection — so the success value stays unread here.
|
|
25
27
|
*/
|
|
26
28
|
export type GoalActionResult = RemoteResult<unknown> | GoalLocalFailure;
|
|
29
|
+
/** Process-local activation matched to one exact goal revision. */
|
|
30
|
+
export interface GoalActivationSnapshot {
|
|
31
|
+
/** Exact current goal id, absent before create or after clear. */
|
|
32
|
+
readonly id?: GoalId;
|
|
33
|
+
/** Exact current revision for the activation edge. */
|
|
34
|
+
readonly revision?: number;
|
|
35
|
+
/** Process-local continuation state; absent while no matching edge is known. */
|
|
36
|
+
readonly activation?: GoalActivation;
|
|
37
|
+
}
|
|
38
|
+
/** Registrant-private observable source bound by the slot renderer. */
|
|
39
|
+
export interface GoalActivationInjected {
|
|
40
|
+
readonly hooks: {
|
|
41
|
+
readonly goalActivation: HostObservable<GoalActivationSnapshot>;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
27
44
|
/** Injected business face of the GoalBar dock entry: the mutation verbs (function properties: the strip destructures them freely). */
|
|
28
45
|
export interface GoalBarActions {
|
|
29
46
|
/**
|
|
@@ -38,4 +55,6 @@ export interface GoalBarActions {
|
|
|
38
55
|
/** Clear the current goal (tombstone). */
|
|
39
56
|
onClear: () => Promise<GoalActionResult>;
|
|
40
57
|
}
|
|
58
|
+
/** Injected business face of the GoalBar dock entry. */
|
|
59
|
+
export type GoalBarInjected = GoalBarActions & GoalActivationInjected;
|
|
41
60
|
//# sourceMappingURL=slots.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-ui-goal",
|
|
3
3
|
"description": "Session goal surface: GoalBar docked above the composer, read from the goal session projection",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.5-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -48,21 +48,21 @@
|
|
|
48
48
|
"@types/react": "~18.3.1",
|
|
49
49
|
"react": "^18.2.0",
|
|
50
50
|
"react-dom": "^18.2.0",
|
|
51
|
-
"@deepseek-ai/dsh-api-
|
|
52
|
-
"@deepseek-ai/dsh-
|
|
53
|
-
"@deepseek-ai/dsh-client-
|
|
54
|
-
"@deepseek-ai/dsh-client-ui-chat": "^0.1.
|
|
55
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.
|
|
56
|
-
"@deepseek-ai/dsh-client-
|
|
57
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.
|
|
58
|
-
"@deepseek-ai/dsh-client-ui-
|
|
59
|
-
"@deepseek-ai/dsh-client-ui-
|
|
60
|
-
"@deepseek-ai/dsh-commands": "^0.1.
|
|
61
|
-
"@deepseek-ai/dsh-goal": "^0.1.
|
|
51
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.1.5-alpha.2",
|
|
52
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.5-alpha.2",
|
|
53
|
+
"@deepseek-ai/dsh-client-test-runtime": "^0.1.5-alpha.2",
|
|
54
|
+
"@deepseek-ai/dsh-client-ui-chat": "^0.1.5-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.5-alpha.2",
|
|
57
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-alpha.2",
|
|
58
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-alpha.2",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-session": "^0.1.5-alpha.2",
|
|
60
|
+
"@deepseek-ai/dsh-commands": "^0.1.5-alpha.2",
|
|
61
|
+
"@deepseek-ai/dsh-goal": "^0.1.5-alpha.2",
|
|
62
62
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
63
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
64
|
-
"@deepseek-ai/dsh-
|
|
65
|
-
"@deepseek-ai/dsh-
|
|
63
|
+
"@deepseek-ai/dsh-session": "^0.1.5-alpha.2",
|
|
64
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.5-alpha.2",
|
|
65
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.5-alpha.2"
|
|
66
66
|
},
|
|
67
67
|
"files": [
|
|
68
68
|
"lib/index.js",
|