@deepseek-ai/dsh-client-ui-goal 0.0.1-rc.1

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/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, DeepSeek
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/client/ui-goal/README.md
5
+ README.md: a53fb3a89eaee364cb025ca728ca42ce934887b0
6
+ README.zh.md: 1ad9f50aee5b103f6455e4d4b7d29fa9eb29a108
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # @deepseek-ai/dsh-client-ui-goal
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ Goal surface plugin, browser half: the `GoalBar` strip is the second standalone card in the `conversation.input.dock` composer-context stack (order 10, after Todo and before Queue). The live goal arrives through `useProjection('goal')` — the host-computed whole value seeded by the history tail page and updated by `session/projection` frames — so the plugin owns no domain store, refresh chain, or event listener. The slot inject face carries only the four mutation verbs (edit / pause / resume / clear through `ctx.remote.goals` — an active goal offers the pause action, a paused one resume); each reads the CAS ref from the session's current projected value at call time and surfaces the rejected Remote error inline. The strip single-flights mutations synchronously because React's pending render cannot fence same-frame clicks; after a successful clear it immediately suppresses that exact goal id while the authoritative null projection catches up. Goal creation stays on the `/goal` host command; loading, absent, completed, and successfully cleared goals render nothing.
6
+
7
+ The `/client` export surface is the plugin body (`apply`/`inject`), the `GoalBar`/`GoalDock` components, and the injected verb face types.
8
+
9
+ ## Model Experience
10
+
11
+ Indirectly, through the `goals/edit`, `goals/pause`, `goals/resume`, and `goals/clear` Remote methods the strip invokes: each accepted mutation commits in a durable `agent/inbox/spliced` insertion, which the goal projection folds immediately, and queues a `goal/change` context message. The model sees that context only if a later pre-step admits it; discarding the queued message does not roll back the projected state. The strip itself adds no prompt content.
12
+
13
+ #### KV Cache effect
14
+
15
+ None unless the queued goal context is admitted. An admitted context extends the history tail like any other message; an insertion discarded before admission does not affect the cache.
16
+
17
+ ## Known Limitations and Deferred Work
18
+
19
+ - **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.
package/README.zh.md ADDED
@@ -0,0 +1,19 @@
1
+ # @deepseek-ai/dsh-client-ui-goal
2
+
3
+ [English](README.md) | 中文
4
+
5
+ Goal 界面插件(浏览器端部分):`GoalBar` 条带是 `conversation.input.dock` composer 上下文堆栈中的第二张独立卡片(order 10,位于 Todo 之后、Queue 之前)。活值经 `useProjection('goal')` 到达——host 计算的全量值由历史尾页播种、由 `session/projection` 帧更新——因此本插件不持有领域 store、不设刷新链、不挂事件监听。slot 注入面只携带四个变更动词(edit / pause / resume / clear,经 `ctx.remote.goals` 调用——active 的 goal 提供暂停动作,paused 的提供恢复);每个动词在调用时从会话当前投影值读取 CAS ref,并将 Remote 调用的拒绝错误内联呈现。由于 React 的 pending 渲染无法拦住同一帧内的点击,横条会同步为变更建立 single-flight 防护;清除成功后,会立即抑制该 goal id 对应的目标显示,直到权威的 null 投影追上。goal 的创建仍归 `/goal` host 命令;加载中、无 goal、已完成和已成功清除的 goal 一律不渲染。
6
+
7
+ `/client` 的导出接口包括插件本体(`apply`/`inject`)、`GoalBar`/`GoalDock` 组件与注入动词面类型。
8
+
9
+ ## 模型体验
10
+
11
+ 间接影响:条带通过调用 `goals/edit`、`goals/pause`、`goals/resume` 和 `goals/clear` Remote 方法提交变更;每次被接受的变更都会在持久 `agent/inbox/spliced` 插入项中提交,goal 投影会立即折叠该插入项,同时将一条 `goal/change` 上下文消息排队。只有后续 pre-step 准入该上下文时,模型才会看到它;丢弃已排队的消息不会回滚投影状态。条带自身不添加任何提示词内容。
12
+
13
+ #### KV Cache 影响
14
+
15
+ 除非已排队的 goal 上下文获准,否则没有影响。获准的上下文会像其他消息一样扩展历史尾部;准入前被丢弃的插入项不会影响缓存。
16
+
17
+ ## 已知限制与暂缓事项
18
+
19
+ - **只反映持久 phase**——投影省略进程本地 activation,因此条带无法区分 active-but-disarmed 与 armed 状态;resume 通过 RPC 重新置为 armed 状态。不存在 host 实时 activation 通道。
package/lib/client.js ADDED
@@ -0,0 +1,383 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@deepseek-ai/dsh-client-ui-goal",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ let react = require("react");
9
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-goal/src/client/GoalBar.module.css.mjs
11
+ const css = ".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:1px 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:1px solid var(--dsw-alias-border-l2);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{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
+ const tagId = "@deepseek-ai/dsh-client-ui-goal/GoalBar.module.css";
13
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
14
+ const tag = document.createElement("style");
15
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-goal";
16
+ tag.dataset.pluginCss = tagId;
17
+ tag.textContent = css;
18
+ document.head.appendChild(tag);
19
+ }
20
+ var GoalBar_module_css_default = {
21
+ "actions": "nLMEza_actions",
22
+ "goalGlyph": "nLMEza_goalGlyph",
23
+ "dock": "nLMEza_dock",
24
+ "objectiveInput": "nLMEza_objectiveInput",
25
+ "objective": "nLMEza_objective",
26
+ "error": "nLMEza_error",
27
+ "label": "nLMEza_label",
28
+ "bar": "nLMEza_bar",
29
+ "iconBtn": "nLMEza_iconBtn"
30
+ };
31
+ //#endregion
32
+ //#region lib/types/client/GoalBar.js
33
+ /**
34
+ * GoalBar: the goal indicator docked above the message composer (input dock
35
+ * strip). A present goal shows a goal glyph, a phase label, the truncated
36
+ * objective, and icon actions — resume when paused, edit (inline form in the
37
+ * same strip), and clear. Goal creation lives on the `/goal` command, not
38
+ * here: loading (undefined), no goal (null), and complete goals render
39
+ * nothing. Live state arrives as the projected whole snapshot; the verbs are
40
+ * the injected face.
41
+ */
42
+ /** Strip label keys per visible phase; complete goals render nothing. */
43
+ const PHASE_LABELS = {
44
+ active: "phase.active",
45
+ paused: "phase.paused",
46
+ blocked: "phase.blocked"
47
+ };
48
+ function GoalBar({ goal, onEdit, onPause, onResume, onClear, t }) {
49
+ const [editing, setEditing] = (0, react.useState)(false);
50
+ const [draft, setDraft] = (0, react.useState)("");
51
+ const [pending, setPending] = (0, react.useState)(false);
52
+ const [actionError, setActionError] = (0, react.useState)(null);
53
+ const [clearedGoalId, setClearedGoalId] = (0, react.useState)(null);
54
+ const pendingRef = (0, react.useRef)(false);
55
+ const goalId = goal?.id;
56
+ (0, react.useEffect)(() => {
57
+ setEditing(false);
58
+ setActionError(null);
59
+ setClearedGoalId(null);
60
+ }, [goalId]);
61
+ const runAction = (0, react.useCallback)(async (action) => {
62
+ if (pendingRef.current) return void 0;
63
+ pendingRef.current = true;
64
+ setPending(true);
65
+ setActionError(null);
66
+ const result = await action();
67
+ pendingRef.current = false;
68
+ setPending(false);
69
+ if (!result.ok) setActionError(`${result.error.message} (${result.error.code})`);
70
+ return result;
71
+ }, []);
72
+ const handleEdit = (0, react.useCallback)(async () => {
73
+ const trimmed = draft.trim();
74
+ if (trimmed === "") return;
75
+ if ((await runAction(() => onEdit(trimmed)))?.ok) setEditing(false);
76
+ }, [
77
+ draft,
78
+ onEdit,
79
+ runAction
80
+ ]);
81
+ const handleClear = (0, react.useCallback)(async (clearedId) => {
82
+ if ((await runAction(onClear))?.ok) setClearedGoalId(clearedId);
83
+ }, [onClear, runAction]);
84
+ if (goal === void 0 || goal === null || goal.phase === "complete" || goal.id === clearedGoalId) return null;
85
+ if (editing) return (0, react_jsx_runtime.jsx)("div", {
86
+ className: GoalBar_module_css_default.dock,
87
+ "data-goal-bar": true,
88
+ children: (0, react_jsx_runtime.jsxs)("div", {
89
+ className: GoalBar_module_css_default.bar,
90
+ children: [
91
+ (0, react_jsx_runtime.jsx)("input", {
92
+ className: GoalBar_module_css_default.objectiveInput,
93
+ type: "text",
94
+ "aria-label": t("objective.aria"),
95
+ value: draft,
96
+ onChange: (e) => {
97
+ setDraft(e.target.value);
98
+ },
99
+ onKeyDown: (e) => {
100
+ if (e.key === "Enter") handleEdit();
101
+ if (e.key === "Escape") setEditing(false);
102
+ },
103
+ autoFocus: true
104
+ }),
105
+ actionError !== null && (0, react_jsx_runtime.jsx)("span", {
106
+ className: GoalBar_module_css_default.error,
107
+ role: "alert",
108
+ children: actionError
109
+ }),
110
+ (0, react_jsx_runtime.jsxs)("div", {
111
+ className: GoalBar_module_css_default.actions,
112
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
113
+ label: t("action.save"),
114
+ side: "bottom",
115
+ delayMs: 500,
116
+ children: (0, react_jsx_runtime.jsx)("button", {
117
+ type: "button",
118
+ className: GoalBar_module_css_default.iconBtn,
119
+ onClick: () => {
120
+ handleEdit();
121
+ },
122
+ disabled: pending || draft.trim() === "",
123
+ "aria-label": t("action.save"),
124
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, { size: 14 })
125
+ })
126
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
127
+ label: t("action.cancel"),
128
+ side: "bottom",
129
+ delayMs: 500,
130
+ children: (0, react_jsx_runtime.jsx)("button", {
131
+ type: "button",
132
+ className: GoalBar_module_css_default.iconBtn,
133
+ onClick: () => {
134
+ setEditing(false);
135
+ },
136
+ disabled: pending,
137
+ "aria-label": t("action.cancel"),
138
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
139
+ })
140
+ })]
141
+ })
142
+ ]
143
+ })
144
+ });
145
+ const title = goal.phase === "blocked" ? goal.blockedReason?.message : void 0;
146
+ return (0, react_jsx_runtime.jsx)("div", {
147
+ className: GoalBar_module_css_default.dock,
148
+ "data-goal-bar": true,
149
+ children: (0, react_jsx_runtime.jsxs)("div", {
150
+ className: GoalBar_module_css_default.bar,
151
+ title,
152
+ children: [
153
+ (0, react_jsx_runtime.jsx)("span", {
154
+ className: GoalBar_module_css_default.goalGlyph,
155
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconGoalOutline16, { size: 14 })
156
+ }),
157
+ (0, react_jsx_runtime.jsx)("span", {
158
+ className: GoalBar_module_css_default.label,
159
+ children: t(PHASE_LABELS[goal.phase])
160
+ }),
161
+ (0, react_jsx_runtime.jsx)("span", {
162
+ className: GoalBar_module_css_default.objective,
163
+ children: goal.objective
164
+ }),
165
+ actionError !== null && (0, react_jsx_runtime.jsx)("span", {
166
+ className: GoalBar_module_css_default.error,
167
+ role: "alert",
168
+ children: actionError
169
+ }),
170
+ (0, react_jsx_runtime.jsxs)("div", {
171
+ className: GoalBar_module_css_default.actions,
172
+ children: [
173
+ goal.phase === "active" && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
174
+ label: t("action.pause"),
175
+ side: "bottom",
176
+ delayMs: 500,
177
+ children: (0, react_jsx_runtime.jsx)("button", {
178
+ type: "button",
179
+ className: GoalBar_module_css_default.iconBtn,
180
+ disabled: pending,
181
+ onClick: () => {
182
+ runAction(onPause);
183
+ },
184
+ "aria-label": t("action.pause"),
185
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPauseOutline16, { size: 14 })
186
+ })
187
+ }),
188
+ goal.phase === "paused" && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
189
+ label: t("action.resume"),
190
+ side: "bottom",
191
+ delayMs: 500,
192
+ children: (0, react_jsx_runtime.jsx)("button", {
193
+ type: "button",
194
+ className: GoalBar_module_css_default.iconBtn,
195
+ disabled: pending,
196
+ onClick: () => {
197
+ runAction(onResume);
198
+ },
199
+ "aria-label": t("action.resume"),
200
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlayOutline16, { size: 14 })
201
+ })
202
+ }),
203
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
204
+ label: t("action.edit"),
205
+ side: "bottom",
206
+ delayMs: 500,
207
+ children: (0, react_jsx_runtime.jsx)("button", {
208
+ type: "button",
209
+ className: GoalBar_module_css_default.iconBtn,
210
+ disabled: pending,
211
+ onClick: () => {
212
+ setDraft(goal.objective);
213
+ setEditing(true);
214
+ },
215
+ "aria-label": t("action.edit"),
216
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 })
217
+ })
218
+ }),
219
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
220
+ label: t("action.clear"),
221
+ side: "bottom",
222
+ delayMs: 500,
223
+ children: (0, react_jsx_runtime.jsx)("button", {
224
+ type: "button",
225
+ className: GoalBar_module_css_default.iconBtn,
226
+ disabled: pending,
227
+ onClick: () => {
228
+ handleClear(goal.id);
229
+ },
230
+ "aria-label": t("action.clear"),
231
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 14 })
232
+ })
233
+ })
234
+ ]
235
+ })
236
+ ]
237
+ })
238
+ });
239
+ }
240
+ /** Dock adapter: reads the host-computed 'goal' projection (whole value; absent or null renders nothing). */
241
+ function GoalDock({ useProjection, onEdit, onPause, onResume, onClear, t }) {
242
+ const projection = useProjection("goal");
243
+ return (0, react_jsx_runtime.jsx)(GoalBar, {
244
+ goal: projection === void 0 ? void 0 : projection === null ? null : projection.goal,
245
+ onEdit,
246
+ onPause,
247
+ onResume,
248
+ onClear,
249
+ t
250
+ });
251
+ }
252
+ //#endregion
253
+ //#region lib/types/client/locales.js
254
+ /** `goal` namespace dictionaries. */
255
+ /** Simplified Chinese dictionary (the key-set source of truth). */
256
+ const zh = {
257
+ "phase.active": "进行中的目标",
258
+ "phase.paused": "已暂停的目标",
259
+ "phase.blocked": "受阻的目标",
260
+ "objective.aria": "目标内容",
261
+ "action.save": "保存目标",
262
+ "action.cancel": "取消编辑",
263
+ "action.pause": "暂停目标",
264
+ "action.resume": "恢复目标",
265
+ "action.edit": "编辑目标",
266
+ "action.clear": "清除目标"
267
+ };
268
+ /** English dictionary, checked complete against the zh key set. */
269
+ const en = {
270
+ "phase.active": "Ongoing Goal",
271
+ "phase.paused": "Paused Goal",
272
+ "phase.blocked": "Blocked Goal",
273
+ "objective.aria": "Goal objective",
274
+ "action.save": "Save goal",
275
+ "action.cancel": "Cancel edit",
276
+ "action.pause": "Pause goal",
277
+ "action.resume": "Resume goal",
278
+ "action.edit": "Edit goal",
279
+ "action.clear": "Clear goal"
280
+ };
281
+ //#endregion
282
+ //#region lib/types/client/index.js
283
+ /** Dictionary namespace owned by this plugin. */
284
+ const NS = "goal";
285
+ /** Required services: slots for the dock entry, sessions for the projected ref, API for Remote mutations, locale for the copy. */
286
+ const inject = [
287
+ "slots",
288
+ "sessions",
289
+ "remote",
290
+ "remote.goals",
291
+ "locale"
292
+ ];
293
+ /** Map one generated Remote call, including synchronous namespace lookup failures, to the fields rendered by the goal strip. */
294
+ async function settle(invoke) {
295
+ try {
296
+ await invoke();
297
+ return { ok: true };
298
+ } catch (error) {
299
+ const cause = error instanceof Error ? error.cause : void 0;
300
+ if (isRemoteError(cause)) return {
301
+ ok: false,
302
+ error: {
303
+ code: cause.code,
304
+ message: cause.message
305
+ }
306
+ };
307
+ return {
308
+ ok: false,
309
+ error: {
310
+ code: "internal",
311
+ message: error instanceof Error ? error.message : "goal mutation failed"
312
+ }
313
+ };
314
+ }
315
+ }
316
+ function isRemoteError(value) {
317
+ return value !== null && typeof value === "object" && typeof value.code === "string" && typeof value.message === "string";
318
+ }
319
+ /**
320
+ * Client plugin body: the GoalBar dock entry with its mutation verbs.
321
+ * @param ctx - client root context.
322
+ */
323
+ function apply(ctx) {
324
+ ctx.effect(() => ctx.locale.register(NS, {
325
+ zh,
326
+ en
327
+ }), "ui-goal: dictionaries");
328
+ const sessions = ctx.sessions;
329
+ /** The session's current projected CAS ref, read at verb call time (no staleness fence: the RPC's CAS is the guard). */
330
+ const refOf = (sessionId) => {
331
+ const projection = (sessions.binding(sessionId)?.session.projections.faceOf("goal"))?.getSnapshot();
332
+ if (projection == null) return void 0;
333
+ return {
334
+ id: projection.goal.id,
335
+ revision: projection.goal.revision
336
+ };
337
+ };
338
+ const noCurrentGoal = {
339
+ ok: false,
340
+ error: {
341
+ code: "no-current-goal",
342
+ message: "no current goal to mutate"
343
+ }
344
+ };
345
+ ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({
346
+ name: "conversation.input.dock",
347
+ id: "goal",
348
+ order: 10,
349
+ locale: NS,
350
+ inject: (sessionId) => ({
351
+ onEdit: async (objective) => {
352
+ const ref = refOf(sessionId);
353
+ if (ref === void 0) return noCurrentGoal;
354
+ return settle(() => ctx.remote.goals.edit(sessionId, ref, { objective }));
355
+ },
356
+ onPause: async () => {
357
+ const ref = refOf(sessionId);
358
+ if (ref === void 0) return noCurrentGoal;
359
+ return settle(() => ctx.remote.goals.pause(sessionId, ref));
360
+ },
361
+ onResume: async () => {
362
+ const ref = refOf(sessionId);
363
+ if (ref === void 0) return noCurrentGoal;
364
+ return settle(() => ctx.remote.goals.resume(sessionId, ref));
365
+ },
366
+ onClear: async () => {
367
+ const ref = refOf(sessionId);
368
+ if (ref === void 0) return noCurrentGoal;
369
+ return settle(() => ctx.remote.goals.clear(sessionId, ref));
370
+ }
371
+ })
372
+ }, GoalDock));
373
+ }
374
+ //#endregion
375
+ exports.GoalBar = GoalBar;
376
+ exports.GoalDock = GoalDock;
377
+ exports.apply = apply;
378
+ exports.inject = inject;
379
+ return module.exports;
380
+ }
381
+ });
382
+
383
+ //# sourceMappingURL=client.js.map
package/lib/index.js ADDED
@@ -0,0 +1,11 @@
1
+ //#region lib/types/index.js
2
+ /**
3
+ * Goal surface plugin, node half. Pure UI plugin: the empty apply exists so
4
+ * the plugin appears in the host cordis.yml / Loader; the browser half
5
+ * ships via exports["./client"], discovered through the package.json
6
+ * dsh.client declaration.
7
+ */
8
+ /** Host plugin body — no host-side behavior for this surface plugin. */
9
+ function apply() {}
10
+ //#endregion
11
+ export { apply };
@@ -0,0 +1,25 @@
1
+ //#region lib/types/invariant.js
2
+ /**
3
+ * Package-owned invariant companion for `@deepseek-ai/dsh-client-ui-goal`.
4
+ * @module @deepseek-ai/dsh-client-ui-goal/invariant
5
+ */
6
+ const PACKAGE_NAME = "@deepseek-ai/dsh-client-ui-goal";
7
+ /** Cordis companion plugin name. */
8
+ const name = "client-ui-goal-invariant";
9
+ /** Service required before the companion can reserve package ownership. */
10
+ const inject = ["invariants"];
11
+ /**
12
+ * No runtime invariant: a single GoalBar dock registration whose disposal is
13
+ * proven by the HMR-safety spec — the plugin owns no store (state arrives on
14
+ * the goal projection), emits no cordis events, and holds no cross-plugin
15
+ * mutable state.
16
+ */
17
+ const install = () => {};
18
+ /**
19
+ * Register this package's invariant companion.
20
+ * @param ctx - Cordis context carrying the invariant service.
21
+ * @returns the installed registration's disposer after setup succeeds.
22
+ */
23
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
24
+ //#endregion
25
+ export { apply, inject, name };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * GoalBar: the goal indicator docked above the message composer (input dock
3
+ * strip). A present goal shows a goal glyph, a phase label, the truncated
4
+ * objective, and icon actions — resume when paused, edit (inline form in the
5
+ * same strip), and clear. Goal creation lives on the `/goal` command, not
6
+ * here: loading (undefined), no goal (null), and complete goals render
7
+ * nothing. Live state arrives as the projected whole snapshot; the verbs are
8
+ * the injected face.
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';
13
+ export interface GoalBarProps extends GoalBarActions {
14
+ /** Current goal snapshot; undefined = capability absent or loading, null = no goal set. */
15
+ goal: GoalSnapshot | null | undefined;
16
+ }
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 + session standard kit + injected verbs + the locale seat. */
19
+ export type GoalDockProps = import('@deepseek-ai/dsh-client-ui-slots').PropsRuntime<'conversation.input.dock'> & GoalBarActions & PropsLocale<'goal'>;
20
+ /** Dock adapter: reads the host-computed 'goal' projection (whole value; absent or null renders nothing). */
21
+ export declare function GoalDock({ useProjection, onEdit, onPause, onResume, onClear, t }: GoalDockProps): import("react").JSX.Element;
22
+ //# sourceMappingURL=GoalBar.d.ts.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Goal surface plugin, browser half: the GoalBar entry in the
3
+ * conversation.input.dock strip. Projection-mode surface — the live goal
4
+ * arrives through `useProjection('goal')` (seeded by the history tail page,
5
+ * updated by session/projection frames), so this plugin owns no store, no
6
+ * refresh chain, and no event listener. The inject face carries only the
7
+ * four mutation verbs through the generated Goal Remote API;
8
+ * their CAS ref reads the session's current projected value at call time.
9
+ * Goal creation stays on the /goal host command.
10
+ */
11
+ import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
12
+ import { type GoalKey } from './locales.ts';
13
+ export { GoalBar, GoalDock } from './GoalBar.tsx';
14
+ export type { GoalActionResult, GoalBarActions } from './slots.ts';
15
+ export type { GoalKey } from './locales.ts';
16
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
17
+ interface LocaleNamespaceMap {
18
+ /** The goal strip's copy. */
19
+ goal: GoalKey;
20
+ }
21
+ }
22
+ /** Required services: slots for the dock entry, sessions for the projected ref, API for Remote mutations, locale for the copy. */
23
+ export declare const inject: string[];
24
+ /**
25
+ * Client plugin body: the GoalBar dock entry with its mutation verbs.
26
+ * @param ctx - client root context.
27
+ */
28
+ export declare function apply(ctx: ClientContext): void;
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,30 @@
1
+ /** `goal` namespace dictionaries. */
2
+ /** Simplified Chinese dictionary (the key-set source of truth). */
3
+ export declare const zh: {
4
+ 'phase.active': string;
5
+ 'phase.paused': string;
6
+ 'phase.blocked': string;
7
+ 'objective.aria': string;
8
+ 'action.save': string;
9
+ 'action.cancel': string;
10
+ 'action.pause': string;
11
+ 'action.resume': string;
12
+ 'action.edit': string;
13
+ 'action.clear': string;
14
+ };
15
+ /** The goal namespace key union. */
16
+ export type GoalKey = keyof typeof zh;
17
+ /** English dictionary, checked complete against the zh key set. */
18
+ export declare const en: {
19
+ 'phase.active': string;
20
+ 'phase.paused': string;
21
+ 'phase.blocked': string;
22
+ 'objective.aria': string;
23
+ 'action.save': string;
24
+ 'action.cancel': string;
25
+ 'action.pause': string;
26
+ 'action.resume': string;
27
+ 'action.edit': string;
28
+ 'action.clear': string;
29
+ };
30
+ //# sourceMappingURL=locales.d.ts.map
@@ -0,0 +1,33 @@
1
+ /**
2
+ * GoalBar's injected face. The target 'conversation.input.dock' slot is
3
+ * declared (children table) and typed by ui-conversation; this package only
4
+ * contributes the entry, so no SlotMap merge lives here. The live goal value
5
+ * is NOT part of this face — it arrives through `useProjection('goal')`
6
+ * (the framework standard kit); inject carries only the mutation verbs
7
+ * (callbacks from inject, live state from useProjection).
8
+ */
9
+ /** Settled outcome of one goal mutation, rendered inline by the strip. */
10
+ export type GoalActionResult = {
11
+ ok: true;
12
+ } | {
13
+ ok: false;
14
+ error: {
15
+ code: string;
16
+ message: string;
17
+ };
18
+ };
19
+ /** Injected business face of the GoalBar dock entry: the mutation verbs (function properties: the strip destructures them freely). */
20
+ export interface GoalBarActions {
21
+ /**
22
+ * Replace the current goal's objective (CAS on the projected ref).
23
+ * @param objective - replacement objective text.
24
+ */
25
+ onEdit: (objective: string) => Promise<GoalActionResult>;
26
+ /** Pause an active goal. */
27
+ onPause: () => Promise<GoalActionResult>;
28
+ /** Resume a paused goal. */
29
+ onResume: () => Promise<GoalActionResult>;
30
+ /** Clear the current goal (tombstone). */
31
+ onClear: () => Promise<GoalActionResult>;
32
+ }
33
+ //# sourceMappingURL=slots.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Goal surface plugin, node half. Pure UI plugin: the empty apply exists so
3
+ * the plugin appears in the host cordis.yml / Loader; the browser half
4
+ * ships via exports["./client"], discovered through the package.json
5
+ * dsh.client declaration.
6
+ */
7
+ /** Host plugin body — no host-side behavior for this surface plugin. */
8
+ export declare function apply(): void;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Package-owned invariant companion for `@deepseek-ai/dsh-client-ui-goal`.
3
+ * @module @deepseek-ai/dsh-client-ui-goal/invariant
4
+ */
5
+ import type { Context } from '@deepseek-ai/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "client-ui-goal-invariant";
8
+ /** Service required before the companion can reserve package ownership. */
9
+ export declare const inject: string[];
10
+ /**
11
+ * Register this package's invariant companion.
12
+ * @param ctx - Cordis context carrying the invariant service.
13
+ * @returns the installed registration's disposer after setup succeeds.
14
+ */
15
+ export declare const apply: (ctx: Context) => Promise<() => void>;
16
+ //# sourceMappingURL=invariant.d.ts.map
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@deepseek-ai/dsh-client-ui-goal",
3
+ "description": "Session goal surface: GoalBar docked above the composer, read from the goal session projection",
4
+ "version": "0.0.1-rc.1",
5
+ "publishConfig": {
6
+ "access": "restricted"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
11
+ "directory": "packages/client/ui-goal"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./client": {
26
+ "types": "./lib/types/client/index.d.ts",
27
+ "default": "./lib/client.js"
28
+ },
29
+ "./src/*": "./src/*",
30
+ "./package.json": "./package.json"
31
+ },
32
+ "dsh": {
33
+ "client": {
34
+ "inject": [
35
+ "@deepseek-ai/dsh-client-runtime",
36
+ "@deepseek-ai/dsh-api-remotes",
37
+ "@deepseek-ai/dsh-client-locale",
38
+ "@deepseek-ai/dsh-client-ui-conversation"
39
+ ],
40
+ "platform": "web"
41
+ }
42
+ },
43
+ "license": "BSD-3-Clause",
44
+ "peerDependencies": {
45
+ "react": "^18.2.0",
46
+ "@deepseek-ai/dsh-client-locale": "^0.0.1-rc.1",
47
+ "@deepseek-ai/dsh-api-remotes": "^0.0.1-rc.1",
48
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.0.1-rc.1",
49
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.0.1-rc.1",
50
+ "@deepseek-ai/dsh-client-runtime": "^0.0.1-rc.1",
51
+ "@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.1",
52
+ "@deepseek-ai/dsh-goal": "^0.0.1-rc.1",
53
+ "@deepseek-ai/cordis": "^4.0.1-rc.1",
54
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.1"
55
+ },
56
+ "devDependencies": {
57
+ "@testing-library/react": "^16.1.0",
58
+ "@types/react": "~18.3.1",
59
+ "react": "^18.2.0",
60
+ "react-dom": "^18.2.0",
61
+ "@deepseek-ai/dsh-client-locale": "^0.0.1-rc.1",
62
+ "@deepseek-ai/dsh-api-remotes": "^0.0.1-rc.1",
63
+ "@deepseek-ai/dsh-client-runtime": "^0.0.1-rc.1",
64
+ "@deepseek-ai/dsh-client-test-runtime": "^0.0.1-rc.1",
65
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.0.1-rc.1",
66
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.0.1-rc.1",
67
+ "@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.1",
68
+ "@deepseek-ai/dsh-goal": "^0.0.1-rc.1",
69
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
70
+ "@deepseek-ai/cordis": "^4.0.1-rc.1"
71
+ },
72
+ "files": [
73
+ "lib/index.js",
74
+ "lib/invariant.js",
75
+ "lib/client.js",
76
+ "lib/types/**/*.d.ts"
77
+ ],
78
+ "scripts": {
79
+ "bundle": "tsdown",
80
+ "watch": "tsdown --watch"
81
+ }
82
+ }