@deepseek-ai/dsh-client-ui-session 0.1.6-alpha.1 → 0.1.7-alpha.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/README.i18n.yaml +2 -2
- package/README.md +3 -1
- package/README.zh.md +3 -1
- package/lib/client.js +231 -72
- package/lib/types/client/index.d.ts +77 -20
- package/lib/types/client/session-provider.d.ts +2 -2
- package/package.json +13 -8
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-session/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: df85bf42b0eb6507662ab4e4a26d1fde6e08872e
|
|
6
|
+
README.zh.md: 5f54c1db7b5daf2d113717d890dc90eeb2be87f9
|
package/README.md
CHANGED
|
@@ -8,7 +8,9 @@ English | [中文](README.zh.md)
|
|
|
8
8
|
|
|
9
9
|
## Summary
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Use this package to expose Session catalog, retain information, and unified UI status through standard Slot hooks. It materializes per-`SessionBinding` hooks and props, while `SessionProvider` can inherit an outer binding or bind an explicit `SessionReference`. It owns process-local pending-interaction and completion-reminder policy without owning Controller transport, history, or references.
|
|
12
|
+
|
|
13
|
+
Running status comes from Host list baselines or status events. Subagent catalog rows and retained subagent fallback rows do not establish running status; main view references still acknowledge completions.
|
|
12
14
|
|
|
13
15
|
## Table of Contents
|
|
14
16
|
|
package/README.zh.md
CHANGED
|
@@ -8,7 +8,9 @@ kind: "package-reference"
|
|
|
8
8
|
|
|
9
9
|
## 概述
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
使用本包可通过标准 Slot 钩子公开 Session catalog、retain 信息与统一 UI 状态。它按 `SessionBinding` 物化钩子和 prop,而 `SessionProvider` 可以继承外围 binding 或绑定显式 `SessionReference`。它拥有进程本地的 pending-interaction 与完成提醒策略,但不拥有 Controller transport、历史或 reference。
|
|
12
|
+
|
|
13
|
+
运行状态来自 Host 列表基线或状态事件。Subagent 目录行和已保留子会话的 fallback 行不会建立运行状态;主视图引用仍会确认完成提醒。
|
|
12
14
|
|
|
13
15
|
## 目录
|
|
14
16
|
|
package/lib/client.js
CHANGED
|
@@ -8,19 +8,79 @@ window.__ModuleLoader__.load({
|
|
|
8
8
|
let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
9
9
|
let _deepseek_ai_dsh_client_ui_slots = require("@deepseek-ai/dsh-client-ui-slots");
|
|
10
10
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
11
|
-
|
|
11
|
+
//#region ../../util/values/src/index.ts
|
|
12
|
+
/**
|
|
13
|
+
* Weak-key lookup with a strongly retained iterable set of associated values.
|
|
14
|
+
*
|
|
15
|
+
* Each value must belong to only one key. The container performs no automatic
|
|
16
|
+
* cleanup; owners delete associations or clear the container at lifecycle end.
|
|
17
|
+
*/
|
|
18
|
+
var WeakMapWithValues = class {
|
|
19
|
+
keys = /* @__PURE__ */ new WeakMap();
|
|
20
|
+
valueSet = /* @__PURE__ */ new Set();
|
|
21
|
+
/** Live strongly retained values in insertion order. */
|
|
22
|
+
values = this.valueSet;
|
|
23
|
+
/**
|
|
24
|
+
* Read the value associated with a key.
|
|
25
|
+
* @param key - weakly held lookup key.
|
|
26
|
+
* @returns the associated value, or absence.
|
|
27
|
+
*/
|
|
28
|
+
get(key) {
|
|
29
|
+
return this.keys.get(key);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Test whether a key has an association.
|
|
33
|
+
* @param key - weakly held lookup key.
|
|
34
|
+
* @returns whether the key is present.
|
|
35
|
+
*/
|
|
36
|
+
has(key) {
|
|
37
|
+
return this.keys.has(key);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Associate one key with one caller-unique value.
|
|
41
|
+
* @param key - weakly held lookup key.
|
|
42
|
+
* @param value - strongly retained value that belongs to no other key.
|
|
43
|
+
* @returns this container.
|
|
44
|
+
*/
|
|
45
|
+
set(key, value) {
|
|
46
|
+
if (this.keys.has(key)) {
|
|
47
|
+
const previous = this.keys.get(key);
|
|
48
|
+
if (previous === value) return this;
|
|
49
|
+
this.valueSet.delete(previous);
|
|
50
|
+
}
|
|
51
|
+
this.keys.set(key, value);
|
|
52
|
+
this.valueSet.add(value);
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Remove one association and its strongly retained value.
|
|
57
|
+
* @param key - weakly held lookup key.
|
|
58
|
+
* @returns whether an association was removed.
|
|
59
|
+
*/
|
|
60
|
+
delete(key) {
|
|
61
|
+
if (!this.keys.has(key)) return false;
|
|
62
|
+
const value = this.keys.get(key);
|
|
63
|
+
const deleted = this.keys.delete(key);
|
|
64
|
+
this.valueSet.delete(value);
|
|
65
|
+
return deleted;
|
|
66
|
+
}
|
|
67
|
+
/** Remove every association and strongly retained value. */
|
|
68
|
+
clear() {
|
|
69
|
+
this.keys = /* @__PURE__ */ new WeakMap();
|
|
70
|
+
this.valueSet.clear();
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
//#endregion
|
|
12
74
|
//#region lib/types/client/session-provider.js
|
|
13
|
-
/** Session-owned rendering semantics for the standard SessionProvider seat. */
|
|
14
75
|
/**
|
|
15
76
|
* Render the selected Session body or its empty branch.
|
|
16
77
|
* @param binding - current Session scope binding.
|
|
17
78
|
* @param props - standard Session area render props.
|
|
18
|
-
* @returns the selected Session subtree
|
|
79
|
+
* @returns the selected Session subtree.
|
|
19
80
|
*/
|
|
20
81
|
function renderSessionArea(binding, { empty, children }) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return (0, react_jsx_runtime.jsx)(react.Fragment, { children }, sessionId);
|
|
82
|
+
if (binding.key === void 0) return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: empty?.() ?? null });
|
|
83
|
+
return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children });
|
|
24
84
|
}
|
|
25
85
|
//#endregion
|
|
26
86
|
//#region lib/types/client/index.js
|
|
@@ -72,20 +132,25 @@ window.__ModuleLoader__.load({
|
|
|
72
132
|
var UiSession = class extends _deepseek_ai_cordis.Service {
|
|
73
133
|
sessions;
|
|
74
134
|
descriptors = [BUILTIN_SOURCE];
|
|
75
|
-
bindings =
|
|
135
|
+
bindings = new WeakMapWithValues();
|
|
76
136
|
absent;
|
|
77
|
-
|
|
78
|
-
currentListeners = /* @__PURE__ */ new Set();
|
|
137
|
+
current;
|
|
79
138
|
pendingDomains = [];
|
|
80
139
|
pendingSnapshot = /* @__PURE__ */ new Map();
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
140
|
+
running = /* @__PURE__ */ new Map();
|
|
141
|
+
completionUnread = /* @__PURE__ */ new Set();
|
|
142
|
+
statusSnapshot = /* @__PURE__ */ new Map();
|
|
143
|
+
statusListeners = /* @__PURE__ */ new Set();
|
|
144
|
+
mainRetainId;
|
|
145
|
+
disposeMainRetain = () => {};
|
|
146
|
+
active = true;
|
|
147
|
+
/** Root source combining running, pending-interaction, and completion-reminder facts. */
|
|
148
|
+
sessionStatus = {
|
|
149
|
+
getSnapshot: () => this.statusSnapshot,
|
|
85
150
|
subscribe: (listener) => {
|
|
86
|
-
this.
|
|
151
|
+
this.statusListeners.add(listener);
|
|
87
152
|
return () => {
|
|
88
|
-
this.
|
|
153
|
+
this.statusListeners.delete(listener);
|
|
89
154
|
};
|
|
90
155
|
}
|
|
91
156
|
};
|
|
@@ -98,34 +163,51 @@ window.__ModuleLoader__.load({
|
|
|
98
163
|
constructor(ctx, sessions) {
|
|
99
164
|
super(ctx, "uiSession");
|
|
100
165
|
this.sessions = sessions;
|
|
101
|
-
this.absent = this.materializeAbsent();
|
|
102
|
-
this.
|
|
166
|
+
this.absent = createBindingSource(this.materializeAbsent());
|
|
167
|
+
this.current = createBindingSource(this.absent.value);
|
|
103
168
|
this.adapter = {
|
|
104
|
-
current:
|
|
105
|
-
|
|
106
|
-
subscribe: (listener) => {
|
|
107
|
-
this.currentListeners.add(listener);
|
|
108
|
-
return () => {
|
|
109
|
-
this.currentListeners.delete(listener);
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
resolve: (key) => this.resolve(key),
|
|
169
|
+
current: this.current,
|
|
170
|
+
bindingSource: (target) => this.bindingSource(target),
|
|
114
171
|
renderArea: renderSessionArea
|
|
115
172
|
};
|
|
116
173
|
ctx.effect(() => {
|
|
117
174
|
const disposeList = sessions.list.subscribe(() => {
|
|
118
|
-
this.
|
|
175
|
+
this.publishMain();
|
|
119
176
|
});
|
|
177
|
+
const disposeStatus = sessions.list.subscribe(() => {
|
|
178
|
+
this.reconcileStatus();
|
|
179
|
+
});
|
|
180
|
+
const disposeRemoteStatus = ctx.remote.$on("api-session/status", (sessionId, running) => {
|
|
181
|
+
this.observeRunning(sessionId, running);
|
|
182
|
+
});
|
|
183
|
+
this.publishMain();
|
|
184
|
+
this.reconcileStatus();
|
|
120
185
|
return () => {
|
|
186
|
+
this.active = false;
|
|
121
187
|
disposeList();
|
|
122
|
-
|
|
188
|
+
disposeStatus();
|
|
189
|
+
disposeRemoteStatus();
|
|
190
|
+
this.disposeMainRetain();
|
|
191
|
+
const records = [...this.bindings.values];
|
|
123
192
|
this.bindings.clear();
|
|
124
193
|
for (const record of records) record.release();
|
|
125
194
|
};
|
|
126
195
|
}, "ui-session: Session binding projection");
|
|
127
196
|
}
|
|
128
197
|
/**
|
|
198
|
+
* Resolve a stable renderer source for an owned Session reference or explicit absence.
|
|
199
|
+
* @param reference - active reference supplied by the Provider owner, or absence.
|
|
200
|
+
* @returns the binding source, which falls back to the absent projection when its generation ends.
|
|
201
|
+
* @throws when the reference does not belong to the active Controller generation.
|
|
202
|
+
*/
|
|
203
|
+
bindingSource(reference) {
|
|
204
|
+
if (!this.active) return this.absent;
|
|
205
|
+
if (reference === void 0) return this.absent;
|
|
206
|
+
const owner = reference.binding;
|
|
207
|
+
if (this.sessions.binding(reference.sessionId) !== owner) throw new Error("ui-session: Session reference is not active in this Controller");
|
|
208
|
+
return this.sourceFor(owner);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
129
211
|
* Register one Session-scoped standard-source contribution.
|
|
130
212
|
* @param descriptor - static member roster and per-binding resolver.
|
|
131
213
|
* @returns disposer owned by the caller's Cordis fiber.
|
|
@@ -177,38 +259,42 @@ window.__ModuleLoader__.load({
|
|
|
177
259
|
}
|
|
178
260
|
rebuildBindings() {
|
|
179
261
|
const absent = this.materializeAbsent();
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
this.
|
|
189
|
-
this.bindings = bindings;
|
|
190
|
-
for (const record of previous.values()) record.release();
|
|
191
|
-
this.publishCurrent();
|
|
262
|
+
const updates = [...this.bindings.values].map((record) => ({
|
|
263
|
+
source: record.source,
|
|
264
|
+
value: this.materialize(record.owner)
|
|
265
|
+
}));
|
|
266
|
+
this.absent.value = absent;
|
|
267
|
+
for (const { source, value } of updates) source.value = value;
|
|
268
|
+
(0, _deepseek_ai_dsh_client_store.notifySubscribers)(this.absent.listeners, "[ui-session] absent binding");
|
|
269
|
+
for (const { source } of updates) (0, _deepseek_ai_dsh_client_store.notifySubscribers)(source.listeners, "[ui-session] Session binding");
|
|
270
|
+
this.publishMain();
|
|
192
271
|
}
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
if (
|
|
196
|
-
const cached = this.bindings.get(key);
|
|
197
|
-
if (cached?.owner === owner) return cached.value;
|
|
272
|
+
sourceFor(owner) {
|
|
273
|
+
const cached = this.bindings.get(owner);
|
|
274
|
+
if (cached !== void 0) return cached.source;
|
|
198
275
|
const record = this.createMaterializedBinding(owner);
|
|
199
|
-
this.bindings.set(
|
|
200
|
-
|
|
201
|
-
return record.value;
|
|
276
|
+
this.bindings.set(owner, record);
|
|
277
|
+
return record.source;
|
|
202
278
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
279
|
+
publishMain() {
|
|
280
|
+
if (!this.active) return;
|
|
281
|
+
const byId = this.sessions.list.getSnapshot().byId;
|
|
282
|
+
const currentId = this.current.value.key;
|
|
283
|
+
const nextId = currentId !== void 0 && (this.sessions.retainInfo(currentId).getSnapshot().retainedBy.mainView ?? 0) > 0 ? currentId : Object.values(byId).find((candidate) => (candidate.retainedBy.mainView ?? 0) > 0)?.id;
|
|
284
|
+
this.watchMainRetention(nextId);
|
|
285
|
+
const owner = nextId === void 0 ? void 0 : this.sessions.binding(nextId);
|
|
286
|
+
const value = owner === void 0 ? this.absent.value : this.sourceFor(owner).value;
|
|
287
|
+
if (this.current.value === value) return;
|
|
288
|
+
this.current.value = value;
|
|
289
|
+
(0, _deepseek_ai_dsh_client_store.notifySubscribers)(this.current.listeners, "[ui-session] main binding");
|
|
206
290
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
this.
|
|
211
|
-
|
|
291
|
+
watchMainRetention(sessionId) {
|
|
292
|
+
if (sessionId === this.mainRetainId) return;
|
|
293
|
+
this.disposeMainRetain();
|
|
294
|
+
this.mainRetainId = sessionId;
|
|
295
|
+
this.disposeMainRetain = sessionId === void 0 ? () => {} : this.sessions.retainInfo(sessionId).subscribe(() => {
|
|
296
|
+
this.publishMain();
|
|
297
|
+
});
|
|
212
298
|
}
|
|
213
299
|
publishPendingInteractions() {
|
|
214
300
|
const next = /* @__PURE__ */ new Map();
|
|
@@ -223,20 +309,66 @@ window.__ModuleLoader__.load({
|
|
|
223
309
|
const projected = new Map([...next].map(([sessionId, value]) => [sessionId, value.interaction]));
|
|
224
310
|
if (samePendingInteractions(this.pendingSnapshot, projected)) return;
|
|
225
311
|
this.pendingSnapshot = projected;
|
|
226
|
-
|
|
312
|
+
this.publishStatus();
|
|
313
|
+
}
|
|
314
|
+
observeRunning(sessionId, running) {
|
|
315
|
+
const previous = this.running.get(sessionId);
|
|
316
|
+
const beforeBaseline = this.sessions.list.getSnapshot().phase === "pending";
|
|
317
|
+
this.running.set(sessionId, running);
|
|
318
|
+
if (running) this.completionUnread.delete(sessionId);
|
|
319
|
+
else if ((previous === true || previous === void 0 && beforeBaseline) && !this.isMain(sessionId)) this.completionUnread.add(sessionId);
|
|
320
|
+
this.publishStatus();
|
|
321
|
+
}
|
|
322
|
+
reconcileStatus() {
|
|
323
|
+
const list = this.sessions.list.getSnapshot();
|
|
324
|
+
const present = new Set(Object.keys(list.byId));
|
|
325
|
+
for (const id of list.ids) {
|
|
326
|
+
const row = list.byId[id];
|
|
327
|
+
const previous = this.running.get(id);
|
|
328
|
+
if (previous === void 0) this.running.set(id, row.running);
|
|
329
|
+
else if (previous !== row.running) this.observeRunning(id, row.running);
|
|
330
|
+
}
|
|
331
|
+
for (const id of present) if (this.isMain(id)) this.completionUnread.delete(id);
|
|
332
|
+
if (list.phase === "ready") for (const id of this.running.keys()) {
|
|
333
|
+
if (present.has(id)) continue;
|
|
334
|
+
this.running.delete(id);
|
|
335
|
+
this.completionUnread.delete(id);
|
|
336
|
+
}
|
|
337
|
+
this.publishStatus();
|
|
338
|
+
}
|
|
339
|
+
isMain(sessionId) {
|
|
340
|
+
return (this.sessions.list.getSnapshot().byId[sessionId]?.retainedBy.mainView ?? 0) > 0;
|
|
341
|
+
}
|
|
342
|
+
publishStatus() {
|
|
343
|
+
const ids = new Set([
|
|
344
|
+
...Object.keys(this.sessions.list.getSnapshot().byId),
|
|
345
|
+
...this.running.keys(),
|
|
346
|
+
...this.pendingSnapshot.keys(),
|
|
347
|
+
...this.completionUnread
|
|
348
|
+
]);
|
|
349
|
+
const next = /* @__PURE__ */ new Map();
|
|
350
|
+
for (const id of ids) next.set(id, {
|
|
351
|
+
running: this.running.get(id),
|
|
352
|
+
pendingInteraction: this.pendingSnapshot.get(id),
|
|
353
|
+
completionUnread: this.completionUnread.has(id)
|
|
354
|
+
});
|
|
355
|
+
if (sameSessionStatus(this.statusSnapshot, next)) return;
|
|
356
|
+
this.statusSnapshot = next;
|
|
357
|
+
(0, _deepseek_ai_dsh_client_store.notifySubscribers)(this.statusListeners, "[ui-session] Session status");
|
|
227
358
|
}
|
|
228
359
|
createMaterializedBinding(owner) {
|
|
229
360
|
const value = this.materialize(owner);
|
|
361
|
+
this.ctx.slots.bindStoreScope(value);
|
|
362
|
+
const source = createBindingSource(value);
|
|
230
363
|
const releaseEffect = owner.ctx.effect(() => () => {
|
|
231
|
-
if (this.bindings.get(owner
|
|
232
|
-
this.
|
|
233
|
-
|
|
234
|
-
this.
|
|
235
|
-
(0, _deepseek_ai_dsh_client_store.notifySubscribers)(this.currentListeners, "[ui-session] current binding");
|
|
364
|
+
if (this.bindings.get(owner) === record) this.bindings.delete(owner);
|
|
365
|
+
source.value = this.absent.value;
|
|
366
|
+
(0, _deepseek_ai_dsh_client_store.notifySubscribers)(source.listeners, "[ui-session] Session binding");
|
|
367
|
+
this.publishMain();
|
|
236
368
|
}, `ui-session: binding ${owner.sessionId}`);
|
|
237
369
|
const record = {
|
|
238
370
|
owner,
|
|
239
|
-
|
|
371
|
+
source,
|
|
240
372
|
release: () => {
|
|
241
373
|
releaseEffect();
|
|
242
374
|
}
|
|
@@ -255,15 +387,13 @@ window.__ModuleLoader__.load({
|
|
|
255
387
|
copyDeclared("keyed hook", keyedHooks, descriptor.keyedHooks, contribution.keyedHooks, finalProps);
|
|
256
388
|
copyDeclared("prop", props, descriptor.props, contribution.props, finalProps);
|
|
257
389
|
}
|
|
258
|
-
|
|
390
|
+
return {
|
|
259
391
|
key: binding.sessionId,
|
|
260
392
|
ctx: binding.ctx,
|
|
261
393
|
hooks,
|
|
262
394
|
keyedHooks,
|
|
263
395
|
props
|
|
264
396
|
};
|
|
265
|
-
this.ctx.slots.bindStoreScope(value);
|
|
266
|
-
return value;
|
|
267
397
|
}
|
|
268
398
|
materializeAbsent() {
|
|
269
399
|
const hooks = {};
|
|
@@ -283,6 +413,20 @@ window.__ModuleLoader__.load({
|
|
|
283
413
|
};
|
|
284
414
|
}
|
|
285
415
|
};
|
|
416
|
+
function createBindingSource(value) {
|
|
417
|
+
const source = {
|
|
418
|
+
value,
|
|
419
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
420
|
+
getSnapshot: () => source.value,
|
|
421
|
+
subscribe: (listener) => {
|
|
422
|
+
source.listeners.add(listener);
|
|
423
|
+
return () => {
|
|
424
|
+
source.listeners.delete(listener);
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
return source;
|
|
429
|
+
}
|
|
286
430
|
function validateContribution(descriptor, contribution) {
|
|
287
431
|
rejectUndeclared("hook", descriptor.hooks, contribution.hooks);
|
|
288
432
|
rejectUndeclared("keyed hook", descriptor.keyedHooks, contribution.keyedHooks);
|
|
@@ -311,19 +455,34 @@ window.__ModuleLoader__.load({
|
|
|
311
455
|
finalProps.add(propName);
|
|
312
456
|
}
|
|
313
457
|
/** Required Controller and renderer services. */
|
|
314
|
-
const inject = [
|
|
458
|
+
const inject = [
|
|
459
|
+
"sessions",
|
|
460
|
+
"slots",
|
|
461
|
+
"remote"
|
|
462
|
+
];
|
|
315
463
|
/**
|
|
316
464
|
* Install the Session root source and scoped adapter.
|
|
317
465
|
* @param ctx - Client Cordis context.
|
|
318
466
|
*/
|
|
319
467
|
function apply(ctx) {
|
|
320
468
|
const service = new UiSession(ctx, ctx.sessions);
|
|
321
|
-
ctx.slots.provideRoot({
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
469
|
+
ctx.slots.provideRoot({
|
|
470
|
+
hooks: {
|
|
471
|
+
sessions: ctx.sessions.list,
|
|
472
|
+
sessionStatus: service.sessionStatus
|
|
473
|
+
},
|
|
474
|
+
keyedHooks: { sessionRetainInfo: (key) => ctx.sessions.retainInfo(key) }
|
|
475
|
+
});
|
|
325
476
|
ctx.slots.installScope("session", service.adapter);
|
|
326
477
|
}
|
|
478
|
+
function sameSessionStatus(left, right) {
|
|
479
|
+
if (left.size !== right.size) return false;
|
|
480
|
+
for (const [id, status] of left) {
|
|
481
|
+
const candidate = right.get(id);
|
|
482
|
+
if (candidate === void 0 || candidate.running !== status.running || candidate.pendingInteraction !== status.pendingInteraction || candidate.completionUnread !== status.completionUnread) return false;
|
|
483
|
+
}
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
327
486
|
function samePendingInteractions(left, right) {
|
|
328
487
|
if (left.size !== right.size) return false;
|
|
329
488
|
for (const [sessionId, interaction] of left) if (right.get(sessionId) !== interaction) return false;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** Session Controller adapter for React selector hooks and Slot scope data. */
|
|
2
2
|
import { Service, type Context } from '@deepseek-ai/cordis';
|
|
3
|
-
import type { ISessions, SessionBinding, SessionListState, SessionSnapshot, UseProjection } from '@deepseek-ai/dsh-api-session-controller/client';
|
|
3
|
+
import type { ISessions, SessionBinding, SessionListState, SessionReference, SessionRetainInfo, SessionSnapshot, UseProjection } from '@deepseek-ai/dsh-api-session-controller/client';
|
|
4
4
|
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
5
|
-
import type { HostObservable, KeyedStandardSource, MaybeSnapshotSelectorHook, SlotScopeAdapter, SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots';
|
|
5
|
+
import type { HostObservable, KeyedStandardSource, MaybeSnapshotSelectorHook, SlotScopeAdapter, SnapshotSelectorHook, StandardSourceBinding } from '@deepseek-ai/dsh-client-ui-slots';
|
|
6
6
|
/** Selector hook over the Session Controller list and current selection. */
|
|
7
7
|
export type UseSessions = SnapshotSelectorHook<SessionListState>;
|
|
8
8
|
/** Selector hook over one Session's lifecycle and control state. */
|
|
@@ -18,25 +18,61 @@ export interface SessionPendingInteractionBase {
|
|
|
18
18
|
/** Session whose UI can answer this interaction. */
|
|
19
19
|
readonly sessionId: SessionId;
|
|
20
20
|
}
|
|
21
|
-
/** Declaration-merged
|
|
21
|
+
/** Declaration-merged map of domain keys to their pending-interaction values. */
|
|
22
22
|
export interface SessionPendingInteractionMap {
|
|
23
23
|
}
|
|
24
|
-
/**
|
|
24
|
+
/** Union of every pending-interaction value contributed by the assembled Client. */
|
|
25
25
|
export type SessionPendingInteraction = [
|
|
26
26
|
keyof SessionPendingInteractionMap
|
|
27
27
|
] extends [never] ? SessionPendingInteractionBase : SessionPendingInteractionMap[keyof SessionPendingInteractionMap];
|
|
28
|
-
/**
|
|
29
|
-
export
|
|
30
|
-
/**
|
|
31
|
-
|
|
28
|
+
/** Independent UI status facts for one Session identity. */
|
|
29
|
+
export interface SessionStatus {
|
|
30
|
+
/** Latest known running state; absent until a baseline or event establishes it. */
|
|
31
|
+
readonly running: boolean | undefined;
|
|
32
|
+
/** Highest-precedence domain request currently awaiting user interaction. */
|
|
33
|
+
readonly pendingInteraction: SessionPendingInteraction | undefined;
|
|
34
|
+
/** Whether an observed stop outside the main view still needs acknowledgement. */
|
|
35
|
+
readonly completionUnread: boolean;
|
|
36
|
+
}
|
|
37
|
+
/** Current UI status indexed by Session identity. */
|
|
38
|
+
export type SessionStatusSnapshot = ReadonlyMap<SessionId, SessionStatus>;
|
|
39
|
+
/** Selector hook over the unified Session UI status snapshot. */
|
|
40
|
+
export type UseSessionStatus = SnapshotSelectorHook<SessionStatusSnapshot>;
|
|
41
|
+
/** Selector hook for explicit or surrounding-Provider Session reference counts. */
|
|
42
|
+
export interface UseSessionRetainInfo {
|
|
43
|
+
/**
|
|
44
|
+
* Read the complete retain information for an explicit Session identity.
|
|
45
|
+
* @param sessionId - Session identity to inspect without retaining it.
|
|
46
|
+
* @returns current local reference counts, or absence while the source is unavailable.
|
|
47
|
+
*/
|
|
48
|
+
(sessionId: SessionId): SessionRetainInfo | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Select from the retain information for an explicit Session identity.
|
|
51
|
+
* @param sessionId - Session identity to inspect without retaining it.
|
|
52
|
+
* @param selector - projection over the current value.
|
|
53
|
+
* @param equal - optional selected-value equality.
|
|
54
|
+
* @returns selected value.
|
|
55
|
+
*/
|
|
56
|
+
<Selected>(sessionId: SessionId, selector: (value: SessionRetainInfo | undefined) => Selected, equal?: (left: Selected, right: Selected) => boolean): Selected;
|
|
57
|
+
/**
|
|
58
|
+
* Select from the surrounding Provider's Session retain information.
|
|
59
|
+
* @param selector - projection receiving absence outside a Session binding.
|
|
60
|
+
* @param equal - optional selected-value equality.
|
|
61
|
+
* @returns selected value.
|
|
62
|
+
*/
|
|
63
|
+
<Selected>(selector: (value: SessionRetainInfo | undefined) => Selected, equal?: (left: Selected, right: Selected) => boolean): Selected;
|
|
64
|
+
}
|
|
32
65
|
/** Publish one pending interaction and define how plugin teardown delegates it. */
|
|
33
66
|
export type PendingInteractionPublisher<T extends SessionPendingInteractionBase> = (interaction: T, delegate: () => Promise<void>) => () => void;
|
|
34
67
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
68
|
+
interface SlotScopeTargetMap {
|
|
69
|
+
session: SessionReference;
|
|
70
|
+
}
|
|
35
71
|
interface GlobalStandardProps {
|
|
36
72
|
/** Session list and current selection. */
|
|
37
73
|
useSessions: UseSessions;
|
|
38
|
-
|
|
39
|
-
|
|
74
|
+
useSessionStatus: UseSessionStatus;
|
|
75
|
+
useSessionRetainInfo: UseSessionRetainInfo;
|
|
40
76
|
}
|
|
41
77
|
interface SessionStandardProps {
|
|
42
78
|
/** Current Session lifecycle and control state. */
|
|
@@ -55,6 +91,11 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
55
91
|
useProjection: UseProjection;
|
|
56
92
|
}
|
|
57
93
|
}
|
|
94
|
+
declare module '@deepseek-ai/dsh-api-session-controller/client' {
|
|
95
|
+
interface SessionReferenceSourceMap {
|
|
96
|
+
mainView: unknown;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
58
99
|
declare module '@deepseek-ai/cordis' {
|
|
59
100
|
interface Context {
|
|
60
101
|
/** Session Controller adapter and session-scoped source registry. */
|
|
@@ -85,15 +126,20 @@ export interface SessionSourceDescriptor<Hooks extends SessionSourceRoster = Ses
|
|
|
85
126
|
export declare class UiSession extends Service {
|
|
86
127
|
private readonly sessions;
|
|
87
128
|
private readonly descriptors;
|
|
88
|
-
private bindings;
|
|
89
|
-
private absent;
|
|
90
|
-
private
|
|
91
|
-
private readonly currentListeners;
|
|
129
|
+
private readonly bindings;
|
|
130
|
+
private readonly absent;
|
|
131
|
+
private readonly current;
|
|
92
132
|
private readonly pendingDomains;
|
|
93
133
|
private pendingSnapshot;
|
|
94
|
-
private readonly
|
|
95
|
-
|
|
96
|
-
|
|
134
|
+
private readonly running;
|
|
135
|
+
private readonly completionUnread;
|
|
136
|
+
private statusSnapshot;
|
|
137
|
+
private readonly statusListeners;
|
|
138
|
+
private mainRetainId;
|
|
139
|
+
private disposeMainRetain;
|
|
140
|
+
private active;
|
|
141
|
+
/** Root source combining running, pending-interaction, and completion-reminder facts. */
|
|
142
|
+
readonly sessionStatus: HostObservable<SessionStatusSnapshot>;
|
|
97
143
|
/** Renderer-facing adapter for `session` and `session-maybe` scopes. */
|
|
98
144
|
readonly adapter: SlotScopeAdapter;
|
|
99
145
|
/**
|
|
@@ -101,6 +147,13 @@ export declare class UiSession extends Service {
|
|
|
101
147
|
* @param sessions - Controller-owned Session object layer.
|
|
102
148
|
*/
|
|
103
149
|
constructor(ctx: Context, sessions: ISessions);
|
|
150
|
+
/**
|
|
151
|
+
* Resolve a stable renderer source for an owned Session reference or explicit absence.
|
|
152
|
+
* @param reference - active reference supplied by the Provider owner, or absence.
|
|
153
|
+
* @returns the binding source, which falls back to the absent projection when its generation ends.
|
|
154
|
+
* @throws when the reference does not belong to the active Controller generation.
|
|
155
|
+
*/
|
|
156
|
+
bindingSource(reference: SessionReference | undefined): HostObservable<StandardSourceBinding>;
|
|
104
157
|
/**
|
|
105
158
|
* Register one Session-scoped standard-source contribution.
|
|
106
159
|
* @param descriptor - static member roster and per-binding resolver.
|
|
@@ -116,10 +169,14 @@ export declare class UiSession extends Service {
|
|
|
116
169
|
*/
|
|
117
170
|
registerPendingInteraction<T extends SessionPendingInteractionBase>(precedence: (interaction: T) => number): PendingInteractionPublisher<T>;
|
|
118
171
|
private rebuildBindings;
|
|
119
|
-
private
|
|
120
|
-
private
|
|
121
|
-
private
|
|
172
|
+
private sourceFor;
|
|
173
|
+
private publishMain;
|
|
174
|
+
private watchMainRetention;
|
|
122
175
|
private publishPendingInteractions;
|
|
176
|
+
private observeRunning;
|
|
177
|
+
private reconcileStatus;
|
|
178
|
+
private isMain;
|
|
179
|
+
private publishStatus;
|
|
123
180
|
private createMaterializedBinding;
|
|
124
181
|
private materialize;
|
|
125
182
|
private materializeAbsent;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** Session-owned rendering semantics for the standard SessionProvider seat. */
|
|
2
|
-
import {
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
3
|
import type { SessionAreaProps, StandardSourceBinding } from '@deepseek-ai/dsh-client-ui-slots';
|
|
4
4
|
/**
|
|
5
5
|
* Render the selected Session body or its empty branch.
|
|
6
6
|
* @param binding - current Session scope binding.
|
|
7
7
|
* @param props - standard Session area render props.
|
|
8
|
-
* @returns the selected Session subtree
|
|
8
|
+
* @returns the selected Session subtree.
|
|
9
9
|
*/
|
|
10
10
|
export declare function renderSessionArea(binding: StandardSourceBinding, { empty, children }: SessionAreaProps): ReactNode;
|
|
11
11
|
//# sourceMappingURL=session-provider.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-ui-session",
|
|
3
3
|
"description": "Session Controller adapter for React and session-scoped slots",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.7-alpha.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"dsh": {
|
|
29
29
|
"client": {
|
|
30
30
|
"inject": [
|
|
31
|
+
"@deepseek-ai/dsh-api-remotes",
|
|
31
32
|
"@deepseek-ai/dsh-api-session-controller",
|
|
32
33
|
"@deepseek-ai/dsh-client-ui-renderer"
|
|
33
34
|
],
|
|
@@ -36,17 +37,21 @@
|
|
|
36
37
|
},
|
|
37
38
|
"license": "MIT",
|
|
38
39
|
"peerDependencies": {
|
|
39
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
40
|
+
"@deepseek-ai/cordis": "^4.0.3"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@types/react": "~18.3.1",
|
|
43
44
|
"react": "^18.2.0",
|
|
44
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
45
|
-
"@deepseek-ai/dsh-api-
|
|
46
|
-
"@deepseek-ai/dsh-
|
|
47
|
-
"@deepseek-ai/dsh-client-
|
|
48
|
-
"@deepseek-ai/dsh-client-
|
|
49
|
-
"@deepseek-ai/dsh-
|
|
45
|
+
"@deepseek-ai/cordis": "^4.0.3",
|
|
46
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.7-alpha.1",
|
|
47
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.1.7-alpha.1",
|
|
48
|
+
"@deepseek-ai/dsh-client-store": "^0.1.7-alpha.1",
|
|
49
|
+
"@deepseek-ai/dsh-client-test-runtime": "^0.1.7-alpha.1",
|
|
50
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-alpha.1",
|
|
51
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.7-alpha.1",
|
|
52
|
+
"@deepseek-ai/dsh-remote-mock": "^0.1.7-alpha.1",
|
|
53
|
+
"@deepseek-ai/dsh-session": "^0.1.7-alpha.1",
|
|
54
|
+
"@deepseek-ai/dsh-util-values": "^0.1.7-alpha.1"
|
|
50
55
|
},
|
|
51
56
|
"files": [
|
|
52
57
|
"lib/index.js",
|