@deepseek-ai/dsh-api-session-controller 0.1.6-alpha.2 → 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 +31 -8
- package/README.zh.md +31 -8
- package/lib/client.js +603 -515
- package/lib/index.js +299 -137
- package/lib/typert.host.js +373 -183
- package/lib/typert.remote-client.d.ts +10 -5
- package/lib/typert.remote-client.js +216 -124
- package/lib/types/archived-session-gate.d.ts +30 -0
- package/lib/types/archived-session-gate.js +52 -0
- package/lib/types/client/contract/session.d.ts +2 -0
- package/lib/types/client/contract/sessions.d.ts +11 -16
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/sessions/lineage.d.ts +4 -1
- package/lib/types/client/sessions/lineage.js +2 -1
- package/lib/types/client/sessions/manager.d.ts +66 -59
- package/lib/types/client/sessions/manager.js +249 -277
- package/lib/types/client/sessions/projection-store.d.ts +32 -13
- package/lib/types/client/sessions/projection-store.js +45 -13
- package/lib/types/client/sessions/remotes.d.ts +1 -2
- package/lib/types/client/sessions/service.d.ts +23 -35
- package/lib/types/client/sessions/service.js +35 -47
- package/lib/types/client/sessions/session.d.ts +18 -16
- package/lib/types/client/sessions/session.js +112 -50
- package/lib/types/commands.d.ts +4 -2
- package/lib/types/commands.js +72 -36
- package/lib/types/control.d.ts +2 -4
- package/lib/types/control.js +2 -44
- package/lib/types/history.js +1 -1
- package/lib/types/index.d.ts +32 -5
- package/lib/types/index.js +117 -13
- package/lib/types/list.js +24 -15
- package/lib/types/skill-catalog.js +60 -50
- package/lib/types/types.d.ts +37 -23
- package/lib/types/types.js +0 -1
- package/package.json +71 -76
package/lib/client.js
CHANGED
|
@@ -149,6 +149,7 @@ window.__ModuleLoader__.load({
|
|
|
149
149
|
"compaction/start",
|
|
150
150
|
"compaction/summary",
|
|
151
151
|
"deliverables/presented",
|
|
152
|
+
"developer/message",
|
|
152
153
|
"feedback/message-delete",
|
|
153
154
|
"feedback/message-put",
|
|
154
155
|
"feedback/record",
|
|
@@ -199,6 +200,7 @@ window.__ModuleLoader__.load({
|
|
|
199
200
|
/** Runtime counterpart of the message-producing event union. */
|
|
200
201
|
const SURFACE_EVENT_TYPES = new Set([
|
|
201
202
|
"system/message",
|
|
203
|
+
"developer/message",
|
|
202
204
|
"user/message",
|
|
203
205
|
"assistant/message",
|
|
204
206
|
"tool/result"
|
|
@@ -206,7 +208,7 @@ window.__ModuleLoader__.load({
|
|
|
206
208
|
/**
|
|
207
209
|
* Whether an event type can join the model-visible surface.
|
|
208
210
|
* @param type - event type to test.
|
|
209
|
-
* @returns true for one of the
|
|
211
|
+
* @returns true for one of the message-producing event types.
|
|
210
212
|
*/
|
|
211
213
|
function isSurfaceEligibleType(type) {
|
|
212
214
|
return SURFACE_EVENT_TYPES.has(type);
|
|
@@ -216,14 +218,33 @@ window.__ModuleLoader__.load({
|
|
|
216
218
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
217
219
|
}
|
|
218
220
|
/**
|
|
219
|
-
* Reject noncanonical request-header fields and contradictory tool failure metadata.
|
|
221
|
+
* Reject noncanonical request-header fields, developer roles/content, and contradictory tool failure metadata.
|
|
220
222
|
* This does not validate complete event payloads or embedded provider streams.
|
|
221
223
|
* @param event - event whose locally related payload fields are inspected.
|
|
222
224
|
* @param subject - event location to include in validation errors.
|
|
223
|
-
* @throws when request
|
|
225
|
+
* @throws when request-header fields, developer roles/content, or tool failure metadata are invalid.
|
|
224
226
|
*/
|
|
225
227
|
function validateSessionEventData(event, subject) {
|
|
226
228
|
const data = event.data;
|
|
229
|
+
if (SURFACE_EVENT_TYPES.has(event.type) && isRecord(data)) {
|
|
230
|
+
const message = event.type === "user/message" ? data : data["message"];
|
|
231
|
+
if (isRecord(message)) {
|
|
232
|
+
if (event.type === "developer/message" !== (message["role"] === "developer")) throw new Error(`${subject} developer/message and developer role must occur together`);
|
|
233
|
+
if (message["role"] !== "developer" && Array.isArray(message["content"]) && message["content"].some((block) => isRecord(block) && (block["type"] === "tool-addition" || block["type"] === "tool-removal"))) throw new Error(`${subject} tool-change blocks require developer role`);
|
|
234
|
+
if (event.type === "developer/message" && Array.isArray(message["content"])) {
|
|
235
|
+
let hasAdditions = false;
|
|
236
|
+
for (const block of message["content"]) {
|
|
237
|
+
if (!isRecord(block) || block["type"] !== "tool-addition" && block["type"] !== "tool-removal") continue;
|
|
238
|
+
if (typeof block["toolName"] !== "string" || block["toolName"].length === 0) throw new Error(`${subject} ${block["type"]} requires a nonempty toolName`);
|
|
239
|
+
if (block["type"] === "tool-addition") {
|
|
240
|
+
hasAdditions = true;
|
|
241
|
+
if (Object.hasOwn(block, "tool")) throw new Error(`${subject} tool-addition must omit inline tool definitions`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (hasAdditions ? !isEventSeq(data["headerSeq"]) : Object.hasOwn(data, "headerSeq")) throw new Error(`${subject} requires headerSeq exactly when tool additions are present`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
227
248
|
if (event.type === "request/header") {
|
|
228
249
|
if (!isRecord(data)) throw new Error(`${subject} data must be an object`);
|
|
229
250
|
const header = data["header"];
|
|
@@ -236,9 +257,7 @@ window.__ModuleLoader__.load({
|
|
|
236
257
|
if (!isRecord(data)) throw new Error(`${subject} data must be an object`);
|
|
237
258
|
if (data["error"] === void 0) return;
|
|
238
259
|
const message = data["message"];
|
|
239
|
-
|
|
240
|
-
const block = Array.isArray(content) ? content[0] : void 0;
|
|
241
|
-
if (!isRecord(block) || block["isError"] !== true) throw new Error(`${subject} error requires message content[0].isError === true`);
|
|
260
|
+
if (!isRecord(message) || message["isError"] !== true) throw new Error(`${subject} error requires message.isError === true`);
|
|
242
261
|
}
|
|
243
262
|
}
|
|
244
263
|
/** Whether a runtime value is a non-negative safe event sequence. */
|
|
@@ -331,7 +350,6 @@ window.__ModuleLoader__.load({
|
|
|
331
350
|
}
|
|
332
351
|
//#endregion
|
|
333
352
|
//#region lib/types/types.js
|
|
334
|
-
/** Browser-safe request, result, and lifecycle vocabulary for the Session Remote service. */
|
|
335
353
|
/** Maximum number of Sessions returned by one search. */
|
|
336
354
|
const SESSION_SEARCH_RESULT_LIMIT = 20;
|
|
337
355
|
/** Maximum search snippet length in Unicode code points. */
|
|
@@ -558,6 +576,210 @@ window.__ModuleLoader__.load({
|
|
|
558
576
|
return merged;
|
|
559
577
|
}
|
|
560
578
|
//#endregion
|
|
579
|
+
//#region ../../util/values/lib/index.js
|
|
580
|
+
/** Duplicate-install-safe JSON and immutable-value helpers. @module @deepseek-ai/dsh-util-values */
|
|
581
|
+
/**
|
|
582
|
+
* Mark an unreachable closed-union branch.
|
|
583
|
+
* @param value - impossible value; an unhandled typed variant fails at the call site.
|
|
584
|
+
* @param context - optional switch-site label included in the failure message.
|
|
585
|
+
* @returns never; a runtime value that escaped its type always throws.
|
|
586
|
+
*/
|
|
587
|
+
function assertNever(value, context) {
|
|
588
|
+
const rendered = JSON.stringify(value) ?? String(value);
|
|
589
|
+
throw new Error(`unreachable variant${context ? ` in ${context}` : ""}: ${rendered}`);
|
|
590
|
+
}
|
|
591
|
+
/** Whether a realm-owned intrinsic prototype is backed by its native constructor. */
|
|
592
|
+
function hasIntrinsicConstructor(prototype, name) {
|
|
593
|
+
const constructor = Object.getOwnPropertyDescriptor(prototype, "constructor")?.value;
|
|
594
|
+
if (typeof constructor !== "function") return false;
|
|
595
|
+
try {
|
|
596
|
+
return constructor.name === name && constructor.prototype === prototype && Function.prototype.toString.call(constructor) === `function ${name}() { [native code] }`;
|
|
597
|
+
} catch {
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
/** Whether a candidate is one realm's intrinsic `Object.prototype`. */
|
|
602
|
+
function isIntrinsicObjectPrototype(value) {
|
|
603
|
+
return Object.getPrototypeOf(value) === null && hasIntrinsicConstructor(value, "Object");
|
|
604
|
+
}
|
|
605
|
+
/** Whether an array uses one realm's intrinsic `Array.prototype`, not a subclass or forged prototype. */
|
|
606
|
+
function hasPlainArrayPrototype(value) {
|
|
607
|
+
const prototype = Object.getPrototypeOf(value);
|
|
608
|
+
if (!Array.isArray(prototype) || !hasIntrinsicConstructor(prototype, "Array")) return false;
|
|
609
|
+
const objectPrototype = Object.getPrototypeOf(prototype);
|
|
610
|
+
return typeof objectPrototype === "object" && objectPrototype !== null && isIntrinsicObjectPrototype(objectPrototype);
|
|
611
|
+
}
|
|
612
|
+
/** Whether an object is a plain or null-prototype record from any JavaScript realm. */
|
|
613
|
+
function hasPlainObjectPrototype(value) {
|
|
614
|
+
const prototype = Object.getPrototypeOf(value);
|
|
615
|
+
return prototype === null || typeof prototype === "object" && isIntrinsicObjectPrototype(prototype);
|
|
616
|
+
}
|
|
617
|
+
/** Return every JSON-visible object key, or reject own data JSON would discard. */
|
|
618
|
+
function enumerableStringKeys(value) {
|
|
619
|
+
const keys = Reflect.ownKeys(value);
|
|
620
|
+
if (keys.some((key) => typeof key !== "string" || !Object.prototype.propertyIsEnumerable.call(value, key))) return void 0;
|
|
621
|
+
return keys;
|
|
622
|
+
}
|
|
623
|
+
/** Validate lossless JSON iteratively, optionally materializing a detached snapshot. */
|
|
624
|
+
function walkJsonValue(value, detach) {
|
|
625
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
626
|
+
let root;
|
|
627
|
+
const assign = (destination, item) => {
|
|
628
|
+
if (destination === void 0) return;
|
|
629
|
+
if (destination.kind === "root") root = item;
|
|
630
|
+
else if (destination.kind === "array") destination.target[destination.index] = item;
|
|
631
|
+
else Object.defineProperty(destination.target, destination.key, {
|
|
632
|
+
value: item,
|
|
633
|
+
enumerable: true,
|
|
634
|
+
configurable: true,
|
|
635
|
+
writable: true
|
|
636
|
+
});
|
|
637
|
+
};
|
|
638
|
+
const tasks = [{
|
|
639
|
+
kind: "visit",
|
|
640
|
+
value,
|
|
641
|
+
...detach ? { destination: { kind: "root" } } : {}
|
|
642
|
+
}];
|
|
643
|
+
for (let task = tasks.pop(); task !== void 0; task = tasks.pop()) {
|
|
644
|
+
if (task.kind === "leave") {
|
|
645
|
+
ancestors.delete(task.source);
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
if (task.kind === "array-item") {
|
|
649
|
+
if (!Object.prototype.hasOwnProperty.call(task.source, task.index)) return void 0;
|
|
650
|
+
tasks.push({
|
|
651
|
+
kind: "visit",
|
|
652
|
+
value: task.source[task.index],
|
|
653
|
+
...task.target === void 0 ? {} : { destination: {
|
|
654
|
+
kind: "array",
|
|
655
|
+
target: task.target,
|
|
656
|
+
index: task.index
|
|
657
|
+
} }
|
|
658
|
+
});
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
if (task.kind === "object-property") {
|
|
662
|
+
tasks.push({
|
|
663
|
+
kind: "visit",
|
|
664
|
+
value: task.source[task.key],
|
|
665
|
+
...task.target === void 0 ? {} : { destination: {
|
|
666
|
+
kind: "object",
|
|
667
|
+
target: task.target,
|
|
668
|
+
key: task.key
|
|
669
|
+
} }
|
|
670
|
+
});
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
const current = task.value;
|
|
674
|
+
if (current === null) {
|
|
675
|
+
assign(task.destination, null);
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
if (typeof current === "boolean" || typeof current === "string") {
|
|
679
|
+
assign(task.destination, current);
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
if (typeof current === "number") {
|
|
683
|
+
if (!Number.isFinite(current) || Object.is(current, -0)) return void 0;
|
|
684
|
+
assign(task.destination, current);
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
if (typeof current !== "object") return void 0;
|
|
688
|
+
if (ancestors.has(current)) return void 0;
|
|
689
|
+
if (Array.isArray(current)) {
|
|
690
|
+
if (!hasPlainArrayPrototype(current)) return void 0;
|
|
691
|
+
const length = current.length;
|
|
692
|
+
if (Reflect.ownKeys(current).length !== length + 1) return void 0;
|
|
693
|
+
const target = detach ? [] : void 0;
|
|
694
|
+
if (target !== void 0) assign(task.destination, target);
|
|
695
|
+
ancestors.add(current);
|
|
696
|
+
tasks.push({
|
|
697
|
+
kind: "leave",
|
|
698
|
+
source: current
|
|
699
|
+
});
|
|
700
|
+
for (let index = length - 1; index >= 0; index--) tasks.push({
|
|
701
|
+
kind: "array-item",
|
|
702
|
+
source: current,
|
|
703
|
+
index,
|
|
704
|
+
...target === void 0 ? {} : { target }
|
|
705
|
+
});
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
if (!hasPlainObjectPrototype(current)) return void 0;
|
|
709
|
+
const keys = enumerableStringKeys(current);
|
|
710
|
+
if (keys === void 0) return void 0;
|
|
711
|
+
const target = detach ? {} : void 0;
|
|
712
|
+
if (target !== void 0) assign(task.destination, target);
|
|
713
|
+
ancestors.add(current);
|
|
714
|
+
tasks.push({
|
|
715
|
+
kind: "leave",
|
|
716
|
+
source: current
|
|
717
|
+
});
|
|
718
|
+
for (let index = keys.length - 1; index >= 0; index--) {
|
|
719
|
+
const key = keys[index];
|
|
720
|
+
/* v8 ignore next -- the loop is bounded by the captured key count. */
|
|
721
|
+
if (key === void 0) return void 0;
|
|
722
|
+
tasks.push({
|
|
723
|
+
kind: "object-property",
|
|
724
|
+
source: current,
|
|
725
|
+
key,
|
|
726
|
+
...target === void 0 ? {} : { target }
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return detach ? root : true;
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Validate and detach lossless JSON in one read per property.
|
|
734
|
+
* @param value - candidate value to validate and detach.
|
|
735
|
+
* @returns the detached snapshot, or `undefined` when the value is not losslessly JSON-serializable.
|
|
736
|
+
*/
|
|
737
|
+
function snapshotJsonValue(value) {
|
|
738
|
+
return walkJsonValue(value, true);
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Deep-freeze an object graph in place while leaving live AbortSignal objects mutable.
|
|
742
|
+
* @param value - value to freeze.
|
|
743
|
+
* @returns the same value after every reachable enumerable child is frozen.
|
|
744
|
+
*/
|
|
745
|
+
function deepFreeze(value) {
|
|
746
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
747
|
+
const pending = [{
|
|
748
|
+
kind: "visit",
|
|
749
|
+
node: value
|
|
750
|
+
}];
|
|
751
|
+
while (pending.length > 0) {
|
|
752
|
+
const task = pending.pop();
|
|
753
|
+
/* v8 ignore next -- the loop condition guarantees one pending task. */
|
|
754
|
+
if (task === void 0) continue;
|
|
755
|
+
if (task.kind === "property") {
|
|
756
|
+
pending.push({
|
|
757
|
+
kind: "visit",
|
|
758
|
+
node: task.source[task.key]
|
|
759
|
+
});
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
const node = task.node;
|
|
763
|
+
if (node === null || typeof node !== "object") continue;
|
|
764
|
+
if (node instanceof AbortSignal) continue;
|
|
765
|
+
if (seen.has(node)) continue;
|
|
766
|
+
seen.add(node);
|
|
767
|
+
Object.freeze(node);
|
|
768
|
+
const keys = Object.keys(node);
|
|
769
|
+
for (let index = keys.length - 1; index >= 0; index--) {
|
|
770
|
+
const key = keys[index];
|
|
771
|
+
/* v8 ignore next -- the loop is bounded by the captured key count. */
|
|
772
|
+
if (key === void 0) continue;
|
|
773
|
+
pending.push({
|
|
774
|
+
kind: "property",
|
|
775
|
+
source: node,
|
|
776
|
+
key
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
return value;
|
|
781
|
+
}
|
|
782
|
+
//#endregion
|
|
561
783
|
//#region lib/types/client/sessions/lineage.js
|
|
562
784
|
/**
|
|
563
785
|
* Summaries -> flat list with lineage indentation. Root and sibling order
|
|
@@ -584,8 +806,9 @@ window.__ModuleLoader__.load({
|
|
|
584
806
|
return;
|
|
585
807
|
}
|
|
586
808
|
visited.add(s.sessionId);
|
|
809
|
+
const { agentAvailable: _agentAvailable, ...row } = s;
|
|
587
810
|
out.push({
|
|
588
|
-
...
|
|
811
|
+
...row,
|
|
589
812
|
depth
|
|
590
813
|
});
|
|
591
814
|
const kids = children.get(s.sessionId);
|
|
@@ -688,10 +911,14 @@ window.__ModuleLoader__.load({
|
|
|
688
911
|
//#region lib/types/client/sessions/projection-store.js
|
|
689
912
|
/**
|
|
690
913
|
* One session's projection values. Framework semantics, uniform across every
|
|
691
|
-
* key
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
*
|
|
914
|
+
* key. Sequenced writes (a baseline seeds rows at its cut, a push frame
|
|
915
|
+
* updates one row) compare seqs among themselves: a lower-or-equal seq within
|
|
916
|
+
* the Host generation loses, so a replayed frame cannot regress a value and a
|
|
917
|
+
* stale baseline cannot overwrite a newer frame. Cached writes (the session
|
|
918
|
+
* list's zero-I/O block) only fill keys no sequenced row holds, and a baseline
|
|
919
|
+
* discards every cached row before it seeds, regardless of seq: the connected
|
|
920
|
+
* Session is the truth and a cached value never outranks it. A key the store
|
|
921
|
+
* has never seen reads `undefined` (capability absent). Faces are identity-stable
|
|
695
922
|
* per key (create-on-demand, cached) so the React side binds each exactly
|
|
696
923
|
* once; the store-level channel (`subscribeAny`) serves coarse consumers (the
|
|
697
924
|
* manager's list projection reads the `title` key).
|
|
@@ -746,27 +973,53 @@ window.__ModuleLoader__.load({
|
|
|
746
973
|
*/
|
|
747
974
|
apply(key, value, seq) {
|
|
748
975
|
const row = this.rows.get(key);
|
|
749
|
-
if (row
|
|
976
|
+
if (row?.kind === "sequenced" && seq <= row.seq) return;
|
|
750
977
|
this.rows.set(key, {
|
|
978
|
+
kind: "sequenced",
|
|
751
979
|
value,
|
|
752
980
|
seq
|
|
753
981
|
});
|
|
754
982
|
this.changed(key);
|
|
755
983
|
}
|
|
756
984
|
/**
|
|
757
|
-
*
|
|
758
|
-
*
|
|
759
|
-
*
|
|
760
|
-
*
|
|
761
|
-
*
|
|
985
|
+
* Fill keys from a session-list block the Host labeled `cached`: a zero-I/O
|
|
986
|
+
* view of the persisted checkpoint. A cached value lands only where no
|
|
987
|
+
* sequenced row exists: a connected Session has already answered for such
|
|
988
|
+
* a key, and the list's view of the persisted checkpoint cannot be newer
|
|
989
|
+
* than it.
|
|
990
|
+
* @param values - whole values by key viewed from the persisted checkpoint.
|
|
991
|
+
*/
|
|
992
|
+
applyCached(values) {
|
|
993
|
+
for (const key of Object.keys(values)) {
|
|
994
|
+
if (this.rows.get(key)?.kind === "sequenced") continue;
|
|
995
|
+
this.rows.set(key, {
|
|
996
|
+
kind: "cached",
|
|
997
|
+
value: values[key]
|
|
998
|
+
});
|
|
999
|
+
this.changed(key);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Seed from a history tail page's projections block. Every cached row is
|
|
1004
|
+
* discarded first, regardless of seq: the block comes from the connected
|
|
1005
|
+
* Session, and a value viewed from the persisted checkpoint never outranks
|
|
1006
|
+
* it. Then every carried key lands under the same seq rule as frames, and a
|
|
1007
|
+
* key the block omits is capability-absent as of the cut — its row clears
|
|
1008
|
+
* unless a newer frame already superseded the cut (a stale baseline can
|
|
1009
|
+
* neither overwrite nor clear newer sequenced values).
|
|
762
1010
|
* @param baseline - the response's projections block.
|
|
763
1011
|
*/
|
|
764
1012
|
seed(baseline) {
|
|
1013
|
+
for (const [key, row] of this.rows) {
|
|
1014
|
+
if (row.kind !== "cached") continue;
|
|
1015
|
+
this.rows.delete(key);
|
|
1016
|
+
this.changed(key);
|
|
1017
|
+
}
|
|
765
1018
|
const values = baseline.values;
|
|
766
1019
|
for (const key of Object.keys(values)) this.apply(key, values[key], baseline.asOfSeq);
|
|
767
1020
|
for (const [key, row] of this.rows) {
|
|
768
1021
|
if (Object.hasOwn(values, key)) continue;
|
|
769
|
-
if (row.seq > baseline.asOfSeq) continue;
|
|
1022
|
+
if (row.kind === "sequenced" && row.seq > baseline.asOfSeq) continue;
|
|
770
1023
|
this.rows.delete(key);
|
|
771
1024
|
this.changed(key);
|
|
772
1025
|
}
|
|
@@ -957,199 +1210,6 @@ window.__ModuleLoader__.load({
|
|
|
957
1210
|
return timeZone;
|
|
958
1211
|
}
|
|
959
1212
|
//#endregion
|
|
960
|
-
//#region ../../util/values/lib/index.js
|
|
961
|
-
/** Whether a realm-owned intrinsic prototype is backed by its native constructor. */
|
|
962
|
-
function hasIntrinsicConstructor(prototype, name) {
|
|
963
|
-
const constructor = Object.getOwnPropertyDescriptor(prototype, "constructor")?.value;
|
|
964
|
-
if (typeof constructor !== "function") return false;
|
|
965
|
-
try {
|
|
966
|
-
return constructor.name === name && constructor.prototype === prototype && Function.prototype.toString.call(constructor) === `function ${name}() { [native code] }`;
|
|
967
|
-
} catch {
|
|
968
|
-
return false;
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
/** Whether a candidate is one realm's intrinsic `Object.prototype`. */
|
|
972
|
-
function isIntrinsicObjectPrototype(value) {
|
|
973
|
-
return Object.getPrototypeOf(value) === null && hasIntrinsicConstructor(value, "Object");
|
|
974
|
-
}
|
|
975
|
-
/** Whether an array uses one realm's intrinsic `Array.prototype`, not a subclass or forged prototype. */
|
|
976
|
-
function hasPlainArrayPrototype(value) {
|
|
977
|
-
const prototype = Object.getPrototypeOf(value);
|
|
978
|
-
if (!Array.isArray(prototype) || !hasIntrinsicConstructor(prototype, "Array")) return false;
|
|
979
|
-
const objectPrototype = Object.getPrototypeOf(prototype);
|
|
980
|
-
return typeof objectPrototype === "object" && objectPrototype !== null && isIntrinsicObjectPrototype(objectPrototype);
|
|
981
|
-
}
|
|
982
|
-
/** Whether an object is a plain or null-prototype record from any JavaScript realm. */
|
|
983
|
-
function hasPlainObjectPrototype(value) {
|
|
984
|
-
const prototype = Object.getPrototypeOf(value);
|
|
985
|
-
return prototype === null || typeof prototype === "object" && isIntrinsicObjectPrototype(prototype);
|
|
986
|
-
}
|
|
987
|
-
/** Return every JSON-visible object key, or reject own data JSON would discard. */
|
|
988
|
-
function enumerableStringKeys(value) {
|
|
989
|
-
const keys = Reflect.ownKeys(value);
|
|
990
|
-
if (keys.some((key) => typeof key !== "string" || !Object.prototype.propertyIsEnumerable.call(value, key))) return void 0;
|
|
991
|
-
return keys;
|
|
992
|
-
}
|
|
993
|
-
/** Validate lossless JSON iteratively, optionally materializing a detached snapshot. */
|
|
994
|
-
function walkJsonValue(value, detach) {
|
|
995
|
-
const ancestors = /* @__PURE__ */ new Set();
|
|
996
|
-
let root;
|
|
997
|
-
const assign = (destination, item) => {
|
|
998
|
-
if (destination === void 0) return;
|
|
999
|
-
if (destination.kind === "root") root = item;
|
|
1000
|
-
else if (destination.kind === "array") destination.target[destination.index] = item;
|
|
1001
|
-
else Object.defineProperty(destination.target, destination.key, {
|
|
1002
|
-
value: item,
|
|
1003
|
-
enumerable: true,
|
|
1004
|
-
configurable: true,
|
|
1005
|
-
writable: true
|
|
1006
|
-
});
|
|
1007
|
-
};
|
|
1008
|
-
const tasks = [{
|
|
1009
|
-
kind: "visit",
|
|
1010
|
-
value,
|
|
1011
|
-
...detach ? { destination: { kind: "root" } } : {}
|
|
1012
|
-
}];
|
|
1013
|
-
for (let task = tasks.pop(); task !== void 0; task = tasks.pop()) {
|
|
1014
|
-
if (task.kind === "leave") {
|
|
1015
|
-
ancestors.delete(task.source);
|
|
1016
|
-
continue;
|
|
1017
|
-
}
|
|
1018
|
-
if (task.kind === "array-item") {
|
|
1019
|
-
if (!Object.prototype.hasOwnProperty.call(task.source, task.index)) return void 0;
|
|
1020
|
-
tasks.push({
|
|
1021
|
-
kind: "visit",
|
|
1022
|
-
value: task.source[task.index],
|
|
1023
|
-
...task.target === void 0 ? {} : { destination: {
|
|
1024
|
-
kind: "array",
|
|
1025
|
-
target: task.target,
|
|
1026
|
-
index: task.index
|
|
1027
|
-
} }
|
|
1028
|
-
});
|
|
1029
|
-
continue;
|
|
1030
|
-
}
|
|
1031
|
-
if (task.kind === "object-property") {
|
|
1032
|
-
tasks.push({
|
|
1033
|
-
kind: "visit",
|
|
1034
|
-
value: task.source[task.key],
|
|
1035
|
-
...task.target === void 0 ? {} : { destination: {
|
|
1036
|
-
kind: "object",
|
|
1037
|
-
target: task.target,
|
|
1038
|
-
key: task.key
|
|
1039
|
-
} }
|
|
1040
|
-
});
|
|
1041
|
-
continue;
|
|
1042
|
-
}
|
|
1043
|
-
const current = task.value;
|
|
1044
|
-
if (current === null) {
|
|
1045
|
-
assign(task.destination, null);
|
|
1046
|
-
continue;
|
|
1047
|
-
}
|
|
1048
|
-
if (typeof current === "boolean" || typeof current === "string") {
|
|
1049
|
-
assign(task.destination, current);
|
|
1050
|
-
continue;
|
|
1051
|
-
}
|
|
1052
|
-
if (typeof current === "number") {
|
|
1053
|
-
if (!Number.isFinite(current) || Object.is(current, -0)) return void 0;
|
|
1054
|
-
assign(task.destination, current);
|
|
1055
|
-
continue;
|
|
1056
|
-
}
|
|
1057
|
-
if (typeof current !== "object") return void 0;
|
|
1058
|
-
if (ancestors.has(current)) return void 0;
|
|
1059
|
-
if (Array.isArray(current)) {
|
|
1060
|
-
if (!hasPlainArrayPrototype(current)) return void 0;
|
|
1061
|
-
const length = current.length;
|
|
1062
|
-
if (Reflect.ownKeys(current).length !== length + 1) return void 0;
|
|
1063
|
-
const target = detach ? [] : void 0;
|
|
1064
|
-
if (target !== void 0) assign(task.destination, target);
|
|
1065
|
-
ancestors.add(current);
|
|
1066
|
-
tasks.push({
|
|
1067
|
-
kind: "leave",
|
|
1068
|
-
source: current
|
|
1069
|
-
});
|
|
1070
|
-
for (let index = length - 1; index >= 0; index--) tasks.push({
|
|
1071
|
-
kind: "array-item",
|
|
1072
|
-
source: current,
|
|
1073
|
-
index,
|
|
1074
|
-
...target === void 0 ? {} : { target }
|
|
1075
|
-
});
|
|
1076
|
-
continue;
|
|
1077
|
-
}
|
|
1078
|
-
if (!hasPlainObjectPrototype(current)) return void 0;
|
|
1079
|
-
const keys = enumerableStringKeys(current);
|
|
1080
|
-
if (keys === void 0) return void 0;
|
|
1081
|
-
const target = detach ? {} : void 0;
|
|
1082
|
-
if (target !== void 0) assign(task.destination, target);
|
|
1083
|
-
ancestors.add(current);
|
|
1084
|
-
tasks.push({
|
|
1085
|
-
kind: "leave",
|
|
1086
|
-
source: current
|
|
1087
|
-
});
|
|
1088
|
-
for (let index = keys.length - 1; index >= 0; index--) {
|
|
1089
|
-
const key = keys[index];
|
|
1090
|
-
/* v8 ignore next -- the loop is bounded by the captured key count. */
|
|
1091
|
-
if (key === void 0) return void 0;
|
|
1092
|
-
tasks.push({
|
|
1093
|
-
kind: "object-property",
|
|
1094
|
-
source: current,
|
|
1095
|
-
key,
|
|
1096
|
-
...target === void 0 ? {} : { target }
|
|
1097
|
-
});
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
return detach ? root : true;
|
|
1101
|
-
}
|
|
1102
|
-
/**
|
|
1103
|
-
* Validate and detach lossless JSON in one read per property.
|
|
1104
|
-
* @param value - candidate value to validate and detach.
|
|
1105
|
-
* @returns the detached snapshot, or `undefined` when the value is not losslessly JSON-serializable.
|
|
1106
|
-
*/
|
|
1107
|
-
function snapshotJsonValue(value) {
|
|
1108
|
-
return walkJsonValue(value, true);
|
|
1109
|
-
}
|
|
1110
|
-
/**
|
|
1111
|
-
* Deep-freeze an object graph in place while leaving live AbortSignal objects mutable.
|
|
1112
|
-
* @param value - value to freeze.
|
|
1113
|
-
* @returns the same value after every reachable enumerable child is frozen.
|
|
1114
|
-
*/
|
|
1115
|
-
function deepFreeze(value) {
|
|
1116
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
1117
|
-
const pending = [{
|
|
1118
|
-
kind: "visit",
|
|
1119
|
-
node: value
|
|
1120
|
-
}];
|
|
1121
|
-
while (pending.length > 0) {
|
|
1122
|
-
const task = pending.pop();
|
|
1123
|
-
/* v8 ignore next -- the loop condition guarantees one pending task. */
|
|
1124
|
-
if (task === void 0) continue;
|
|
1125
|
-
if (task.kind === "property") {
|
|
1126
|
-
pending.push({
|
|
1127
|
-
kind: "visit",
|
|
1128
|
-
node: task.source[task.key]
|
|
1129
|
-
});
|
|
1130
|
-
continue;
|
|
1131
|
-
}
|
|
1132
|
-
const node = task.node;
|
|
1133
|
-
if (node === null || typeof node !== "object") continue;
|
|
1134
|
-
if (node instanceof AbortSignal) continue;
|
|
1135
|
-
if (seen.has(node)) continue;
|
|
1136
|
-
seen.add(node);
|
|
1137
|
-
Object.freeze(node);
|
|
1138
|
-
const keys = Object.keys(node);
|
|
1139
|
-
for (let index = keys.length - 1; index >= 0; index--) {
|
|
1140
|
-
const key = keys[index];
|
|
1141
|
-
/* v8 ignore next -- the loop is bounded by the captured key count. */
|
|
1142
|
-
if (key === void 0) continue;
|
|
1143
|
-
pending.push({
|
|
1144
|
-
kind: "property",
|
|
1145
|
-
source: node,
|
|
1146
|
-
key
|
|
1147
|
-
});
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
return value;
|
|
1151
|
-
}
|
|
1152
|
-
//#endregion
|
|
1153
1213
|
//#region ../../llm/llm/lib/types/assistant-stream.js
|
|
1154
1214
|
/**
|
|
1155
1215
|
* Lossless compact representation of one model-stream attempt, plus record-level
|
|
@@ -1479,6 +1539,7 @@ window.__ModuleLoader__.load({
|
|
|
1479
1539
|
jumpTargetSeq = null;
|
|
1480
1540
|
/** The running jump loop's completion, shared by retargeting callers. */
|
|
1481
1541
|
jumpPromise = null;
|
|
1542
|
+
pendingHistory = null;
|
|
1482
1543
|
stopObservingInbox;
|
|
1483
1544
|
assistantStream = new ClientAssistantStream();
|
|
1484
1545
|
running = false;
|
|
@@ -1492,7 +1553,7 @@ window.__ModuleLoader__.load({
|
|
|
1492
1553
|
promptAttempted = false;
|
|
1493
1554
|
/** A first accepted prompt stays in the engaging phase until its turn is observable. */
|
|
1494
1555
|
firstPromptPendingTurn = false;
|
|
1495
|
-
/**
|
|
1556
|
+
/** New Session display state; unknown bare sessions begin conservatively blank. */
|
|
1496
1557
|
blankBit = true;
|
|
1497
1558
|
removed = false;
|
|
1498
1559
|
promptError = null;
|
|
@@ -1508,8 +1569,9 @@ window.__ModuleLoader__.load({
|
|
|
1508
1569
|
* Per-session projection value store (push model; see the session-projection
|
|
1509
1570
|
* subsystem page, docs/subsystems/session-projection.md): finished whole
|
|
1510
1571
|
* values computed on the Host, seeded by the tail page's
|
|
1511
|
-
* projections block and updated by Session Controller control frames
|
|
1512
|
-
*
|
|
1572
|
+
* projections block and updated by Session Controller control frames;
|
|
1573
|
+
* Host-sequenced writes merge under higher-seq-wins and cached list blocks
|
|
1574
|
+
* yield to them (projection-store.ts). Keys are read via `projections.faceOf(key)`
|
|
1513
1575
|
* (the useProjection resolution face); the conversation snapshot never
|
|
1514
1576
|
* carries projection values, and no client-side domain folding exists.
|
|
1515
1577
|
* Manager-owned when constructed through SessionManager (frames route and
|
|
@@ -1574,14 +1636,16 @@ window.__ModuleLoader__.load({
|
|
|
1574
1636
|
*/
|
|
1575
1637
|
beginSubmission(input) {
|
|
1576
1638
|
const requestId = randomUUID();
|
|
1639
|
+
const placement = this.running ? input.mode === "steer" ? "steering" : "queued" : "transcript";
|
|
1577
1640
|
this.pendingSubmissions = [...this.pendingSubmissions, {
|
|
1578
1641
|
requestId,
|
|
1579
|
-
placement
|
|
1642
|
+
placement,
|
|
1580
1643
|
time: Date.now(),
|
|
1581
1644
|
text: input.text,
|
|
1582
1645
|
attachments: input.attachments
|
|
1583
1646
|
}];
|
|
1584
1647
|
this.submissionSettlements.set(requestId, {
|
|
1648
|
+
placement,
|
|
1585
1649
|
onRetire: input.onRetire,
|
|
1586
1650
|
retiring: false
|
|
1587
1651
|
});
|
|
@@ -1649,9 +1713,9 @@ window.__ModuleLoader__.load({
|
|
|
1649
1713
|
}
|
|
1650
1714
|
if (this.blankBit) {
|
|
1651
1715
|
this.blankBit = false;
|
|
1652
|
-
this.options.onEngaged?.(this);
|
|
1653
1716
|
this.notifier.markDirty();
|
|
1654
1717
|
}
|
|
1718
|
+
this.options.onEngaged?.(this);
|
|
1655
1719
|
return result;
|
|
1656
1720
|
}
|
|
1657
1721
|
/**
|
|
@@ -1779,29 +1843,37 @@ window.__ModuleLoader__.load({
|
|
|
1779
1843
|
return this.jumpPromise;
|
|
1780
1844
|
}
|
|
1781
1845
|
if (this.loadingOlder) return Promise.resolve();
|
|
1846
|
+
const events = this.events;
|
|
1847
|
+
if (events === void 0) return Promise.resolve();
|
|
1848
|
+
const pending = {
|
|
1849
|
+
beforeSeq: this.baseSeq,
|
|
1850
|
+
hasMore: this.hasMore,
|
|
1851
|
+
pages: []
|
|
1852
|
+
};
|
|
1853
|
+
this.pendingHistory = pending;
|
|
1782
1854
|
this.jumpTargetSeq = seq;
|
|
1783
1855
|
this.loadingOlder = true;
|
|
1784
1856
|
this.notifier.markDirty();
|
|
1785
1857
|
const generation = this.openGeneration;
|
|
1786
1858
|
this.jumpPromise = (async () => {
|
|
1787
1859
|
try {
|
|
1788
|
-
while (
|
|
1860
|
+
while (pending.hasMore && this.jumpTargetSeq !== null && pending.beforeSeq > this.jumpTargetSeq) {
|
|
1789
1861
|
if (generation !== this.openGeneration) return;
|
|
1790
|
-
const
|
|
1791
|
-
if (events === void 0) return;
|
|
1792
|
-
const before = this.baseSeq;
|
|
1862
|
+
const before = pending.beforeSeq;
|
|
1793
1863
|
await events.prepend({
|
|
1794
|
-
beforeSeq:
|
|
1864
|
+
beforeSeq: before,
|
|
1795
1865
|
maxMessages: 200
|
|
1796
1866
|
});
|
|
1797
|
-
if (
|
|
1867
|
+
if (pending.beforeSeq >= before) return;
|
|
1798
1868
|
}
|
|
1799
1869
|
} catch (error) {
|
|
1800
1870
|
if (!(0, _deepseek_ai_dsh_api_gateway_client.isRemoteFailure)(error)) console.error("[session-controller] loadThrough failed:", error);
|
|
1801
1871
|
} finally {
|
|
1802
1872
|
this.jumpTargetSeq = null;
|
|
1803
1873
|
this.jumpPromise = null;
|
|
1874
|
+
this.pendingHistory = null;
|
|
1804
1875
|
this.loadingOlder = false;
|
|
1876
|
+
if (generation === this.openGeneration && pending.pages.length > 0) this.prependWindow(pending.pages.reverse().flat(), pending.hasMore);
|
|
1805
1877
|
this.notifier.markDirty();
|
|
1806
1878
|
}
|
|
1807
1879
|
})();
|
|
@@ -1876,13 +1948,15 @@ window.__ModuleLoader__.load({
|
|
|
1876
1948
|
this.notifier.markDirty();
|
|
1877
1949
|
}
|
|
1878
1950
|
/**
|
|
1879
|
-
*
|
|
1880
|
-
* `
|
|
1881
|
-
* running
|
|
1882
|
-
*
|
|
1883
|
-
*
|
|
1951
|
+
* Apply the Manager's effective display blank, further reconciled with the
|
|
1952
|
+
* current `sessionListMetadata` projection. Local send attempts and current
|
|
1953
|
+
* running state prevent re-blanking; an earlier false summary alone does not.
|
|
1954
|
+
* The Manager retains acceptance and earlier running observations across
|
|
1955
|
+
* Session-object replacement.
|
|
1956
|
+
* @param blank - New Session display state after Manager reconciliation.
|
|
1884
1957
|
*/
|
|
1885
1958
|
handleBlank(blank) {
|
|
1959
|
+
blank = blank && this.projections.values().sessionListMetadata?.blank !== false;
|
|
1886
1960
|
if (blank === this.blankBit) return;
|
|
1887
1961
|
if (blank && (this.promptAttempted || this.running)) return;
|
|
1888
1962
|
this.blankBit = blank;
|
|
@@ -1962,10 +2036,22 @@ window.__ModuleLoader__.load({
|
|
|
1962
2036
|
const visible = this.assistantStream.replace(entries, assistantStream);
|
|
1963
2037
|
this.baseSeq = SessionLogOffset(entries[0]?.event.seq ?? 0);
|
|
1964
2038
|
this.hasMore = hasMore;
|
|
2039
|
+
if (this.pendingHistory !== null) {
|
|
2040
|
+
this.pendingHistory.beforeSeq = this.baseSeq;
|
|
2041
|
+
this.pendingHistory.hasMore = hasMore;
|
|
2042
|
+
this.pendingHistory.pages.length = 0;
|
|
2043
|
+
}
|
|
1965
2044
|
if (visible.some((entry) => entry.event.type === "turn/start")) this.firstPromptPendingTurn = false;
|
|
1966
2045
|
if (projections !== void 0) this.projections.seed(projections);
|
|
1967
2046
|
this.eventSource.replace(visible, hasMore);
|
|
2047
|
+
if (projections !== void 0) {
|
|
2048
|
+
for (const [requestId, { receipt }] of this.submissionSettlements) if (receipt !== void 0 && receipt.seq <= projections.asOfSeq) this.scheduleObservedRetirement(requestId, receipt.attachments);
|
|
2049
|
+
}
|
|
1968
2050
|
for (const entry of visible) this.observeSubmissionEvent(entry.event);
|
|
2051
|
+
if (projections !== void 0) {
|
|
2052
|
+
const inbox = projections.values.inbox;
|
|
2053
|
+
this.observeSteeringInsertions(inbox?.["next-step"] ?? [], 0, projections.asOfSeq);
|
|
2054
|
+
}
|
|
1969
2055
|
this.notifier.markDirty();
|
|
1970
2056
|
}
|
|
1971
2057
|
publishAssistantEntry(result) {
|
|
@@ -1995,6 +2081,13 @@ window.__ModuleLoader__.load({
|
|
|
1995
2081
|
}
|
|
1996
2082
|
/** Prepend one stream-validated history page. */
|
|
1997
2083
|
prependWindow(entries, hasMore) {
|
|
2084
|
+
if (this.pendingHistory !== null) {
|
|
2085
|
+
const pending = this.pendingHistory;
|
|
2086
|
+
pending.beforeSeq = entries[0] === void 0 ? pending.beforeSeq : SessionLogOffset(entries[0].event.seq);
|
|
2087
|
+
pending.hasMore = hasMore;
|
|
2088
|
+
pending.pages.push(entries);
|
|
2089
|
+
return;
|
|
2090
|
+
}
|
|
1998
2091
|
this.baseSeq = entries[0] === void 0 ? this.baseSeq : SessionLogOffset(entries[0].event.seq);
|
|
1999
2092
|
this.hasMore = hasMore;
|
|
2000
2093
|
this.eventSource.prepend(entries, hasMore);
|
|
@@ -2012,28 +2105,55 @@ window.__ModuleLoader__.load({
|
|
|
2012
2105
|
observeSubmissionEvent(event) {
|
|
2013
2106
|
if (this.submissionSettlements.size === 0) return;
|
|
2014
2107
|
if (event.type === "agent/inbox/spliced") {
|
|
2015
|
-
const
|
|
2016
|
-
if (
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2108
|
+
const { target, start, removedCount = 0, inserted, outcome } = event.data;
|
|
2109
|
+
if (target === "next-step") {
|
|
2110
|
+
for (const [requestId, settlement] of this.submissionSettlements) {
|
|
2111
|
+
const receipt = settlement.receipt;
|
|
2112
|
+
if (receipt === void 0 || receipt.index === null || receipt.seq >= event.seq) continue;
|
|
2113
|
+
const removed = receipt.index >= start && receipt.index < start + removedCount;
|
|
2114
|
+
if (removed && outcome === "canceled") this.retireFailedSubmission(requestId);
|
|
2115
|
+
else settlement.receipt = {
|
|
2116
|
+
...receipt,
|
|
2117
|
+
seq: event.seq,
|
|
2118
|
+
index: removed ? null : receipt.index < start ? receipt.index : receipt.index + inserted.length - removedCount
|
|
2119
|
+
};
|
|
2120
|
+
}
|
|
2121
|
+
this.observeSteeringInsertions(inserted, start, event.seq);
|
|
2122
|
+
}
|
|
2123
|
+
for (const message of inserted) this.observeSubmissionMessage(message, false);
|
|
2020
2124
|
return;
|
|
2021
2125
|
}
|
|
2022
|
-
if (event.type
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
this.
|
|
2126
|
+
if (event.type === "request/context" || event.type === "turn/end") {
|
|
2127
|
+
for (const [requestId, settlement] of this.submissionSettlements) if (settlement.receipt?.index === null && settlement.receipt.seq < event.seq) this.retireFailedSubmission(requestId);
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
if (event.type === "user/message") this.observeSubmissionMessage(event.data, true);
|
|
2131
|
+
}
|
|
2132
|
+
observeSteeringInsertions(messages, start, seq) {
|
|
2133
|
+
for (const [index, message] of messages.entries()) {
|
|
2134
|
+
const source = message.source;
|
|
2135
|
+
if (source.kind !== "user" || !("rpcId" in source)) continue;
|
|
2136
|
+
const settlement = this.submissionSettlements.get(source.rpcId);
|
|
2137
|
+
if (settlement?.placement !== "steering" || settlement.retiring || (settlement.receipt?.seq ?? -1) > seq) continue;
|
|
2138
|
+
settlement.receipt = {
|
|
2139
|
+
seq,
|
|
2140
|
+
index: start + index,
|
|
2141
|
+
attachments: attachmentRefsIn(message.content)
|
|
2142
|
+
};
|
|
2143
|
+
}
|
|
2027
2144
|
}
|
|
2028
|
-
|
|
2145
|
+
observeSubmissionMessage(message, admitted) {
|
|
2146
|
+
const source = message.source;
|
|
2147
|
+
if (source.kind !== "user" || !("rpcId" in source)) return;
|
|
2148
|
+
if (!admitted && this.submissionSettlements.get(source.rpcId)?.placement === "steering") return;
|
|
2149
|
+
this.scheduleObservedRetirement(source.rpcId, attachmentRefsIn(message.content));
|
|
2150
|
+
}
|
|
2151
|
+
/** Retire non-steering echoes when the Inbox accepts their queue occurrences. */
|
|
2029
2152
|
observeSubmissionInbox() {
|
|
2030
2153
|
if (this.submissionSettlements.size === 0) return;
|
|
2031
2154
|
const inbox = this.projections.get("inbox");
|
|
2032
2155
|
if (inbox === void 0) return;
|
|
2033
|
-
for (const message of [...inbox["next-turn"], ...inbox["next-step"]])
|
|
2034
|
-
const source = message.source;
|
|
2035
|
-
if (source.kind === "user" && "rpcId" in source) this.scheduleObservedRetirement(source.rpcId, attachmentRefsIn(message.content));
|
|
2036
|
-
}
|
|
2156
|
+
for (const message of [...inbox["next-turn"], ...inbox["next-step"]]) this.observeSubmissionMessage(message, false);
|
|
2037
2157
|
}
|
|
2038
2158
|
/**
|
|
2039
2159
|
* Latch one observed settlement and remove the echo an animation frame
|
|
@@ -2082,12 +2202,16 @@ window.__ModuleLoader__.load({
|
|
|
2082
2202
|
this.notifier.markDirty();
|
|
2083
2203
|
}
|
|
2084
2204
|
buildSnapshot() {
|
|
2205
|
+
const identity = this.projections.values().subagent;
|
|
2085
2206
|
return {
|
|
2086
2207
|
sessionId: this.sessionId,
|
|
2087
2208
|
pendingSubmissions: this.pendingSubmissions,
|
|
2088
2209
|
running: this.running,
|
|
2089
2210
|
subagent: this.address === void 0 ? null : {
|
|
2090
|
-
address: this.address
|
|
2211
|
+
address: this.address.mode === "unknown" && identity != null ? {
|
|
2212
|
+
...this.address,
|
|
2213
|
+
mode: identity.mode
|
|
2214
|
+
} : this.address,
|
|
2091
2215
|
...this.parentAvailable === void 0 ? {} : { parentAvailable: this.parentAvailable }
|
|
2092
2216
|
},
|
|
2093
2217
|
removed: this.removed,
|
|
@@ -2136,15 +2260,21 @@ window.__ModuleLoader__.load({
|
|
|
2136
2260
|
function sessionSeqCursor(value) {
|
|
2137
2261
|
return value === -1 ? -1 : SessionSeq(value);
|
|
2138
2262
|
}
|
|
2139
|
-
function catalogAvailability(parentAvailable) {
|
|
2140
|
-
return parentAvailable === void 0 ? {} : { parentAvailable };
|
|
2141
|
-
}
|
|
2142
2263
|
/** Instance cluster + frame entry + the session list. */
|
|
2143
2264
|
var SessionManager = class {
|
|
2144
2265
|
remote;
|
|
2145
2266
|
sessions = /* @__PURE__ */ new Map();
|
|
2146
2267
|
/** In-flight Session disposals remain here after instances leave `sessions`, so manager disposal can await quiescence. */
|
|
2147
2268
|
sessionDisposals = /* @__PURE__ */ new Set();
|
|
2269
|
+
/**
|
|
2270
|
+
* Accepted/running presentation must survive a later empty-history list
|
|
2271
|
+
* response. Host-asserted running is recorded even before a row, instance, or
|
|
2272
|
+
* address holds the identity — the listing that would hold it may not have
|
|
2273
|
+
* landed yet — while the client-local acceptance callback requires a current
|
|
2274
|
+
* holder, because it can arrive from a replaced or already-dropped Session.
|
|
2275
|
+
*/
|
|
2276
|
+
engagedSessions = /* @__PURE__ */ new Set();
|
|
2277
|
+
disposed = false;
|
|
2148
2278
|
/** Per-session projection value stores, retained independently of instance arrival (the
|
|
2149
2279
|
* title-snapshot precedent, generalized): push frames land here whether or not the Session
|
|
2150
2280
|
* is instantiated (list rows read the 'title' key), and an instantiated Session adopts the
|
|
@@ -2159,18 +2289,8 @@ window.__ModuleLoader__.load({
|
|
|
2159
2289
|
/** Active list request's mutation log; its identity also fences completion after reconnect. */
|
|
2160
2290
|
listMutations = null;
|
|
2161
2291
|
addresses = /* @__PURE__ */ new Map();
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
/** Catalog owners whose membership changed while a pull was in flight: one trailing refresh after it settles. */
|
|
2165
|
-
catalogStale = /* @__PURE__ */ new Set();
|
|
2166
|
-
openCatalogs = /* @__PURE__ */ new Set();
|
|
2167
|
-
catalogDebounce = /* @__PURE__ */ new Map();
|
|
2168
|
-
/**
|
|
2169
|
-
* Background jobs per session, last-wins from Session Controller's control
|
|
2170
|
-
* stream. An empty set is stored as an absent key, so absence and `[]` are
|
|
2171
|
-
* one representation.
|
|
2172
|
-
*/
|
|
2173
|
-
jobsBySession = /* @__PURE__ */ new Map();
|
|
2292
|
+
projectionLoads = /* @__PURE__ */ new Map();
|
|
2293
|
+
projectionInflight = /* @__PURE__ */ new Map();
|
|
2174
2294
|
listSnapshotCache;
|
|
2175
2295
|
/** Entry-identity cache (reference stability): list rebuilds reuse the previous entry
|
|
2176
2296
|
* object when every field matches — wire refreshes mint all-new summary objects, so identity
|
|
@@ -2192,31 +2312,23 @@ window.__ModuleLoader__.load({
|
|
|
2192
2312
|
*/
|
|
2193
2313
|
resolveTarget(target) {
|
|
2194
2314
|
const id = typeof target === "string" ? target : target.childSessionId;
|
|
2195
|
-
const address = typeof target === "string" ? this.
|
|
2315
|
+
const address = typeof target === "string" ? this.subagentAddress(id) : target;
|
|
2196
2316
|
if (typeof target === "string" && !this.sessions.has(id) && !this.summaries.some((summary) => summary.sessionId === id) && address === void 0) throw new Error(`sessions.retain: unknown session ${id}`);
|
|
2197
2317
|
if (address !== void 0) this.addresses.set(id, address);
|
|
2198
|
-
this.sessions.get(id)?.configureSubagent(address, address === void 0 ? void 0 : this.
|
|
2318
|
+
this.sessions.get(id)?.configureSubagent(address, address === void 0 ? void 0 : this.agentAvailable(address.parentSessionId));
|
|
2199
2319
|
return id;
|
|
2200
2320
|
}
|
|
2201
2321
|
/**
|
|
2202
|
-
* Return the durable catalog address retained for one child.
|
|
2203
|
-
* @param sessionId - possible addressed child id.
|
|
2204
|
-
* @returns The direct-parent address, when navigation discovered one.
|
|
2205
|
-
*/
|
|
2206
|
-
subagentAddress(sessionId) {
|
|
2207
|
-
return this.navigationAddress(sessionId);
|
|
2208
|
-
}
|
|
2209
|
-
/**
|
|
2210
2322
|
* Resolve an address for breadcrumb navigation without retaining transport authority.
|
|
2211
2323
|
* @param sessionId - possible child id in an already-loaded catalog.
|
|
2212
2324
|
* @returns A retained or catalog-derived direct-parent address.
|
|
2213
2325
|
*/
|
|
2214
|
-
|
|
2326
|
+
subagentAddress(sessionId) {
|
|
2215
2327
|
const retained = this.addresses.get(sessionId);
|
|
2216
2328
|
if (retained !== void 0) return retained;
|
|
2217
|
-
for (const
|
|
2218
|
-
const child =
|
|
2219
|
-
if (child
|
|
2329
|
+
for (const parentSessionId of this.projectionStores.keys()) {
|
|
2330
|
+
const child = this.projectionStores.get(parentSessionId)?.values().subagentCatalog?.find((entry) => entry.id === sessionId);
|
|
2331
|
+
if (child !== void 0) return {
|
|
2220
2332
|
parentSessionId,
|
|
2221
2333
|
childSessionId: sessionId,
|
|
2222
2334
|
mode: child.mode
|
|
@@ -2234,6 +2346,7 @@ window.__ModuleLoader__.load({
|
|
|
2234
2346
|
if (session !== expected) return Promise.resolve();
|
|
2235
2347
|
this.sessions.delete(sessionId);
|
|
2236
2348
|
this.addresses.delete(sessionId);
|
|
2349
|
+
this.pruneEngagement(sessionId, this.retainedIds(this.summaries));
|
|
2237
2350
|
return this.startSessionDisposal(session);
|
|
2238
2351
|
}
|
|
2239
2352
|
/**
|
|
@@ -2241,10 +2354,14 @@ window.__ModuleLoader__.load({
|
|
|
2241
2354
|
* @returns once catalog requests and every Session stream have stopped.
|
|
2242
2355
|
*/
|
|
2243
2356
|
async dispose() {
|
|
2244
|
-
|
|
2245
|
-
this.
|
|
2246
|
-
this.
|
|
2247
|
-
this.
|
|
2357
|
+
this.disposed = true;
|
|
2358
|
+
this.listMutations = null;
|
|
2359
|
+
this.listInflight = null;
|
|
2360
|
+
this.engagedSessions.clear();
|
|
2361
|
+
const reads = [...this.projectionInflight.values()];
|
|
2362
|
+
for (const { controller } of reads) controller.abort();
|
|
2363
|
+
this.projectionInflight.clear();
|
|
2364
|
+
await Promise.all(reads.map((read) => read.promise));
|
|
2248
2365
|
const sessions = [...this.sessions.values()];
|
|
2249
2366
|
this.sessions.clear();
|
|
2250
2367
|
this.addresses.clear();
|
|
@@ -2267,6 +2384,7 @@ window.__ModuleLoader__.load({
|
|
|
2267
2384
|
/**
|
|
2268
2385
|
* Lazy build: return the existing instance or construct one (no auto-open —
|
|
2269
2386
|
* the reference allocator opens history after binding the scope).
|
|
2387
|
+
* New instances reconcile retained metadata before returning.
|
|
2270
2388
|
* @param sessionId - the session to get.
|
|
2271
2389
|
* @returns the resident instance.
|
|
2272
2390
|
*/
|
|
@@ -2277,42 +2395,71 @@ window.__ModuleLoader__.load({
|
|
|
2277
2395
|
this.sessions.set(sessionId, session);
|
|
2278
2396
|
const summary = this.summaries.find((s) => s.sessionId === sessionId);
|
|
2279
2397
|
if (summary !== void 0) {
|
|
2280
|
-
session.handleBlank(summary
|
|
2398
|
+
session.handleBlank(this.effectiveBlank(summary));
|
|
2281
2399
|
session.handleRunning(summary.running);
|
|
2282
2400
|
} else {
|
|
2283
2401
|
const address = this.addresses.get(sessionId);
|
|
2284
|
-
|
|
2285
|
-
if (child?.kind === "child") {
|
|
2402
|
+
if ((address === void 0 ? void 0 : this.projectionStores.get(address.parentSessionId)?.values().subagentCatalog?.find((entry) => entry.id === sessionId)) !== void 0) {
|
|
2286
2403
|
session.handleBlank(false);
|
|
2287
|
-
session.handleRunning(
|
|
2288
|
-
}
|
|
2404
|
+
session.handleRunning(false);
|
|
2405
|
+
} else session.handleBlank(true);
|
|
2289
2406
|
}
|
|
2290
2407
|
}
|
|
2291
2408
|
return session;
|
|
2292
2409
|
}
|
|
2293
2410
|
createSession(sessionId) {
|
|
2294
2411
|
const address = this.addresses.get(sessionId);
|
|
2295
|
-
const parentAvailable = address === void 0 ? void 0 : this.
|
|
2412
|
+
const parentAvailable = address === void 0 ? void 0 : this.agentAvailable(address.parentSessionId);
|
|
2296
2413
|
return new Session(sessionId, this.remote, {
|
|
2297
2414
|
...address === void 0 ? {} : {
|
|
2298
2415
|
address,
|
|
2299
|
-
...
|
|
2416
|
+
...parentAvailable === void 0 ? {} : { parentAvailable }
|
|
2300
2417
|
},
|
|
2301
2418
|
onEngaged: (engaged) => {
|
|
2302
|
-
this.
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2419
|
+
if (this.disposed || !this.retainedIds(this.summaries).has(engaged.sessionId)) return;
|
|
2420
|
+
if (!this.engagedSessions.has(engaged.sessionId)) {
|
|
2421
|
+
this.engagedSessions.add(engaged.sessionId);
|
|
2422
|
+
this.recordMutation({
|
|
2423
|
+
kind: "engaged",
|
|
2424
|
+
sessionId: engaged.sessionId
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
this.sessions.get(engaged.sessionId)?.handleBlank(false);
|
|
2306
2428
|
},
|
|
2307
2429
|
projections: this.projectionStore(sessionId)
|
|
2308
2430
|
});
|
|
2309
2431
|
}
|
|
2432
|
+
effectiveBlank(summary) {
|
|
2433
|
+
return summary.blank && !this.engagedSessions.has(summary.sessionId);
|
|
2434
|
+
}
|
|
2435
|
+
/**
|
|
2436
|
+
* Identities an engagement may still belong to: the given list rows, resident
|
|
2437
|
+
* Session instances, and retained child addresses.
|
|
2438
|
+
* @param summaries - list rows of the caller's snapshot.
|
|
2439
|
+
* @returns the retained identity set.
|
|
2440
|
+
*/
|
|
2441
|
+
retainedIds(summaries) {
|
|
2442
|
+
const retained = new Set(summaries.map((summary) => summary.sessionId));
|
|
2443
|
+
for (const sessionId of this.sessions.keys()) retained.add(sessionId);
|
|
2444
|
+
for (const sessionId of this.addresses.keys()) retained.add(sessionId);
|
|
2445
|
+
return retained;
|
|
2446
|
+
}
|
|
2447
|
+
/**
|
|
2448
|
+
* Forget one engagement that no retained identity holds.
|
|
2449
|
+
* @param sessionId - identity whose engagement may be dropped.
|
|
2450
|
+
* @param retained - identities from {@link retainedIds} for the caller's snapshot.
|
|
2451
|
+
*/
|
|
2452
|
+
pruneEngagement(sessionId, retained) {
|
|
2453
|
+
if (!retained.has(sessionId)) this.engagedSessions.delete(sessionId);
|
|
2454
|
+
}
|
|
2310
2455
|
/** Resident per-session projection store (create-on-demand; outlives instantiation). */
|
|
2311
2456
|
projectionStore(sessionId) {
|
|
2312
2457
|
let store = this.projectionStores.get(sessionId);
|
|
2313
2458
|
if (store === void 0) {
|
|
2314
2459
|
store = new ProjectionValueStore();
|
|
2460
|
+
const projections = store;
|
|
2315
2461
|
store.subscribeAny(() => {
|
|
2462
|
+
if (projections.values().sessionListMetadata?.blank === false) this.sessions.get(sessionId)?.handleBlank(false);
|
|
2316
2463
|
this.notifier.markDirty();
|
|
2317
2464
|
});
|
|
2318
2465
|
this.projectionStores.set(sessionId, store);
|
|
@@ -2320,82 +2467,67 @@ window.__ModuleLoader__.load({
|
|
|
2320
2467
|
return store;
|
|
2321
2468
|
}
|
|
2322
2469
|
/**
|
|
2323
|
-
*
|
|
2324
|
-
* @param
|
|
2470
|
+
* Load a complete projection baseline once per connection; retry unsuccessful reads.
|
|
2471
|
+
* @param sessionId - Session to inspect without opening its conversation.
|
|
2472
|
+
* @returns completion of the current or newly started read.
|
|
2325
2473
|
*/
|
|
2326
|
-
|
|
2327
|
-
const existing = this.
|
|
2474
|
+
refreshProjections(sessionId) {
|
|
2475
|
+
const existing = this.projectionInflight.get(sessionId);
|
|
2328
2476
|
if (existing !== void 0) return existing.promise;
|
|
2329
|
-
|
|
2330
|
-
const
|
|
2331
|
-
const
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
...previous?.parentAvailable === void 0 ? {} : { parentAvailable: previous.parentAvailable },
|
|
2477
|
+
if (this.projectionLoads.get(sessionId)?.state === "ready") return Promise.resolve();
|
|
2478
|
+
const controller = new AbortController();
|
|
2479
|
+
const store = this.projectionStore(sessionId);
|
|
2480
|
+
const initialValues = store.values();
|
|
2481
|
+
this.projectionLoads.set(sessionId, {
|
|
2335
2482
|
state: "loading",
|
|
2336
2483
|
error: null
|
|
2337
2484
|
});
|
|
2338
2485
|
this.notifier.markDirty();
|
|
2339
2486
|
const operation = (async () => {
|
|
2340
2487
|
try {
|
|
2341
|
-
const result = await this.remote.
|
|
2488
|
+
const result = await this.remote.session.projections({ sessionId }, controller.signal);
|
|
2489
|
+
if (controller.signal.aborted) return;
|
|
2342
2490
|
if (result.ok) {
|
|
2343
|
-
|
|
2344
|
-
this.catalogs.set(parentSessionId, {
|
|
2491
|
+
if (result.value !== null) store.seed({
|
|
2345
2492
|
...result.value,
|
|
2346
|
-
|
|
2347
|
-
|
|
2493
|
+
asOfSeq: sessionSeqCursor(result.value.asOfSeq)
|
|
2494
|
+
});
|
|
2495
|
+
else if (store.values() === initialValues) store.clear();
|
|
2496
|
+
this.projectionLoads.set(sessionId, {
|
|
2348
2497
|
state: "ready",
|
|
2349
2498
|
error: null
|
|
2350
2499
|
});
|
|
2351
|
-
|
|
2352
|
-
if (address.parentSessionId !== parentSessionId) continue;
|
|
2353
|
-
this.sessions.get(childId)?.handleSubagentParentAvailable(parentAvailable);
|
|
2354
|
-
}
|
|
2355
|
-
} else this.catalogs.set(parentSessionId, {
|
|
2356
|
-
entries: this.withCatalogMutations(previous?.entries ?? [], expandableRows, activityRows),
|
|
2357
|
-
...catalogAvailability(this.catalogInflight.get(parentSessionId)?.parentAvailableOverride ?? previous?.parentAvailable),
|
|
2500
|
+
} else this.projectionLoads.set(sessionId, {
|
|
2358
2501
|
state: "error",
|
|
2359
2502
|
error: result.error
|
|
2360
2503
|
});
|
|
2361
2504
|
} catch (error) {
|
|
2505
|
+
if (controller.signal.aborted) return;
|
|
2362
2506
|
if (!(0, _deepseek_ai_dsh_api_gateway_client.isRemoteFailure)(error)) throw error;
|
|
2363
|
-
this.
|
|
2364
|
-
entries: this.withCatalogMutations(previous?.entries ?? [], expandableRows, activityRows),
|
|
2365
|
-
...catalogAvailability(this.catalogInflight.get(parentSessionId)?.parentAvailableOverride ?? previous?.parentAvailable),
|
|
2507
|
+
this.projectionLoads.set(sessionId, {
|
|
2366
2508
|
state: "error",
|
|
2367
2509
|
error
|
|
2368
2510
|
});
|
|
2369
2511
|
} finally {
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2512
|
+
if (!controller.signal.aborted) {
|
|
2513
|
+
this.projectionInflight.delete(sessionId);
|
|
2514
|
+
this.notifier.markDirty();
|
|
2515
|
+
}
|
|
2373
2516
|
}
|
|
2374
2517
|
})();
|
|
2375
|
-
this.
|
|
2518
|
+
this.projectionInflight.set(sessionId, {
|
|
2376
2519
|
promise: operation,
|
|
2377
|
-
|
|
2378
|
-
activityRows,
|
|
2379
|
-
parentAvailableOverride: void 0
|
|
2520
|
+
controller
|
|
2380
2521
|
});
|
|
2381
2522
|
return operation;
|
|
2382
2523
|
}
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
this.openCatalogs.add(parentSessionId);
|
|
2391
|
-
this.refreshSubagents(parentSessionId);
|
|
2392
|
-
} else {
|
|
2393
|
-
this.openCatalogs.delete(parentSessionId);
|
|
2394
|
-
const timer = this.catalogDebounce.get(parentSessionId);
|
|
2395
|
-
if (timer !== void 0) {
|
|
2396
|
-
clearTimeout(timer);
|
|
2397
|
-
this.catalogDebounce.delete(parentSessionId);
|
|
2398
|
-
}
|
|
2524
|
+
agentAvailable(sessionId) {
|
|
2525
|
+
return this.summaries.find((summary) => summary.sessionId === sessionId)?.agentAvailable ?? (this.listPhase === "ready" ? false : void 0);
|
|
2526
|
+
}
|
|
2527
|
+
updateParentAvailability() {
|
|
2528
|
+
for (const [childId, address] of this.addresses) {
|
|
2529
|
+
const available = this.agentAvailable(address.parentSessionId);
|
|
2530
|
+
if (available !== void 0) this.sessions.get(childId)?.handleSubagentParentAvailable(available);
|
|
2399
2531
|
}
|
|
2400
2532
|
}
|
|
2401
2533
|
/** Full refresh via session.list (single-flight within one Host generation). */
|
|
@@ -2413,22 +2545,23 @@ window.__ModuleLoader__.load({
|
|
|
2413
2545
|
if (this.listMutations !== mutations) return;
|
|
2414
2546
|
if (result.ok) {
|
|
2415
2547
|
const baseline = this.listPhase === "pending" ? [...result.value.items] : mergeOrderedBaseline(established, result.value.items, (summary) => summary.sessionId);
|
|
2416
|
-
|
|
2548
|
+
const removedSincePull = /* @__PURE__ */ new Set();
|
|
2549
|
+
for (const mutation of mutations) if (mutation.kind === "remove") removedSincePull.add(mutation.sessionId);
|
|
2550
|
+
for (const s of baseline) if (s.running && !removedSincePull.has(s.sessionId)) this.engagedSessions.add(s.sessionId);
|
|
2551
|
+
const summaries = mutations.reduce(applyMutation, baseline);
|
|
2552
|
+
this.summaries = summaries;
|
|
2553
|
+
const retained = this.retainedIds(summaries);
|
|
2554
|
+
for (const sessionId of this.engagedSessions) this.pruneEngagement(sessionId, retained);
|
|
2417
2555
|
this.listState = "idle";
|
|
2418
2556
|
this.listPhase = "ready";
|
|
2557
|
+
this.updateParentAvailability();
|
|
2419
2558
|
for (const s of this.summaries) {
|
|
2420
2559
|
const session = this.sessions.get(s.sessionId);
|
|
2421
2560
|
if (session === void 0) continue;
|
|
2422
|
-
session.handleBlank(s
|
|
2561
|
+
session.handleBlank(this.effectiveBlank(s));
|
|
2423
2562
|
session.handleRunning(s.running);
|
|
2424
2563
|
}
|
|
2425
|
-
for (const s of result.value.items)
|
|
2426
|
-
const block = s.projections;
|
|
2427
|
-
if (block === void 0) continue;
|
|
2428
|
-
const store = this.projectionStore(s.sessionId);
|
|
2429
|
-
const values = block.values;
|
|
2430
|
-
for (const key of Object.keys(values)) store.apply(key, values[key], sessionSeqCursor(block.asOfSeq));
|
|
2431
|
-
}
|
|
2564
|
+
for (const s of result.value.items) if (s.projections !== void 0) this.applyListBlock(s.sessionId, s.projections);
|
|
2432
2565
|
} else {
|
|
2433
2566
|
this.listState = "error";
|
|
2434
2567
|
this.listError = result.error;
|
|
@@ -2484,8 +2617,9 @@ window.__ModuleLoader__.load({
|
|
|
2484
2617
|
};
|
|
2485
2618
|
const result = await this.remote.session.create(payload);
|
|
2486
2619
|
if (result.ok) this.recordMutation({
|
|
2487
|
-
kind: "
|
|
2620
|
+
kind: "placeholder",
|
|
2488
2621
|
summary: {
|
|
2622
|
+
agentAvailable: true,
|
|
2489
2623
|
sessionId: result.value.sessionId,
|
|
2490
2624
|
updatedAt: Date.now(),
|
|
2491
2625
|
running: false,
|
|
@@ -2496,8 +2630,9 @@ window.__ModuleLoader__.load({
|
|
|
2496
2630
|
else {
|
|
2497
2631
|
const publishedSessionId = workspaceAttachSessionId(result.error);
|
|
2498
2632
|
if (publishedSessionId !== void 0) this.recordMutation({
|
|
2499
|
-
kind: "
|
|
2633
|
+
kind: "placeholder",
|
|
2500
2634
|
summary: {
|
|
2635
|
+
agentAvailable: true,
|
|
2501
2636
|
sessionId: publishedSessionId,
|
|
2502
2637
|
updatedAt: Date.now(),
|
|
2503
2638
|
running: false,
|
|
@@ -2509,11 +2644,12 @@ window.__ModuleLoader__.load({
|
|
|
2509
2644
|
}
|
|
2510
2645
|
/**
|
|
2511
2646
|
* Contract session.fork; on success merge the child into summaries
|
|
2512
|
-
* immediately (same synchronous-addressability guarantee as create).
|
|
2513
|
-
*
|
|
2514
|
-
*
|
|
2515
|
-
*
|
|
2516
|
-
*
|
|
2647
|
+
* immediately (same synchronous-addressability guarantee as create).
|
|
2648
|
+
* Blankness starts provisionally true so the authoritative Host summary can
|
|
2649
|
+
* preserve it or lower it after an exact cut before the first `turn/start`;
|
|
2650
|
+
* lineage rides parentSessionId. A child published before Workspace
|
|
2651
|
+
* attachment fails is also reconciled into the list.
|
|
2652
|
+
* @param opts - source session and the optional exact inclusive boundary seq.
|
|
2517
2653
|
* @returns the fork result (the child session id).
|
|
2518
2654
|
*/
|
|
2519
2655
|
async fork(opts) {
|
|
@@ -2524,12 +2660,13 @@ window.__ModuleLoader__.load({
|
|
|
2524
2660
|
});
|
|
2525
2661
|
const childId = result.ok ? result.value.sessionId : workspaceAttachSessionId(result.error);
|
|
2526
2662
|
if (childId !== void 0) this.recordMutation({
|
|
2527
|
-
kind: "
|
|
2663
|
+
kind: "placeholder",
|
|
2528
2664
|
summary: {
|
|
2665
|
+
agentAvailable: true,
|
|
2529
2666
|
sessionId: childId,
|
|
2530
2667
|
updatedAt: Date.now(),
|
|
2531
2668
|
running: false,
|
|
2532
|
-
blank:
|
|
2669
|
+
blank: true,
|
|
2533
2670
|
parentSessionId: opts.sessionId,
|
|
2534
2671
|
...source?.cwd !== void 0 ? { cwd: source.cwd } : {}
|
|
2535
2672
|
}
|
|
@@ -2537,19 +2674,33 @@ window.__ModuleLoader__.load({
|
|
|
2537
2674
|
return result;
|
|
2538
2675
|
}
|
|
2539
2676
|
/**
|
|
2540
|
-
*
|
|
2541
|
-
*
|
|
2542
|
-
*
|
|
2543
|
-
*
|
|
2677
|
+
* Rename a Session and update its title projection without opening its history.
|
|
2678
|
+
* @param sessionId - Session to rename.
|
|
2679
|
+
* @param title - raw title text for Host normalization.
|
|
2680
|
+
* @returns the accepted title and event position, or the Remote failure.
|
|
2681
|
+
*/
|
|
2682
|
+
async rename(sessionId, title) {
|
|
2683
|
+
const result = await this.remote.session.rename({
|
|
2684
|
+
sessionId,
|
|
2685
|
+
title
|
|
2686
|
+
});
|
|
2687
|
+
if (result.ok) this.projectionStore(sessionId).apply("title", result.value.title, SessionSeq(result.value.seq));
|
|
2688
|
+
return result;
|
|
2689
|
+
}
|
|
2690
|
+
/**
|
|
2691
|
+
* Merge a Host summary, replacing live state and filling missing metadata.
|
|
2692
|
+
* Local create/fork placeholders only fill metadata on an existing row.
|
|
2544
2693
|
*/
|
|
2545
2694
|
mergeSummary(summary) {
|
|
2546
2695
|
this.recordMutation({
|
|
2547
2696
|
kind: "upsert",
|
|
2548
2697
|
summary
|
|
2549
2698
|
});
|
|
2699
|
+
this.updateParentAvailability();
|
|
2550
2700
|
}
|
|
2551
2701
|
/** Apply immediately and retain for replay when a list response is in flight. */
|
|
2552
2702
|
recordMutation(mutation) {
|
|
2703
|
+
if (this.disposed) return;
|
|
2553
2704
|
this.listMutations?.push(mutation);
|
|
2554
2705
|
this.summaries = applyMutation(this.summaries, mutation);
|
|
2555
2706
|
this.notifier.markDirty();
|
|
@@ -2587,18 +2738,10 @@ window.__ModuleLoader__.load({
|
|
|
2587
2738
|
this.replaceControlBaseline(frame.value);
|
|
2588
2739
|
return;
|
|
2589
2740
|
}
|
|
2590
|
-
|
|
2591
|
-
this.projectionStore(frame.sessionId).apply(frame.key, frame.value, SessionSeq(frame.seq));
|
|
2592
|
-
this.notifier.markDirty();
|
|
2593
|
-
return;
|
|
2594
|
-
}
|
|
2595
|
-
if (frame.jobs.length === 0) this.jobsBySession.delete(frame.sessionId);
|
|
2596
|
-
else this.jobsBySession.set(frame.sessionId, frame.jobs);
|
|
2741
|
+
this.projectionStore(frame.sessionId).apply(frame.key, frame.value, SessionSeq(frame.seq));
|
|
2597
2742
|
this.notifier.markDirty();
|
|
2598
2743
|
}
|
|
2599
2744
|
replaceControlBaseline(baseline) {
|
|
2600
|
-
this.jobsBySession.clear();
|
|
2601
|
-
for (const [sessionId, jobs] of Object.entries(baseline.jobs)) if (jobs.length > 0) this.jobsBySession.set(sessionId, jobs);
|
|
2602
2745
|
for (const [sessionId, block] of Object.entries(baseline.projections)) {
|
|
2603
2746
|
const store = this.projectionStore(sessionId);
|
|
2604
2747
|
const asOfSeq = sessionSeqCursor(block.asOfSeq);
|
|
@@ -2615,44 +2758,55 @@ window.__ModuleLoader__.load({
|
|
|
2615
2758
|
*/
|
|
2616
2759
|
handleSessionAdded(summary) {
|
|
2617
2760
|
this.mergeSummary(summary);
|
|
2618
|
-
this.
|
|
2619
|
-
|
|
2620
|
-
if (projections !== void 0)
|
|
2621
|
-
|
|
2622
|
-
|
|
2761
|
+
if (!this.disposed && summary.running) this.engagedSessions.add(summary.sessionId);
|
|
2762
|
+
this.sessions.get(summary.sessionId)?.handleBlank(this.effectiveBlank(summary));
|
|
2763
|
+
if (summary.projections !== void 0) this.applyListBlock(summary.sessionId, summary.projections);
|
|
2764
|
+
}
|
|
2765
|
+
/**
|
|
2766
|
+
* Merge one list-surface projection block by the sequence space it declares.
|
|
2767
|
+
* A `sequenced` block came from the Host's live registry for an attached
|
|
2768
|
+
* Session, so each key lands under higher-seq-wins against that Session's
|
|
2769
|
+
* baselines and frames. A `cached` block was viewed from the persisted
|
|
2770
|
+
* checkpoint by a header-only listing: its watermark is not comparable with
|
|
2771
|
+
* this connection's seqs, so it only fills keys no sequenced row holds.
|
|
2772
|
+
*/
|
|
2773
|
+
applyListBlock(sessionId, block) {
|
|
2774
|
+
const store = this.projectionStore(sessionId);
|
|
2775
|
+
switch (block.kind) {
|
|
2776
|
+
case "sequenced": {
|
|
2777
|
+
const seq = sessionSeqCursor(block.asOfSeq);
|
|
2778
|
+
for (const [key, value] of Object.entries(block.values)) store.apply(key, value, seq);
|
|
2779
|
+
return;
|
|
2780
|
+
}
|
|
2781
|
+
case "cached":
|
|
2782
|
+
store.applyCached(block.values);
|
|
2783
|
+
return;
|
|
2784
|
+
default: assertNever(block.kind, "session list projection block kind");
|
|
2623
2785
|
}
|
|
2624
|
-
if (summary.origin === "subagent" && summary.parentSessionId !== void 0) this.markCatalogParentExpandable(summary.parentSessionId);
|
|
2625
|
-
if (summary.parentSessionId !== void 0 && this.openCatalogs.has(summary.parentSessionId)) this.scheduleCatalogRefresh(summary.parentSessionId);
|
|
2626
2786
|
}
|
|
2627
2787
|
/**
|
|
2628
2788
|
* Apply one Session removal forwarded through `ctx.remote.$on`.
|
|
2629
2789
|
* @param sessionId - removed Session identity.
|
|
2630
2790
|
*/
|
|
2631
2791
|
handleSessionRemoved(sessionId) {
|
|
2632
|
-
const durableSubagent = this.summaries.
|
|
2792
|
+
const durableSubagent = this.subagentAddress(sessionId) !== void 0 || this.summaries.some((summary) => summary.sessionId === sessionId && summary.origin === "subagent");
|
|
2633
2793
|
this.recordMutation(durableSubagent ? {
|
|
2634
2794
|
kind: "status",
|
|
2635
2795
|
sessionId,
|
|
2636
|
-
running: false
|
|
2796
|
+
running: false,
|
|
2797
|
+
agentAvailable: false
|
|
2637
2798
|
} : {
|
|
2638
2799
|
kind: "remove",
|
|
2639
2800
|
sessionId
|
|
2640
2801
|
});
|
|
2641
|
-
this.updateCatalogActivity(sessionId, false);
|
|
2642
2802
|
if (durableSubagent) this.sessions.get(sessionId)?.handleRunning(false);
|
|
2643
2803
|
else this.sessions.get(sessionId)?.handleRemoved();
|
|
2644
|
-
this.
|
|
2645
|
-
if (!durableSubagent) this.projectionStores.delete(sessionId);
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
}
|
|
2651
|
-
const ownedCatalog = this.catalogs.get(sessionId);
|
|
2652
|
-
if (ownedCatalog !== void 0 && ownedCatalog.parentAvailable) this.catalogs.set(sessionId, {
|
|
2653
|
-
...ownedCatalog,
|
|
2654
|
-
parentAvailable: false
|
|
2655
|
-
});
|
|
2804
|
+
const catalog = this.projectionStores.get(sessionId)?.values().subagentCatalog;
|
|
2805
|
+
if (!durableSubagent && (catalog === void 0 || catalog.length === 0)) this.projectionStores.delete(sessionId);
|
|
2806
|
+
this.pruneEngagement(sessionId, this.retainedIds(this.summaries));
|
|
2807
|
+
this.projectionInflight.get(sessionId)?.controller.abort();
|
|
2808
|
+
this.projectionInflight.delete(sessionId);
|
|
2809
|
+
this.projectionLoads.delete(sessionId);
|
|
2656
2810
|
for (const [childId, address] of this.addresses) if (address.parentSessionId === sessionId) this.sessions.get(childId)?.handleSubagentParentAvailable(false);
|
|
2657
2811
|
}
|
|
2658
2812
|
/**
|
|
@@ -2661,13 +2815,15 @@ window.__ModuleLoader__.load({
|
|
|
2661
2815
|
* @param running - current Agent running state.
|
|
2662
2816
|
*/
|
|
2663
2817
|
handleSessionStatus(sessionId, running) {
|
|
2818
|
+
if (!this.disposed && running) this.engagedSessions.add(sessionId);
|
|
2664
2819
|
this.recordMutation({
|
|
2665
2820
|
kind: "status",
|
|
2666
2821
|
sessionId,
|
|
2667
|
-
running
|
|
2822
|
+
running,
|
|
2823
|
+
agentAvailable: true
|
|
2668
2824
|
});
|
|
2825
|
+
this.updateParentAvailability();
|
|
2669
2826
|
this.sessions.get(sessionId)?.handleRunning(running);
|
|
2670
|
-
this.updateCatalogActivity(sessionId, running);
|
|
2671
2827
|
}
|
|
2672
2828
|
/**
|
|
2673
2829
|
* Advance Session-list activity from one user-authored durable message.
|
|
@@ -2700,93 +2856,26 @@ window.__ModuleLoader__.load({
|
|
|
2700
2856
|
this.listMutations = null;
|
|
2701
2857
|
this.listInflight = null;
|
|
2702
2858
|
this.refreshList();
|
|
2703
|
-
const parents = new Set(this.
|
|
2859
|
+
const parents = new Set(this.projectionLoads.keys());
|
|
2704
2860
|
for (const id of this.sessions.keys()) {
|
|
2705
2861
|
const address = this.addresses.get(id);
|
|
2706
2862
|
if (address !== void 0) parents.add(address.parentSessionId);
|
|
2707
2863
|
}
|
|
2708
|
-
for (const
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
if (this.catalogDebounce.has(parentSessionId)) return;
|
|
2713
|
-
const timer = setTimeout(() => {
|
|
2714
|
-
this.catalogDebounce.delete(parentSessionId);
|
|
2715
|
-
if (this.catalogInflight.has(parentSessionId)) {
|
|
2716
|
-
this.catalogStale.add(parentSessionId);
|
|
2717
|
-
return;
|
|
2718
|
-
}
|
|
2719
|
-
this.refreshSubagents(parentSessionId);
|
|
2720
|
-
}, 50);
|
|
2721
|
-
this.catalogDebounce.set(parentSessionId, timer);
|
|
2722
|
-
}
|
|
2723
|
-
/** Apply one Agent-driver transition to loaded and in-flight catalogs. */
|
|
2724
|
-
updateCatalogActivity(childSessionId, running) {
|
|
2725
|
-
const activity = running ? "running" : "inactive";
|
|
2726
|
-
for (const inflight of this.catalogInflight.values()) inflight.activityRows.set(childSessionId, activity);
|
|
2727
|
-
let changed = false;
|
|
2728
|
-
for (const [parentSessionId, catalog] of this.catalogs) {
|
|
2729
|
-
if (!catalog.entries.some((entry) => entry.kind === "child" && entry.id === childSessionId && entry.activity !== activity)) continue;
|
|
2730
|
-
const entries = catalog.entries.map((entry) => {
|
|
2731
|
-
if (entry.kind !== "child" || entry.id !== childSessionId) return entry;
|
|
2732
|
-
return {
|
|
2733
|
-
...entry,
|
|
2734
|
-
activity
|
|
2735
|
-
};
|
|
2736
|
-
});
|
|
2737
|
-
changed = true;
|
|
2738
|
-
this.catalogs.set(parentSessionId, {
|
|
2739
|
-
...catalog,
|
|
2740
|
-
entries
|
|
2741
|
-
});
|
|
2742
|
-
}
|
|
2743
|
-
if (changed) this.notifier.markDirty();
|
|
2744
|
-
}
|
|
2745
|
-
/** Preserve and project a positive expandability hint after one direct subagent publishes. */
|
|
2746
|
-
markCatalogParentExpandable(parentSessionId) {
|
|
2747
|
-
this.applyCatalogParentExpandable(parentSessionId);
|
|
2748
|
-
for (const inflight of this.catalogInflight.values()) inflight.expandableRows.add(parentSessionId);
|
|
2749
|
-
}
|
|
2750
|
-
/** Apply one positive expandability hint to every loaded catalog containing that unique row id. */
|
|
2751
|
-
applyCatalogParentExpandable(parentSessionId) {
|
|
2752
|
-
let changed = false;
|
|
2753
|
-
for (const [catalogParentId, catalog] of this.catalogs) {
|
|
2754
|
-
if (!catalog.entries.some((entry) => entry.kind === "child" && entry.id === parentSessionId && !entry.hasChildren)) continue;
|
|
2755
|
-
const entries = catalog.entries.map((entry) => {
|
|
2756
|
-
if (entry.kind !== "child" || entry.id !== parentSessionId || entry.hasChildren) return entry;
|
|
2757
|
-
return {
|
|
2758
|
-
...entry,
|
|
2759
|
-
hasChildren: true
|
|
2760
|
-
};
|
|
2761
|
-
});
|
|
2762
|
-
changed = true;
|
|
2763
|
-
this.catalogs.set(catalogParentId, {
|
|
2764
|
-
...catalog,
|
|
2765
|
-
entries
|
|
2766
|
-
});
|
|
2767
|
-
}
|
|
2768
|
-
if (changed) this.notifier.markDirty();
|
|
2769
|
-
}
|
|
2770
|
-
/** Fold request-local row mutations into one catalog result before publication. */
|
|
2771
|
-
withCatalogMutations(entries, expandableRows, activityRows) {
|
|
2772
|
-
return entries.map((entry) => {
|
|
2773
|
-
if (entry.kind !== "child") return entry;
|
|
2774
|
-
const activity = activityRows.get(entry.id);
|
|
2775
|
-
if (!expandableRows.has(entry.id) && activity === void 0) return entry;
|
|
2776
|
-
return {
|
|
2777
|
-
...entry,
|
|
2778
|
-
...expandableRows.has(entry.id) ? { hasChildren: true } : {},
|
|
2779
|
-
...activity === void 0 ? {} : { activity }
|
|
2780
|
-
};
|
|
2781
|
-
});
|
|
2864
|
+
for (const { controller } of this.projectionInflight.values()) controller.abort();
|
|
2865
|
+
this.projectionInflight.clear();
|
|
2866
|
+
this.projectionLoads.clear();
|
|
2867
|
+
for (const parentSessionId of parents) this.refreshProjections(parentSessionId);
|
|
2782
2868
|
}
|
|
2783
2869
|
buildListSnapshot() {
|
|
2784
2870
|
const items = flattenLineage(this.summaries.map((summary) => {
|
|
2785
2871
|
const projectionStore = this.projectionStores.get(summary.sessionId);
|
|
2786
2872
|
const title = projectionStore?.get("title");
|
|
2787
2873
|
const projectionValues = projectionStore?.values();
|
|
2874
|
+
const metadata = projectionValues?.sessionListMetadata;
|
|
2788
2875
|
return {
|
|
2789
2876
|
...summary,
|
|
2877
|
+
blank: this.effectiveBlank(summary) && metadata?.blank !== false,
|
|
2878
|
+
updatedAt: Math.max(summary.updatedAt, metadata?.lastPromptAt ?? 0),
|
|
2790
2879
|
...typeof title === "string" && title !== "" ? { title } : {},
|
|
2791
2880
|
...projectionValues === void 0 ? {} : { projectionValues }
|
|
2792
2881
|
};
|
|
@@ -2804,31 +2893,41 @@ window.__ModuleLoader__.load({
|
|
|
2804
2893
|
state: this.listState,
|
|
2805
2894
|
phase: this.listPhase,
|
|
2806
2895
|
error: this.listError,
|
|
2807
|
-
|
|
2808
|
-
|
|
2896
|
+
projectionsBySession: Object.fromEntries([...this.projectionStores].map(([sessionId, store]) => [sessionId, {
|
|
2897
|
+
values: store.values(),
|
|
2898
|
+
state: "idle",
|
|
2899
|
+
error: null,
|
|
2900
|
+
...this.projectionLoads.get(sessionId)
|
|
2901
|
+
}]))
|
|
2809
2902
|
};
|
|
2810
2903
|
}
|
|
2811
2904
|
};
|
|
2812
2905
|
/** Apply one list mutation without deriving display order. */
|
|
2813
2906
|
function applyMutation(summaries, mutation) {
|
|
2814
2907
|
switch (mutation.kind) {
|
|
2815
|
-
case "upsert":
|
|
2908
|
+
case "upsert":
|
|
2909
|
+
case "placeholder": {
|
|
2816
2910
|
const existing = summaries.find((summary) => summary.sessionId === mutation.summary.sessionId);
|
|
2817
2911
|
if (existing === void 0) return [mutation.summary, ...summaries];
|
|
2818
2912
|
const filled = {
|
|
2819
2913
|
...existing,
|
|
2820
2914
|
blank: existing.blank && mutation.summary.blank,
|
|
2915
|
+
...mutation.kind === "upsert" ? {
|
|
2916
|
+
agentAvailable: mutation.summary.agentAvailable,
|
|
2917
|
+
running: mutation.summary.running
|
|
2918
|
+
} : {},
|
|
2821
2919
|
...existing.cwd === void 0 && mutation.summary.cwd !== void 0 ? { cwd: mutation.summary.cwd } : {},
|
|
2822
2920
|
...existing.parentSessionId === void 0 && mutation.summary.parentSessionId !== void 0 ? { parentSessionId: mutation.summary.parentSessionId } : {},
|
|
2823
2921
|
...existing.origin === void 0 && mutation.summary.origin !== void 0 ? { origin: mutation.summary.origin } : {}
|
|
2824
2922
|
};
|
|
2825
|
-
if (filled.cwd === existing.cwd && filled.parentSessionId === existing.parentSessionId && filled.origin === existing.origin && filled.blank === existing.blank) return [...summaries];
|
|
2923
|
+
if (filled.cwd === existing.cwd && filled.parentSessionId === existing.parentSessionId && filled.origin === existing.origin && filled.blank === existing.blank && filled.agentAvailable === existing.agentAvailable && filled.running === existing.running) return [...summaries];
|
|
2826
2924
|
return summaries.map((summary) => summary.sessionId === mutation.summary.sessionId ? filled : summary);
|
|
2827
2925
|
}
|
|
2828
2926
|
case "remove": return summaries.filter((summary) => summary.sessionId !== mutation.sessionId);
|
|
2829
|
-
case "status": return summaries.map((summary) => summary.sessionId === mutation.sessionId && (summary.running !== mutation.running || mutation.running && summary.blank) ? {
|
|
2927
|
+
case "status": return summaries.map((summary) => summary.sessionId === mutation.sessionId && (summary.running !== mutation.running || summary.agentAvailable !== mutation.agentAvailable || mutation.running && summary.blank) ? {
|
|
2830
2928
|
...summary,
|
|
2831
2929
|
running: mutation.running,
|
|
2930
|
+
agentAvailable: mutation.agentAvailable,
|
|
2832
2931
|
blank: summary.blank && !mutation.running
|
|
2833
2932
|
} : summary);
|
|
2834
2933
|
case "activity": return summaries.map((summary) => summary.sessionId === mutation.sessionId && mutation.updatedAt > summary.updatedAt ? {
|
|
@@ -2999,8 +3098,7 @@ window.__ModuleLoader__.load({
|
|
|
2999
3098
|
ids: [],
|
|
3000
3099
|
byId: {},
|
|
3001
3100
|
phase: "pending",
|
|
3002
|
-
|
|
3003
|
-
jobsBySession: {}
|
|
3101
|
+
projectionsBySession: {}
|
|
3004
3102
|
});
|
|
3005
3103
|
const disposeManagerProjection = this.manager.subscribe(() => {
|
|
3006
3104
|
this.projectList();
|
|
@@ -3072,25 +3170,17 @@ window.__ModuleLoader__.load({
|
|
|
3072
3170
|
* Resolve an already discovered direct-parent address without opening it.
|
|
3073
3171
|
* Feature plugins use this to avoid Agent-bound RPCs in persisted child views.
|
|
3074
3172
|
* @param id - possible addressed child id.
|
|
3075
|
-
* @returns
|
|
3173
|
+
* @returns A retained or loaded-catalog address, without retaining a new selection or scope.
|
|
3076
3174
|
*/
|
|
3077
3175
|
subagentAddress(id) {
|
|
3078
3176
|
return this.manager.subagentAddress(id);
|
|
3079
3177
|
}
|
|
3080
3178
|
/**
|
|
3081
|
-
*
|
|
3082
|
-
* @param
|
|
3083
|
-
* @param open - menu state.
|
|
3084
|
-
*/
|
|
3085
|
-
setSubagentCatalogOpen(parentSessionId, open) {
|
|
3086
|
-
this.manager.setSubagentCatalogOpen(parentSessionId, open);
|
|
3087
|
-
}
|
|
3088
|
-
/**
|
|
3089
|
-
* Refresh one direct-child catalog.
|
|
3090
|
-
* @param parentSessionId - catalog owner.
|
|
3179
|
+
* Load all Session projections once per connection; retry an unsuccessful initial read.
|
|
3180
|
+
* @param sessionId - Session to inspect without opening its conversation.
|
|
3091
3181
|
*/
|
|
3092
|
-
|
|
3093
|
-
return this.manager.
|
|
3182
|
+
refreshProjections(sessionId) {
|
|
3183
|
+
return this.manager.refreshProjections(sessionId);
|
|
3094
3184
|
}
|
|
3095
3185
|
/**
|
|
3096
3186
|
* Refresh the real Session baseline, reusing an in-flight pull.
|
|
@@ -3169,15 +3259,14 @@ window.__ModuleLoader__.load({
|
|
|
3169
3259
|
return result.value.sessionId;
|
|
3170
3260
|
}
|
|
3171
3261
|
/**
|
|
3172
|
-
* Fork a
|
|
3173
|
-
*
|
|
3174
|
-
*
|
|
3175
|
-
*
|
|
3176
|
-
*
|
|
3177
|
-
*
|
|
3178
|
-
*
|
|
3179
|
-
*
|
|
3180
|
-
* wire takes integers only.
|
|
3262
|
+
* Fork a session from an exact inclusive prefix of the source (same
|
|
3263
|
+
* synchronous-addressability guarantee as {@link ClientSessions.create}:
|
|
3264
|
+
* on resolution the child is catalogued and may be explicitly retained).
|
|
3265
|
+
* @param opts - source session id, the optional exact inclusive boundary
|
|
3266
|
+
* seq (a real event seq the caller already knows; a cut inside an open
|
|
3267
|
+
* turn is balanced Host-side with synthetic closers, and omission selects
|
|
3268
|
+
* the latest completed-turn prefix), and whether to increment an
|
|
3269
|
+
* inherited durable title before resolving.
|
|
3181
3270
|
* @returns the child session id.
|
|
3182
3271
|
* @throws {SessionForkError} with the source id.
|
|
3183
3272
|
* @throws {Error} when a requested child-title rename fails after creation.
|
|
@@ -3186,20 +3275,14 @@ window.__ModuleLoader__.load({
|
|
|
3186
3275
|
const sourceTitle = opts.increaseTitle ? this.list.getSnapshot().byId[opts.sessionId]?.title : void 0;
|
|
3187
3276
|
const result = await this.manager.fork({
|
|
3188
3277
|
sessionId: opts.sessionId,
|
|
3189
|
-
...opts.atSeq === void 0 ? {} : { atSeq: SessionSeq(
|
|
3278
|
+
...opts.atSeq === void 0 ? {} : { atSeq: SessionSeq(opts.atSeq) }
|
|
3190
3279
|
});
|
|
3191
3280
|
if (!result.ok) throw new SessionForkError(result.error, opts.sessionId);
|
|
3192
3281
|
this.projectList();
|
|
3193
3282
|
const childId = result.value.sessionId;
|
|
3194
3283
|
if (sourceTitle !== void 0) {
|
|
3195
|
-
const
|
|
3196
|
-
|
|
3197
|
-
await reference.ready;
|
|
3198
|
-
const renamed = await reference.binding.session.rename(increasedForkTitle(sourceTitle));
|
|
3199
|
-
if (!renamed.ok) throw new Error(`fork child rename failed: ${renamed.error.code}: ${renamed.error.message}`);
|
|
3200
|
-
} finally {
|
|
3201
|
-
reference.release();
|
|
3202
|
-
}
|
|
3284
|
+
const renamed = await this.manager.rename(childId, increasedForkTitle(sourceTitle));
|
|
3285
|
+
if (!renamed.ok) throw new Error(`fork child rename failed: ${renamed.error.code}: ${renamed.error.message}`);
|
|
3203
3286
|
}
|
|
3204
3287
|
return childId;
|
|
3205
3288
|
}
|
|
@@ -3279,7 +3362,7 @@ window.__ModuleLoader__.load({
|
|
|
3279
3362
|
if (count === 0) this.retireScope(id, record);
|
|
3280
3363
|
else this.publishRetention(id);
|
|
3281
3364
|
});
|
|
3282
|
-
if (this.list.getSnapshot().byId[id] === void 0) this.projectList();
|
|
3365
|
+
if (this.list.getSnapshot().byId[id] === void 0 && this.manager.subagentAddress(id) !== void 0) this.projectList();
|
|
3283
3366
|
this.publishRetention(id);
|
|
3284
3367
|
return reference;
|
|
3285
3368
|
}
|
|
@@ -3343,7 +3426,7 @@ window.__ModuleLoader__.load({
|
|
|
3343
3426
|
/** Project the manager's list snapshot into the store (title derivation is display-only). */
|
|
3344
3427
|
projectList() {
|
|
3345
3428
|
const previousById = this.list.getSnapshot().byId;
|
|
3346
|
-
const { items, phase,
|
|
3429
|
+
const { items, phase, projectionsBySession } = this.manager.getListSnapshot();
|
|
3347
3430
|
const ids = [];
|
|
3348
3431
|
const byId = {};
|
|
3349
3432
|
for (const entry of items) {
|
|
@@ -3362,8 +3445,7 @@ window.__ModuleLoader__.load({
|
|
|
3362
3445
|
...entry.origin !== void 0 ? { origin: entry.origin } : {}
|
|
3363
3446
|
};
|
|
3364
3447
|
}
|
|
3365
|
-
for (const [parentId,
|
|
3366
|
-
if (child.kind !== "child") continue;
|
|
3448
|
+
for (const [parentId, projection] of Object.entries(projectionsBySession)) for (const child of projection.values.subagentCatalog ?? []) {
|
|
3367
3449
|
const childId = child.id;
|
|
3368
3450
|
const summary = byId[childId];
|
|
3369
3451
|
const projectionValues = summary?.projectionValues ?? this.manager.projectionValues(childId);
|
|
@@ -3375,7 +3457,7 @@ window.__ModuleLoader__.load({
|
|
|
3375
3457
|
displayTitle,
|
|
3376
3458
|
parentId,
|
|
3377
3459
|
origin: "subagent",
|
|
3378
|
-
running:
|
|
3460
|
+
running: this.scopes.get(childId)?.session.getSnapshot().running ?? false,
|
|
3379
3461
|
blank: false,
|
|
3380
3462
|
updatedAt: 0,
|
|
3381
3463
|
retainedBy: this.retentionSnapshot(childId).retainedBy,
|
|
@@ -3390,9 +3472,13 @@ window.__ModuleLoader__.load({
|
|
|
3390
3472
|
}
|
|
3391
3473
|
for (const [id, record] of this.scopes) {
|
|
3392
3474
|
if (byId[id] !== void 0) continue;
|
|
3475
|
+
const address = this.manager.subagentAddress(id);
|
|
3476
|
+
if (address === void 0) continue;
|
|
3393
3477
|
const previous = previousById[id];
|
|
3394
3478
|
const snapshot = record.session.getSnapshot();
|
|
3395
|
-
const
|
|
3479
|
+
const projectionValues = this.manager.projectionValues(id);
|
|
3480
|
+
const projectedTitle = projectionValues?.title;
|
|
3481
|
+
const title = typeof projectedTitle === "string" && projectedTitle !== "" ? projectedTitle : previous?.title;
|
|
3396
3482
|
byId[id] = {
|
|
3397
3483
|
...previous ?? {
|
|
3398
3484
|
id,
|
|
@@ -3402,9 +3488,12 @@ window.__ModuleLoader__.load({
|
|
|
3402
3488
|
running: snapshot.running,
|
|
3403
3489
|
retainedBy: record.retention.retainedBy,
|
|
3404
3490
|
blank: snapshot.blank,
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3491
|
+
parentId: address.parentSessionId,
|
|
3492
|
+
origin: "subagent",
|
|
3493
|
+
...projectionValues === void 0 ? {} : { projectionValues },
|
|
3494
|
+
...title === void 0 ? {} : {
|
|
3495
|
+
title,
|
|
3496
|
+
displayTitle: title
|
|
3408
3497
|
}
|
|
3409
3498
|
};
|
|
3410
3499
|
}
|
|
@@ -3412,8 +3501,7 @@ window.__ModuleLoader__.load({
|
|
|
3412
3501
|
ids,
|
|
3413
3502
|
byId,
|
|
3414
3503
|
phase,
|
|
3415
|
-
|
|
3416
|
-
jobsBySession
|
|
3504
|
+
projectionsBySession
|
|
3417
3505
|
});
|
|
3418
3506
|
}
|
|
3419
3507
|
startScopeDrop(id, record, disposeFiber = true, sessionDisposal = this.manager.drop(id, record.session)) {
|